Files
moodle/question/format/xml/tests/xmlformat_test.php
T
Eloy Lafuente (stronk7) 01148a0816 MDL-81522 phpunit: Add missing void return type to all tests
While this change is not 100% required now, it's good habit
and we are checking for it since Moodle 4.4.

All the changes in this commit have been applied automatically
using the moodle.PHPUnit.TestReturnType sniff and are, exclusively
adding the ": void" return types when missing.
2024-06-11 11:55:07 +02:00

1850 lines
65 KiB
PHP

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for the Moodle XML format.
*
* @package qformat_xml
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace qformat_xml;
use qformat_xml;
use qtype_numerical_answer;
use question_answer;
use question_bank;
use question_check_specified_fields_expectation;
use question_hint;
use question_hint_with_parts;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
require_once($CFG->dirroot . '/question/format/xml/format.php');
require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
/**
* Unit tests for the matching question definition class.
*
* @package qformat_xml
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class xmlformat_test extends \question_testcase {
public function make_test_question() {
global $USER;
$q = new \stdClass();
$q->id = 0;
$q->contextid = 0;
$q->idnumber = null;
$q->category = 0;
$q->parent = 0;
$q->questiontextformat = FORMAT_HTML;
$q->generalfeedbackformat = FORMAT_HTML;
$q->defaultmark = 1;
$q->penalty = 0.3333333;
$q->length = 1;
$q->stamp = make_unique_id_code();
$q->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
$q->timecreated = time();
$q->timemodified = time();
$q->createdby = $USER->id;
$q->modifiedby = $USER->id;
return $q;
}
/**
* The data the XML import format sends to save_question is not exactly
* the same as the data returned from the editing form, so this method
* makes necessary changes to the return value of
* \test_question_maker::get_question_form_data so that the tests can work.
* @param object $expectedq as returned by get_question_form_data.
* @return object one more likely to match the return value of import_...().
*/
public function remove_irrelevant_form_data_fields($expectedq) {
return $this->itemid_to_files($expectedq);
}
/**
* Becuase XML import uses a files array instead of an itemid integer to
* handle saving files with a question, we need to covert the output of
* \test_question_maker::get_question_form_data to match. This method recursively
* replaces all array elements with key itemid with an array entry with
* key files and value an empty array.
*
* @param mixed $var any data structure.
* @return mixed an equivalent structure with the relacements made.
*/
protected function itemid_to_files($var) {
if (is_object($var)) {
$newvar = new \stdClass();
foreach (get_object_vars($var) as $field => $value) {
$newvar->$field = $this->itemid_to_files($value);
}
} else if (is_array($var)) {
$newvar = array();
foreach ($var as $index => $value) {
if ($index === 'itemid') {
$newvar['files'] = array();
} else {
$newvar[$index] = $this->itemid_to_files($value);
}
}
} else {
$newvar = $var;
}
return $newvar;
}
public function test_xml_escape_simple_input_not_escaped(): void {
$exporter = new qformat_xml();
$string = 'Nothing funny here. Even if we go to a café or to 日本.';
$this->assertEquals($string, $exporter->xml_escape($string));
}
public function test_xml_escape_html_wrapped_in_cdata(): void {
$exporter = new qformat_xml();
$string = '<p>Nothing <b>funny<b> here. Even if we go to a café or to 日本.</p>';
$this->assertEquals('<![CDATA[' . $string . ']]>', $exporter->xml_escape($string));
}
public function test_xml_escape_script_tag_handled_ok(): void {
$exporter = new qformat_xml();
$input = '<script><![CDATA[alert(1<2);]]></script>';
$expected = '<![CDATA[<script><![CDATA[alert(1<2);]]]]><![CDATA[></script>]]>';
$this->assertEquals($expected, $exporter->xml_escape($input));
// Check that parsing the expected result does give the input again.
$parsed = simplexml_load_string('<div>' . $expected . '</div>');
$this->assertEquals($input, $parsed->xpath('//div')[0]);
}
public function test_xml_escape_code_that_looks_like_cdata_end_ok(): void {
$exporter = new qformat_xml();
$input = "if (x[[0]]>a) print('hah');";
$expected = "<![CDATA[if (x[[0]]]]><![CDATA[>a) print('hah');]]>";
$this->assertEquals($expected, $exporter->xml_escape($input));
// Check that parsing the expected result does give the input again.
$parsed = simplexml_load_string('<div>' . $expected . '</div>');
$this->assertEquals($input, $parsed->xpath('//div')[0]);
}
public function test_write_hint_basic(): void {
$q = $this->make_test_question();
$q->contextid = \context_system::instance()->id;
$q->name = 'Short answer question';
$q->questiontext = 'Name an amphibian: __________';
$q->generalfeedback = 'Generalfeedback: frog or toad would have been OK.';
if (!isset($q->options)) {
$q->options = new \stdClass();
}
$q->options->usecase = false;
$q->options->answers = array(
13 => new question_answer(13, 'frog', 1.0, 'Frog is a very good answer.', FORMAT_HTML),
14 => new question_answer(14, 'toad', 0.8, 'Toad is an OK good answer.', FORMAT_HTML),
15 => new question_answer(15, '*', 0.0, 'That is a bad answer.', FORMAT_HTML),
);
$q->qtype = 'shortanswer';
$q->hints = array(
new question_hint(0, 'This is the first hint.', FORMAT_MOODLE),
);
$exporter = new qformat_xml();
$xml = $exporter->writequestion($q);
$this->assertMatchesRegularExpression('|<hint format=\"moodle_auto_format\">\s*<text>\s*' .
'This is the first hint\.\s*</text>\s*</hint>|', $xml);
$this->assertDoesNotMatchRegularExpression('|<shownumcorrect/>|', $xml);
$this->assertDoesNotMatchRegularExpression('|<clearwrong/>|', $xml);
$this->assertDoesNotMatchRegularExpression('|<options>|', $xml);
}
public function test_write_hint_with_parts(): void {
$q = $this->make_test_question();
$q->contextid = \context_system::instance()->id;
$q->name = 'Matching question';
$q->questiontext = 'Classify the animals.';
$q->generalfeedback = 'Frogs and toads are amphibians, the others are mammals.';
$q->qtype = 'match';
if (!isset($q->options)) {
$q->options = new \stdClass();
}
$q->options->shuffleanswers = 1;
$q->options->correctfeedback = '';
$q->options->correctfeedbackformat = FORMAT_HTML;
$q->options->partiallycorrectfeedback = '';
$q->options->partiallycorrectfeedbackformat = FORMAT_HTML;
$q->options->incorrectfeedback = '';
$q->options->incorrectfeedbackformat = FORMAT_HTML;
$q->options->subquestions = array();
$q->hints = array(
new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, false, true),
new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, false),
);
$exporter = new qformat_xml();
$xml = $exporter->writequestion($q);
$this->assertMatchesRegularExpression(
'|<hint format=\"html\">\s*<text>\s*This is the first hint\.\s*</text>|', $xml);
$this->assertMatchesRegularExpression(
'|<hint format=\"html\">\s*<text>\s*This is the second hint\.\s*</text>|', $xml);
list($ignored, $hint1, $hint2) = explode('<hint', $xml);
$this->assertDoesNotMatchRegularExpression('|<shownumcorrect/>|', $hint1);
$this->assertMatchesRegularExpression('|<clearwrong/>|', $hint1);
$this->assertMatchesRegularExpression('|<shownumcorrect/>|', $hint2);
$this->assertDoesNotMatchRegularExpression('|<clearwrong/>|', $hint2);
$this->assertDoesNotMatchRegularExpression('|<options>|', $xml);
}
public function test_import_hints_no_parts(): void {
$xml = <<<END
<question>
<hint>
<text>This is the first hint</text>
<clearwrong/>
</hint>
<hint>
<text>This is the second hint</text>
<shownumcorrect/>
</hint>
</question>
END;
$questionxml = xmlize($xml);
$qo = new \stdClass();
$importer = new qformat_xml();
$importer->import_hints($qo, $questionxml['question'], false, false, 'html');
$this->assertEquals(array(
array('text' => 'This is the first hint',
'format' => FORMAT_HTML),
array('text' => 'This is the second hint',
'format' => FORMAT_HTML),
), $qo->hint);
$this->assertFalse(isset($qo->hintclearwrong));
$this->assertFalse(isset($qo->hintshownumcorrect));
}
public function test_import_hints_with_parts(): void {
$xml = <<<END
<question>
<hint>
<text>This is the first hint</text>
<clearwrong/>
</hint>
<hint>
<text>This is the second hint</text>
<shownumcorrect/>
</hint>
</question>
END;
$questionxml = xmlize($xml);
$qo = new \stdClass();
$importer = new qformat_xml();
$importer->import_hints($qo, $questionxml['question'], true, true, 'html');
$this->assertEquals(array(
array('text' => 'This is the first hint',
'format' => FORMAT_HTML),
array('text' => 'This is the second hint',
'format' => FORMAT_HTML),
), $qo->hint);
$this->assertEquals(array(1, 0), $qo->hintclearwrong);
$this->assertEquals(array(0, 1), $qo->hintshownumcorrect);
}
public function test_import_no_hints_no_error(): void {
$xml = <<<END
<question>
</question>
END;
$questionxml = xmlize($xml);
$qo = new \stdClass();
$importer = new qformat_xml();
$importer->import_hints($qo, $questionxml['question'], 'html');
$this->assertFalse(isset($qo->hint));
}
public function test_import_description(): void {
$xml = ' <question type="description">
<name>
<text>A description</text>
</name>
<questiontext format="html">
<text>The question text.</text>
</questiontext>
<generalfeedback>
<text>Here is some general feedback.</text>
</generalfeedback>
<defaultgrade>0</defaultgrade>
<penalty>0</penalty>
<hidden>0</hidden>
<tags>
<tag><text>tagDescription</text></tag>
<tag><text>tagTest</text></tag>
</tags>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_description($xmldata['question']);
$expectedq = new \stdClass();
$expectedq->qtype = 'description';
$expectedq->name = 'A description';
$expectedq->questiontext = 'The question text.';
$expectedq->questiontextformat = FORMAT_HTML;
$expectedq->generalfeedback = 'Here is some general feedback.';
$expectedq->defaultmark = 0;
$expectedq->length = 0;
$expectedq->penalty = 0;
$expectedq->tags = array('tagDescription', 'tagTest');
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
public function test_export_description(): void {
$qdata = new \stdClass();
$qdata->id = 123;
$qdata->contextid = \context_system::instance()->id;
$qdata->qtype = 'description';
$qdata->name = 'A description';
$qdata->questiontext = 'The question text.';
$qdata->questiontextformat = FORMAT_HTML;
$qdata->generalfeedback = 'Here is some general feedback.';
$qdata->generalfeedbackformat = FORMAT_HTML;
$qdata->defaultmark = 0;
$qdata->length = 0;
$qdata->penalty = 0;
$qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
$qdata->idnumber = null;
$exporter = new qformat_xml();
$xml = $exporter->writequestion($qdata);
$expectedxml = '<!-- question: 123 -->
<question type="description">
<name>
<text>A description</text>
</name>
<questiontext format="html">
<text>The question text.</text>
</questiontext>
<generalfeedback format="html">
<text>Here is some general feedback.</text>
</generalfeedback>
<defaultgrade>0</defaultgrade>
<penalty>0</penalty>
<hidden>0</hidden>
<idnumber></idnumber>
</question>
';
$this->assert_same_xml($expectedxml, $xml);
}
public function test_import_essay_20(): void {
$xml = ' <question type="essay">
<name>
<text>An essay</text>
</name>
<questiontext format="moodle_auto_format">
<text>Write something.</text>
</questiontext>
<generalfeedback>
<text>I hope you wrote something interesting.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0</penalty>
<hidden>0</hidden>
<tags>
<tag><text>tagEssay</text></tag>
<tag><text>tagEssay20</text></tag>
<tag><text>tagTest</text></tag>
</tags>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_essay($xmldata['question']);
$expectedq = new \stdClass();
$expectedq->qtype = 'essay';
$expectedq->name = 'An essay';
$expectedq->questiontext = 'Write something.';
$expectedq->questiontextformat = FORMAT_MOODLE;
$expectedq->generalfeedback = 'I hope you wrote something interesting.';
$expectedq->defaultmark = 1;
$expectedq->length = 1;
$expectedq->penalty = 0;
$expectedq->responseformat = 'editor';
$expectedq->responserequired = 1;
$expectedq->responsefieldlines = 15;
$expectedq->minwordlimit = null;
$expectedq->minwordenabled = false;
$expectedq->maxwordlimit = null;
$expectedq->maxwordenabled = false;
$expectedq->attachments = 0;
$expectedq->attachmentsrequired = 0;
$expectedq->maxbytes = 0;
$expectedq->filetypeslist = null;
$expectedq->graderinfo['text'] = '';
$expectedq->graderinfo['format'] = FORMAT_MOODLE;
$expectedq->responsetemplate['text'] = '';
$expectedq->responsetemplate['format'] = FORMAT_MOODLE;
$expectedq->tags = array('tagEssay', 'tagEssay20', 'tagTest');
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
public function test_import_essay_21(): void {
$xml = ' <question type="essay">
<name>
<text>An essay</text>
</name>
<questiontext format="moodle_auto_format">
<text>Write something.</text>
</questiontext>
<generalfeedback>
<text>I hope you wrote something interesting.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0</penalty>
<hidden>0</hidden>
<responseformat>monospaced</responseformat>
<responserequired>0</responserequired>
<responsefieldlines>42</responsefieldlines>
<attachments>-1</attachments>
<attachmentsrequired>1</attachmentsrequired>
<graderinfo format="html">
<text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text>
</graderinfo>
<responsetemplate format="html">
<text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text>
</responsetemplate>
<tags>
<tag><text>tagEssay</text></tag>
<tag><text>tagEssay21</text></tag>
<tag><text>tagTest</text></tag>
</tags>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_essay($xmldata['question']);
$expectedq = new \stdClass();
$expectedq->qtype = 'essay';
$expectedq->name = 'An essay';
$expectedq->questiontext = 'Write something.';
$expectedq->questiontextformat = FORMAT_MOODLE;
$expectedq->generalfeedback = 'I hope you wrote something interesting.';
$expectedq->defaultmark = 1;
$expectedq->length = 1;
$expectedq->penalty = 0;
$expectedq->responseformat = 'monospaced';
$expectedq->responserequired = 0;
$expectedq->responsefieldlines = 42;
$expectedq->minwordlimit = null;
$expectedq->minwordenabled = false;
$expectedq->maxwordlimit = null;
$expectedq->maxwordenabled = false;
$expectedq->attachments = -1;
$expectedq->attachmentsrequired = 1;
$expectedq->maxbytes = 0;
$expectedq->filetypeslist = null;
$expectedq->graderinfo['text'] = '<p>Grade <b>generously</b>!</p>';
$expectedq->graderinfo['format'] = FORMAT_HTML;
$expectedq->responsetemplate['text'] = '<p>Here is something <b>really</b> interesting.</p>';
$expectedq->responsetemplate['format'] = FORMAT_HTML;
$expectedq->tags = array('tagEssay', 'tagEssay21', 'tagTest');
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
public function test_import_essay_311(): void {
$xml = ' <question type="essay">
<name>
<text>An essay</text>
</name>
<questiontext format="moodle_auto_format">
<text>Write something.</text>
</questiontext>
<generalfeedback>
<text>I hope you wrote something interesting.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0</penalty>
<hidden>0</hidden>
<responseformat>monospaced</responseformat>
<responserequired>0</responserequired>
<responsefieldlines>42</responsefieldlines>
<minwordlimit>10</minwordlimit>
<maxwordlimit>20</maxwordlimit>
<attachments>-1</attachments>
<attachmentsrequired>1</attachmentsrequired>
<maxbytes>52428800</maxbytes>
<filetypeslist>.pdf,.zip.,.docx</filetypeslist>
<graderinfo format="html">
<text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text>
</graderinfo>
<responsetemplate format="html">
<text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text>
</responsetemplate>
<tags>
<tag><text>tagEssay</text></tag>
<tag><text>tagEssay21</text></tag>
<tag><text>tagTest</text></tag>
</tags>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_essay($xmldata['question']);
$expectedq = new \stdClass();
$expectedq->qtype = 'essay';
$expectedq->name = 'An essay';
$expectedq->questiontext = 'Write something.';
$expectedq->questiontextformat = FORMAT_MOODLE;
$expectedq->generalfeedback = 'I hope you wrote something interesting.';
$expectedq->defaultmark = 1;
$expectedq->length = 1;
$expectedq->penalty = 0;
$expectedq->responseformat = 'monospaced';
$expectedq->responserequired = 0;
$expectedq->responsefieldlines = 42;
$expectedq->minwordlimit = 10;
$expectedq->minwordenabled = true;
$expectedq->maxwordlimit = 20;
$expectedq->maxwordenabled = true;
$expectedq->attachments = -1;
$expectedq->attachmentsrequired = 1;
$expectedq->maxbytes = 52428800; // 50MB.
$expectedq->filetypeslist = '.pdf,.zip.,.docx';
$expectedq->graderinfo['text'] = '<p>Grade <b>generously</b>!</p>';
$expectedq->graderinfo['format'] = FORMAT_HTML;
$expectedq->responsetemplate['text'] = '<p>Here is something <b>really</b> interesting.</p>';
$expectedq->responsetemplate['format'] = FORMAT_HTML;
$expectedq->tags = array('tagEssay', 'tagEssay21', 'tagTest');
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
public function test_export_essay(): void {
$qdata = new \stdClass();
$qdata->id = 123;
$qdata->contextid = \context_system::instance()->id;
$qdata->qtype = 'essay';
$qdata->name = 'An essay';
$qdata->questiontext = 'Write something.';
$qdata->questiontextformat = FORMAT_MOODLE;
$qdata->generalfeedback = 'I hope you wrote something interesting.';
$qdata->generalfeedbackformat = FORMAT_MOODLE;
$qdata->defaultmark = 1;
$qdata->length = 1;
$qdata->penalty = 0;
$qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
$qdata->idnumber = null;
$qdata->options = new \stdClass();
$qdata->options->id = 456;
$qdata->options->questionid = 123;
$qdata->options->responseformat = 'monospaced';
$qdata->options->responserequired = 0;
$qdata->options->responsefieldlines = 42;
$qdata->options->minwordlimit = 10;
$qdata->options->maxwordlimit = 20;
$qdata->options->attachments = -1;
$qdata->options->attachmentsrequired = 1;
$qdata->options->graderinfo = '<p>Grade <b>generously</b>!</p>';
$qdata->options->graderinfoformat = FORMAT_HTML;
$qdata->options->responsetemplate = '<p>Here is something <b>really</b> interesting.</p>';
$qdata->options->responsetemplateformat = FORMAT_HTML;
$qdata->options->maxbytes = 52428800; // 50MB.
$qdata->options->filetypeslist = '.pdf,.zip.,.docx';
$exporter = new qformat_xml();
$xml = $exporter->writequestion($qdata);
$expectedxml = '<!-- question: 123 -->
<question type="essay">
<name>
<text>An essay</text>
</name>
<questiontext format="moodle_auto_format">
<text>Write something.</text>
</questiontext>
<generalfeedback format="moodle_auto_format">
<text>I hope you wrote something interesting.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0</penalty>
<hidden>0</hidden>
<idnumber></idnumber>
<responseformat>monospaced</responseformat>
<responserequired>0</responserequired>
<responsefieldlines>42</responsefieldlines>
<minwordlimit>10</minwordlimit>
<maxwordlimit>20</maxwordlimit>
<attachments>-1</attachments>
<attachmentsrequired>1</attachmentsrequired>
<maxbytes>52428800</maxbytes>
<filetypeslist>.pdf,.zip.,.docx</filetypeslist>
<graderinfo format="html">
<text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text>
</graderinfo>
<responsetemplate format="html">
<text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text>
</responsetemplate>
</question>
';
$this->assert_same_xml($expectedxml, $xml);
}
public function test_import_match_19(): void {
$xml = ' <question type="matching">
<name>
<text>Matching question</text>
</name>
<questiontext format="html">
<text>Match the upper and lower case letters.</text>
</questiontext>
<generalfeedback>
<text>The answer is A -> a, B -> b and C -> c.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<shuffleanswers>false</shuffleanswers>
<correctfeedback>
<text>Well done.</text>
</correctfeedback>
<partiallycorrectfeedback>
<text>Not entirely.</text>
</partiallycorrectfeedback>
<incorrectfeedback>
<text>Completely wrong!</text>
</incorrectfeedback>
<subquestion>
<text>A</text>
<answer>
<text>a</text>
</answer>
</subquestion>
<subquestion>
<text>B</text>
<answer>
<text>b</text>
</answer>
</subquestion>
<subquestion>
<text>C</text>
<answer>
<text>c</text>
</answer>
</subquestion>
<subquestion>
<text></text>
<answer>
<text>d</text>
</answer>
</subquestion>
<hint>
<text>Hint 1</text>
<shownumcorrect />
</hint>
<hint>
<text></text>
<shownumcorrect />
<clearwrong />
</hint>
<tags>
<tag><text>tagMatching</text></tag>
<tag><text>tagTest</text></tag>
</tags>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_match($xmldata['question']);
$expectedq = new \stdClass();
$expectedq->qtype = 'match';
$expectedq->name = 'Matching question';
$expectedq->questiontext = 'Match the upper and lower case letters.';
$expectedq->questiontextformat = FORMAT_HTML;
$expectedq->correctfeedback = array('text' => 'Well done.',
'format' => FORMAT_HTML);
$expectedq->partiallycorrectfeedback = array('text' => 'Not entirely.',
'format' => FORMAT_HTML);
$expectedq->shownumcorrect = false;
$expectedq->incorrectfeedback = array('text' => 'Completely wrong!',
'format' => FORMAT_HTML);
$expectedq->generalfeedback = 'The answer is A -> a, B -> b and C -> c.';
$expectedq->generalfeedbackformat = FORMAT_HTML;
$expectedq->defaultmark = 1;
$expectedq->length = 1;
$expectedq->penalty = 0.3333333;
$expectedq->shuffleanswers = 0;
$expectedq->subquestions = array(
array('text' => 'A', 'format' => FORMAT_HTML),
array('text' => 'B', 'format' => FORMAT_HTML),
array('text' => 'C', 'format' => FORMAT_HTML),
array('text' => '', 'format' => FORMAT_HTML));
$expectedq->subanswers = array('a', 'b', 'c', 'd');
$expectedq->hint = array(
array('text' => 'Hint 1', 'format' => FORMAT_HTML),
array('text' => '', 'format' => FORMAT_HTML),
);
$expectedq->hintshownumcorrect = array(true, true);
$expectedq->hintclearwrong = array(false, true);
$expectedq->tags = array('tagMatching', 'tagTest');
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
public function test_export_match(): void {
$qdata = new \stdClass();
$qdata->id = 123;
$qdata->contextid = \context_system::instance()->id;
$qdata->qtype = 'match';
$qdata->name = 'Matching question';
$qdata->questiontext = 'Match the upper and lower case letters.';
$qdata->questiontextformat = FORMAT_HTML;
$qdata->generalfeedback = 'The answer is A -> a, B -> b and C -> c.';
$qdata->generalfeedbackformat = FORMAT_HTML;
$qdata->defaultmark = 1;
$qdata->length = 1;
$qdata->penalty = 0.3333333;
$qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
$qdata->idnumber = null;
$qdata->options = new \stdClass();
$qdata->options->shuffleanswers = 1;
$qdata->options->correctfeedback = 'Well done.';
$qdata->options->correctfeedbackformat = FORMAT_HTML;
$qdata->options->partiallycorrectfeedback = 'Not entirely.';
$qdata->options->partiallycorrectfeedbackformat = FORMAT_HTML;
$qdata->options->shownumcorrect = false;
$qdata->options->incorrectfeedback = 'Completely wrong!';
$qdata->options->incorrectfeedbackformat = FORMAT_HTML;
$subq1 = new \stdClass();
$subq1->id = -4;
$subq1->questiontext = 'A';
$subq1->questiontextformat = FORMAT_HTML;
$subq1->answertext = 'a';
$subq2 = new \stdClass();
$subq2->id = -3;
$subq2->questiontext = 'B';
$subq2->questiontextformat = FORMAT_HTML;
$subq2->answertext = 'b';
$subq3 = new \stdClass();
$subq3->id = -2;
$subq3->questiontext = 'C';
$subq3->questiontextformat = FORMAT_HTML;
$subq3->answertext = 'c';
$subq4 = new \stdClass();
$subq4->id = -1;
$subq4->questiontext = '';
$subq4->questiontextformat = FORMAT_HTML;
$subq4->answertext = 'd';
$qdata->options->subquestions = array(
$subq1, $subq2, $subq3, $subq4);
$qdata->hints = array(
new question_hint_with_parts(0, 'Hint 1', FORMAT_HTML, true, false),
new question_hint_with_parts(0, '', FORMAT_HTML, true, true),
);
$exporter = new qformat_xml();
$xml = $exporter->writequestion($qdata);
$expectedxml = '<!-- question: 123 -->
<question type="matching">
<name>
<text>Matching question</text>
</name>
<questiontext format="html">
<text>Match the upper and lower case letters.</text>
</questiontext>
<generalfeedback format="html">
<text><![CDATA[The answer is A -> a, B -> b and C -> c.]]></text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<idnumber></idnumber>
<shuffleanswers>true</shuffleanswers>
<correctfeedback format="html">
<text>Well done.</text>
</correctfeedback>
<partiallycorrectfeedback format="html">
<text>Not entirely.</text>
</partiallycorrectfeedback>
<incorrectfeedback format="html">
<text>Completely wrong!</text>
</incorrectfeedback>
<subquestion format="html">
<text>A</text>
<answer>
<text>a</text>
</answer>
</subquestion>
<subquestion format="html">
<text>B</text>
<answer>
<text>b</text>
</answer>
</subquestion>
<subquestion format="html">
<text>C</text>
<answer>
<text>c</text>
</answer>
</subquestion>
<subquestion format="html">
<text></text>
<answer>
<text>d</text>
</answer>
</subquestion>
<hint format="html">
<text>Hint 1</text>
<shownumcorrect/>
</hint>
<hint format="html">
<text></text>
<shownumcorrect/>
<clearwrong/>
</hint>
</question>
';
$this->assert_same_xml($expectedxml, $xml);
}
public function test_import_multichoice_19(): void {
$xml = ' <question type="multichoice">
<name>
<text>Multiple choice question</text>
</name>
<questiontext format="html">
<text>Which are the even numbers?</text>
</questiontext>
<generalfeedback>
<text>The even numbers are 2 and 4.</text>
</generalfeedback>
<defaultgrade>2</defaultgrade>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<single>false</single>
<shuffleanswers>false</shuffleanswers>
<answernumbering>abc</answernumbering>
<correctfeedback>
<text><![CDATA[<p>Your answer is correct.</p>]]></text>
</correctfeedback>
<partiallycorrectfeedback>
<text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
</partiallycorrectfeedback>
<incorrectfeedback>
<text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
</incorrectfeedback>
<shownumcorrect/>
<answer fraction="0">
<text>1</text>
<feedback>
<text></text>
</feedback>
</answer>
<answer fraction="100">
<text>2</text>
<feedback>
<text></text>
</feedback>
</answer>
<answer fraction="0">
<text>3</text>
<feedback>
<text></text>
</feedback>
</answer>
<answer fraction="100">
<text>4</text>
<feedback>
<text></text>
</feedback>
</answer>
<hint>
<text>Hint 1.</text>
</hint>
<hint>
<text>Hint 2.</text>
</hint>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_multichoice($xmldata['question']);
$expectedq = new \stdClass();
$expectedq->qtype = 'multichoice';
$expectedq->name = 'Multiple choice question';
$expectedq->questiontext = 'Which are the even numbers?';
$expectedq->questiontextformat = FORMAT_HTML;
$expectedq->correctfeedback = array(
'text' => '<p>Your answer is correct.</p>',
'format' => FORMAT_HTML);
$expectedq->shownumcorrect = false;
$expectedq->partiallycorrectfeedback = array(
'text' => '<p>Your answer is partially correct.</p>',
'format' => FORMAT_HTML);
$expectedq->shownumcorrect = true;
$expectedq->incorrectfeedback = array(
'text' => '<p>Your answer is incorrect.</p>',
'format' => FORMAT_HTML);
$expectedq->generalfeedback = 'The even numbers are 2 and 4.';
$expectedq->defaultmark = 2;
$expectedq->length = 1;
$expectedq->penalty = 0.3333333;
$expectedq->shuffleanswers = 0;
$expectedq->single = false;
$expectedq->answer = array(
array('text' => '1', 'format' => FORMAT_HTML),
array('text' => '2', 'format' => FORMAT_HTML),
array('text' => '3', 'format' => FORMAT_HTML),
array('text' => '4', 'format' => FORMAT_HTML));
$expectedq->fraction = array(0, 1, 0, 1);
$expectedq->feedback = array(
array('text' => '', 'format' => FORMAT_HTML),
array('text' => '', 'format' => FORMAT_HTML),
array('text' => '', 'format' => FORMAT_HTML),
array('text' => '', 'format' => FORMAT_HTML));
$expectedq->hint = array(
array('text' => 'Hint 1.', 'format' => FORMAT_HTML),
array('text' => 'Hint 2.', 'format' => FORMAT_HTML),
);
$expectedq->hintshownumcorrect = array(false, false);
$expectedq->hintclearwrong = array(false, false);
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
public function test_export_multichoice(): void {
$qdata = new \stdClass();
$qdata->id = 123;
$qdata->contextid = \context_system::instance()->id;
$qdata->qtype = 'multichoice';
$qdata->name = 'Multiple choice question';
$qdata->questiontext = 'Which are the even numbers?';
$qdata->questiontextformat = FORMAT_HTML;
$qdata->generalfeedback = 'The even numbers are 2 and 4.';
$qdata->generalfeedbackformat = FORMAT_HTML;
$qdata->defaultmark = 2;
$qdata->length = 1;
$qdata->penalty = 0.3333333;
$qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
$qdata->idnumber = null;
$qdata->options = new \stdClass();
$qdata->options->single = 0;
$qdata->options->shuffleanswers = 0;
$qdata->options->answernumbering = 'abc';
$qdata->options->showstandardinstruction = 0;
$qdata->options->correctfeedback = '<p>Your answer is correct.</p>';
$qdata->options->correctfeedbackformat = FORMAT_HTML;
$qdata->options->partiallycorrectfeedback = '<p>Your answer is partially correct.</p>';
$qdata->options->partiallycorrectfeedbackformat = FORMAT_HTML;
$qdata->options->shownumcorrect = 1;
$qdata->options->incorrectfeedback = '<p>Your answer is incorrect.</p>';
$qdata->options->incorrectfeedbackformat = FORMAT_HTML;
$qdata->options->answers = array(
13 => new question_answer(13, '1', 0, '', FORMAT_HTML),
14 => new question_answer(14, '2', 1, '', FORMAT_HTML),
15 => new question_answer(15, '3', 0, '', FORMAT_HTML),
16 => new question_answer(16, '4', 1, '', FORMAT_HTML),
);
$qdata->hints = array(
new question_hint_with_parts(0, 'Hint 1.', FORMAT_HTML, false, false),
new question_hint_with_parts(0, 'Hint 2.', FORMAT_HTML, false, false),
);
$exporter = new qformat_xml();
$xml = $exporter->writequestion($qdata);
$expectedxml = '<!-- question: 123 -->
<question type="multichoice">
<name>
<text>Multiple choice question</text>
</name>
<questiontext format="html">
<text>Which are the even numbers?</text>
</questiontext>
<generalfeedback format="html">
<text>The even numbers are 2 and 4.</text>
</generalfeedback>
<defaultgrade>2</defaultgrade>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<idnumber></idnumber>
<single>false</single>
<shuffleanswers>false</shuffleanswers>
<answernumbering>abc</answernumbering>
<showstandardinstruction>0</showstandardinstruction>
<correctfeedback format="html">
<text><![CDATA[<p>Your answer is correct.</p>]]></text>
</correctfeedback>
<partiallycorrectfeedback format="html">
<text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
</partiallycorrectfeedback>
<incorrectfeedback format="html">
<text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
</incorrectfeedback>
<shownumcorrect/>
<answer fraction="0" format="plain_text">
<text>1</text>
<feedback format="html">
<text></text>
</feedback>
</answer>
<answer fraction="100" format="plain_text">
<text>2</text>
<feedback format="html">
<text></text>
</feedback>
</answer>
<answer fraction="0" format="plain_text">
<text>3</text>
<feedback format="html">
<text></text>
</feedback>
</answer>
<answer fraction="100" format="plain_text">
<text>4</text>
<feedback format="html">
<text></text>
</feedback>
</answer>
<hint format="html">
<text>Hint 1.</text>
</hint>
<hint format="html">
<text>Hint 2.</text>
</hint>
</question>
';
$this->assert_same_xml($expectedxml, $xml);
}
public function test_import_numerical_19(): void {
$xml = ' <question type="numerical">
<name>
<text>Numerical question</text>
</name>
<questiontext format="html">
<text>What is the answer?</text>
</questiontext>
<generalfeedback>
<text>General feedback: Think Hitch-hikers guide to the Galaxy.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0.1</penalty>
<hidden>0</hidden>
<answer fraction="100">
<text>42</text>
<feedback>
<text>Well done!</text>
</feedback>
<tolerance>0.001</tolerance>
</answer>
<answer fraction="0">
<text>13</text>
<feedback>
<text>What were you thinking?!</text>
</feedback>
<tolerance>1</tolerance>
</answer>
<answer fraction="0">
<text>*</text>
<feedback>
<text>Completely wrong.</text>
</feedback>
<tolerance></tolerance>
</answer>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_numerical($xmldata['question']);
$expectedq = new \stdClass();
$expectedq->qtype = 'numerical';
$expectedq->name = 'Numerical question';
$expectedq->questiontext = 'What is the answer?';
$expectedq->questiontextformat = FORMAT_HTML;
$expectedq->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.';
$expectedq->generalfeedbackformat = FORMAT_HTML;
$expectedq->defaultmark = 1;
$expectedq->length = 1;
$expectedq->penalty = 0.1;
$expectedq->answer = array('42', '13', '*');
$expectedq->fraction = array(1, 0, 0);
$expectedq->feedback = array(
array('text' => 'Well done!',
'format' => FORMAT_HTML),
array('text' => 'What were you thinking?!',
'format' => FORMAT_HTML),
array('text' => 'Completely wrong.',
'format' => FORMAT_HTML));
$expectedq->tolerance = array(0.001, 1, '');
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
public function test_export_numerical(): void {
question_bank::load_question_definition_classes('numerical');
$qdata = new \stdClass();
$qdata->id = 123;
$qdata->contextid = \context_system::instance()->id;
$qdata->qtype = 'numerical';
$qdata->name = 'Numerical question';
$qdata->questiontext = 'What is the answer?';
$qdata->questiontextformat = FORMAT_HTML;
$qdata->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.';
$qdata->generalfeedbackformat = FORMAT_HTML;
$qdata->defaultmark = 1;
$qdata->length = 1;
$qdata->penalty = 0.1;
$qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
$qdata->idnumber = null;
$qdata->options = new \stdClass();
$qdata->options->answers = array(
13 => new qtype_numerical_answer(13, '42', 1, 'Well done!',
FORMAT_HTML, 0.001),
14 => new qtype_numerical_answer(14, '13', 0, 'What were you thinking?!',
FORMAT_HTML, 1),
15 => new qtype_numerical_answer(15, '*', 0, 'Completely wrong.',
FORMAT_HTML, ''),
);
$qdata->options->units = array();
$exporter = new qformat_xml();
$xml = $exporter->writequestion($qdata);
$expectedxml = '<!-- question: 123 -->
<question type="numerical">
<name>
<text>Numerical question</text>
</name>
<questiontext format="html">
<text>What is the answer?</text>
</questiontext>
<generalfeedback format="html">
<text>General feedback: Think Hitch-hikers guide to the Galaxy.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0.1</penalty>
<hidden>0</hidden>
<idnumber></idnumber>
<answer fraction="100" format="plain_text">
<text>42</text>
<feedback format="html">
<text>Well done!</text>
</feedback>
<tolerance>0.001</tolerance>
</answer>
<answer fraction="0" format="plain_text">
<text>13</text>
<feedback format="html">
<text>What were you thinking?!</text>
</feedback>
<tolerance>1</tolerance>
</answer>
<answer fraction="0" format="plain_text">
<text>*</text>
<feedback format="html">
<text>Completely wrong.</text>
</feedback>
<tolerance>0</tolerance>
</answer>
</question>
';
$this->assert_same_xml($expectedxml, $xml);
}
public function test_import_shortanswer_19(): void {
$xml = ' <question type="shortanswer">
<name>
<text>Short answer question</text>
</name>
<questiontext format="html">
<text>Fill in the gap in this sequence: Alpha, ________, Gamma.</text>
</questiontext>
<generalfeedback>
<text>The answer is Beta.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<usecase>0</usecase>
<answer fraction="100" format="plain_text">
<text>Beta</text>
<feedback>
<text>Well done!</text>
</feedback>
</answer>
<answer fraction="0" format="plain_text">
<text>*</text>
<feedback>
<text>Doh!</text>
</feedback>
</answer>
<hint>
<text>Hint 1</text>
</hint>
<hint>
<text>Hint 2</text>
</hint>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_shortanswer($xmldata['question']);
$expectedq = new \stdClass();
$expectedq->qtype = 'shortanswer';
$expectedq->name = 'Short answer question';
$expectedq->questiontext = 'Fill in the gap in this sequence: Alpha, ________, Gamma.';
$expectedq->questiontextformat = FORMAT_HTML;
$expectedq->generalfeedback = 'The answer is Beta.';
$expectedq->usecase = false;
$expectedq->defaultmark = 1;
$expectedq->length = 1;
$expectedq->penalty = 0.3333333;
$expectedq->answer = array('Beta', '*');
$expectedq->fraction = array(1, 0);
$expectedq->feedback = array(
array('text' => 'Well done!', 'format' => FORMAT_HTML),
array('text' => 'Doh!', 'format' => FORMAT_HTML));
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
public function test_export_shortanswer(): void {
$qdata = new \stdClass();
$qdata->id = 123;
$qdata->contextid = \context_system::instance()->id;
$qdata->qtype = 'shortanswer';
$qdata->name = 'Short answer question';
$qdata->questiontext = 'Fill in the gap in this sequence: Alpha, ________, Gamma.';
$qdata->questiontextformat = FORMAT_HTML;
$qdata->generalfeedback = 'The answer is Beta.';
$qdata->generalfeedbackformat = FORMAT_HTML;
$qdata->defaultmark = 1;
$qdata->length = 1;
$qdata->penalty = 0.3333333;
$qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
$qdata->idnumber = null;
$qdata->options = new \stdClass();
$qdata->options->usecase = 0;
$qdata->options->answers = array(
13 => new question_answer(13, 'Beta', 1, 'Well done!', FORMAT_HTML),
14 => new question_answer(14, '*', 0, 'Doh!', FORMAT_HTML),
);
$qdata->hints = array(
new question_hint(0, 'Hint 1', FORMAT_HTML),
new question_hint(0, 'Hint 2', FORMAT_HTML),
);
$exporter = new qformat_xml();
$xml = $exporter->writequestion($qdata);
$expectedxml = '<!-- question: 123 -->
<question type="shortanswer">
<name>
<text>Short answer question</text>
</name>
<questiontext format="html">
<text>Fill in the gap in this sequence: Alpha, ________, Gamma.</text>
</questiontext>
<generalfeedback format="html">
<text>The answer is Beta.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<idnumber></idnumber>
<usecase>0</usecase>
<answer fraction="100" format="plain_text">
<text>Beta</text>
<feedback format="html">
<text>Well done!</text>
</feedback>
</answer>
<answer fraction="0" format="plain_text">
<text>*</text>
<feedback format="html">
<text>Doh!</text>
</feedback>
</answer>
<hint format="html">
<text>Hint 1</text>
</hint>
<hint format="html">
<text>Hint 2</text>
</hint>
</question>
';
$this->assert_same_xml($expectedxml, $xml);
}
public function test_import_truefalse_19(): void {
$xml = ' <question type="truefalse">
<name>
<text>True false question</text>
</name>
<questiontext format="html">
<text>The answer is true.</text>
</questiontext>
<generalfeedback>
<text>General feedback: You should have chosen true.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>1</penalty>
<hidden>0</hidden>
<answer fraction="100">
<text>true</text>
<feedback>
<text>Well done!</text>
</feedback>
</answer>
<answer fraction="0">
<text>false</text>
<feedback>
<text>Doh!</text>
</feedback>
</answer>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_truefalse($xmldata['question']);
$expectedq = new \stdClass();
$expectedq->qtype = 'truefalse';
$expectedq->name = 'True false question';
$expectedq->questiontext = 'The answer is true.';
$expectedq->questiontextformat = FORMAT_HTML;
$expectedq->generalfeedback = 'General feedback: You should have chosen true.';
$expectedq->defaultmark = 1;
$expectedq->length = 1;
$expectedq->penalty = 1;
$expectedq->feedbacktrue = array('text' => 'Well done!',
'format' => FORMAT_HTML);
$expectedq->feedbackfalse = array('text' => 'Doh!',
'format' => FORMAT_HTML);
$expectedq->correctanswer = true;
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
public function test_import_truefalse_with_idnumber(): void {
$xml = ' <question type="truefalse">
<name>
<text>True false question</text>
</name>
<questiontext format="html">
<text>The answer is true.</text>
</questiontext>
<generalfeedback>
<text>General feedback: You should have chosen true.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>1</penalty>
<hidden>0</hidden>
<idnumber>TestIdNum1</idnumber>
<answer fraction="100">
<text>true</text>
<feedback>
<text>Well done!</text>
</feedback>
</answer>
<answer fraction="0">
<text>false</text>
<feedback>
<text>Doh!</text>
</feedback>
</answer>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_truefalse($xmldata['question']);
$expectedq = new \stdClass();
$expectedq->qtype = 'truefalse';
$expectedq->name = 'True false question';
$expectedq->questiontext = 'The answer is true.';
$expectedq->questiontextformat = FORMAT_HTML;
$expectedq->generalfeedback = 'General feedback: You should have chosen true.';
$expectedq->defaultmark = 1;
$expectedq->length = 1;
$expectedq->penalty = 1;
$expectedq->idnumber = 'TestIdNum1';
$expectedq->feedbacktrue = array('text' => 'Well done!',
'format' => FORMAT_HTML);
$expectedq->feedbackfalse = array('text' => 'Doh!',
'format' => FORMAT_HTML);
$expectedq->correctanswer = true;
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
public function test_export_truefalse(): void {
$qdata = new \stdClass();
$qdata->id = 12;
$qdata->contextid = \context_system::instance()->id;
$qdata->qtype = 'truefalse';
$qdata->name = 'True false question';
$qdata->questiontext = 'The answer is true.';
$qdata->questiontextformat = FORMAT_HTML;
$qdata->generalfeedback = 'General feedback: You should have chosen true.';
$qdata->generalfeedbackformat = FORMAT_HTML;
$qdata->defaultmark = 1;
$qdata->length = 1;
$qdata->penalty = 1;
$qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
$qdata->idnumber = null;
$qdata->options = new \stdClass();
$qdata->options->answers = array(
1 => new question_answer(1, 'True', 1, 'Well done!', FORMAT_HTML),
2 => new question_answer(2, 'False', 0, 'Doh!', FORMAT_HTML),
);
$qdata->options->trueanswer = 1;
$qdata->options->falseanswer = 2;
$exporter = new qformat_xml();
$xml = $exporter->writequestion($qdata);
$expectedxml = '<!-- question: 12 -->
<question type="truefalse">
<name>
<text>True false question</text>
</name>
<questiontext format="html">
<text>The answer is true.</text>
</questiontext>
<generalfeedback format="html">
<text>General feedback: You should have chosen true.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>1</penalty>
<hidden>0</hidden>
<idnumber></idnumber>
<answer fraction="100" format="plain_text">
<text>true</text>
<feedback format="html">
<text>Well done!</text>
</feedback>
</answer>
<answer fraction="0" format="plain_text">
<text>false</text>
<feedback format="html">
<text>Doh!</text>
</feedback>
</answer>
</question>
';
$this->assert_same_xml($expectedxml, $xml);
}
public function test_export_truefalse_with_idnumber(): void {
$qdata = new \stdClass();
$qdata->id = 12;
$qdata->contextid = \context_system::instance()->id;
$qdata->qtype = 'truefalse';
$qdata->name = 'True false question';
$qdata->questiontext = 'The answer is true.';
$qdata->questiontextformat = FORMAT_HTML;
$qdata->generalfeedback = 'General feedback: You should have chosen true.';
$qdata->generalfeedbackformat = FORMAT_HTML;
$qdata->defaultmark = 1;
$qdata->length = 1;
$qdata->penalty = 1;
$qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
$qdata->idnumber = 'TestIDNum2';
$qdata->options = new \stdClass();
$qdata->options->answers = array(
1 => new question_answer(1, 'True', 1, 'Well done!', FORMAT_HTML),
2 => new question_answer(2, 'False', 0, 'Doh!', FORMAT_HTML),
);
$qdata->options->trueanswer = 1;
$qdata->options->falseanswer = 2;
$exporter = new qformat_xml();
$xml = $exporter->writequestion($qdata);
$expectedxml = '<!-- question: 12 -->
<question type="truefalse">
<name>
<text>True false question</text>
</name>
<questiontext format="html">
<text>The answer is true.</text>
</questiontext>
<generalfeedback format="html">
<text>General feedback: You should have chosen true.</text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>1</penalty>
<hidden>0</hidden>
<idnumber>TestIDNum2</idnumber>
<answer fraction="100" format="plain_text">
<text>true</text>
<feedback format="html">
<text>Well done!</text>
</feedback>
</answer>
<answer fraction="0" format="plain_text">
<text>false</text>
<feedback format="html">
<text>Doh!</text>
</feedback>
</answer>
</question>
';
$this->assert_same_xml($expectedxml, $xml);
}
public function test_import_multianswer(): void {
$xml = ' <question type="cloze">
<name>
<text>Simple multianswer</text>
</name>
<questiontext format="html">
<text><![CDATA[Complete this opening line of verse: "The {1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer} and the {1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!} went to sea".]]></text>
</questiontext>
<generalfeedback format="html">
<text><![CDATA[General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea".]]></text>
</generalfeedback>
<penalty>0.5</penalty>
<hidden>0</hidden>
<idnumber>id-101</idnumber>
<hint format="html">
<text>Hint 1</text>
</hint>
<hint format="html">
<text>Hint 2</text>
</hint>
<tags>
<tag><text>tagCloze</text></tag>
<tag><text>tagTest</text></tag>
</tags>
</question>
';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_multianswer($xmldata['question']);
// Annoyingly, import works in a weird way (it duplicates code, rather
// than just calling save_question) so we cannot use
// \test_question_maker::get_question_form_data('multianswer', 'twosubq').
$expectedqa = new \stdClass();
$expectedqa->name = 'Simple multianswer';
$expectedqa->qtype = 'multianswer';
$expectedqa->questiontext = 'Complete this opening line of verse: "The {#1} and the {#2} went to sea".';
$expectedqa->idnumber = 'id-101';
$expectedqa->generalfeedback =
'General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea".';
$expectedqa->defaultmark = 2;
$expectedqa->penalty = 0.5;
$expectedqa->hint = array(
array('text' => 'Hint 1', 'format' => FORMAT_HTML),
array('text' => 'Hint 2', 'format' => FORMAT_HTML),
);
$sa = new \stdClass();
$sa->questiontext = array('text' => '{1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer}',
'format' => FORMAT_HTML, 'itemid' => null);
$sa->generalfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
$sa->defaultmark = 1.0;
$sa->qtype = 'shortanswer';
$sa->usecase = 0;
$sa->answer = array('Dog', 'Owl', '*');
$sa->fraction = array(0, 1, 0);
$sa->feedback = array(
array('text' => 'Wrong, silly!', 'format' => FORMAT_HTML, 'itemid' => null),
array('text' => 'Well done!', 'format' => FORMAT_HTML, 'itemid' => null),
array('text' => 'Wrong answer', 'format' => FORMAT_HTML, 'itemid' => null),
);
$mc = new \stdClass();
$mc->generalfeedback = '';
$mc->questiontext = array('text' => '{1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~' .
'Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!}',
'format' => FORMAT_HTML, 'itemid' => null);
$mc->generalfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
$mc->defaultmark = 1.0;
$mc->qtype = 'multichoice';
$mc->layout = 0;
$mc->single = 1;
$mc->shuffleanswers = 0;
$mc->correctfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
$mc->partiallycorrectfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
$mc->incorrectfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
$mc->answernumbering = 0;
$mc->answer = array(
array('text' => 'Bow-wow', 'format' => FORMAT_HTML, 'itemid' => null),
array('text' => 'Wiggly worm', 'format' => FORMAT_HTML, 'itemid' => null),
array('text' => 'Pussy-cat', 'format' => FORMAT_HTML, 'itemid' => null),
);
$mc->fraction = array(0, 0, 1);
$mc->feedback = array(
array('text' => 'You seem to have a dog obsessions!', 'format' => FORMAT_HTML, 'itemid' => null),
array('text' => 'Now you are just being ridiculous!', 'format' => FORMAT_HTML, 'itemid' => null),
array('text' => 'Well done!', 'format' => FORMAT_HTML, 'itemid' => null),
);
$expectedqa->options = new \stdClass();
$expectedqa->options->questions = array(
1 => $sa,
2 => $mc,
);
$expectedqa->tags = array('tagCloze', 'tagTest');
$this->assertEquals($expectedqa->hint, $q->hint);
$this->assertEquals($expectedqa->options->questions[1], $q->options->questions[1]);
$this->assertEquals($expectedqa->options->questions[2], $q->options->questions[2]);
$this->assert(new question_check_specified_fields_expectation($expectedqa), $q);
}
public function test_export_multianswer(): void {
$qdata = \test_question_maker::get_question_data('multianswer', 'twosubq');
$qdata->contextid = \context_system::instance()->id;
$exporter = new qformat_xml();
$xml = $exporter->writequestion($qdata);
$expectedxml = '<!-- question: 0 -->
<question type="cloze">
<name>
<text>Simple multianswer</text>
</name>
<questiontext format="html">
<text><![CDATA[Complete this opening line of verse: "The {1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer} and the {1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!} went to sea".]]></text>
</questiontext>
<generalfeedback format="html">
<text><![CDATA[General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea]]></text>
</generalfeedback>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<idnumber></idnumber>
<hint format="html">
<text>Hint 1</text>
</hint>
<hint format="html">
<text>Hint 2</text>
</hint>
</question>
';
$this->assert_same_xml($expectedxml, $xml);
}
public function test_export_multianswer_withdollars(): void {
$qdata = \test_question_maker::get_question_data('multianswer', 'dollarsigns');
$qdata->contextid = \context_system::instance()->id;
$exporter = new qformat_xml();
$xml = $exporter->writequestion($qdata);
$expectedxml = '<!-- question: 0 -->
<question type="cloze">
<name>
<text>Multianswer with $s</text>
</name>
<questiontext format="html">
<text>Which is the right order? {1:MULTICHOICE:=y,y,$3~$3,y,y}</text>
</questiontext>
<generalfeedback format="html">
<text></text>
</generalfeedback>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<idnumber></idnumber>
</question>
';
$this->assert_same_xml($expectedxml, $xml);
}
public function test_import_files_as_draft(): void {
$this->resetAfterTest();
$this->setAdminUser();
$xml = <<<END
<questiontext format="html">
<text><![CDATA[<p><a href="@@PLUGINFILE@@/moodle.txt">This text file</a> contains the word 'Moodle'.</p>]]></text>
<file name="moodle.txt" encoding="base64">TW9vZGxl</file>
</questiontext>
END;
$textxml = xmlize($xml);
$qo = new \stdClass();
$importer = new qformat_xml();
$draftitemid = $importer->import_files_as_draft($textxml['questiontext']['#']['file']);
$files = file_get_drafarea_files($draftitemid);
$this->assertEquals(1, count($files->list));
$file = $files->list[0];
$this->assertEquals('moodle.txt', $file->filename);
$this->assertEquals('/', $file->filepath);
$this->assertEquals(6, $file->size);
}
public function test_import_truefalse_wih_files(): void {
$this->resetAfterTest();
$this->setAdminUser();
$xml = '<question type="truefalse">
<name>
<text>truefalse</text>
</name>
<questiontext format="html">
<text><![CDATA[<p><a href="@@PLUGINFILE@@/myfolder/moodle.txt">This text file</a> contains the word Moodle.</p>]]></text>
<file name="moodle.txt" path="/myfolder/" encoding="base64">TW9vZGxl</file>
</questiontext>
<generalfeedback format="html">
<text><![CDATA[<p>For further information, see the documentation about Moodle.</p>]]></text>
</generalfeedback>
<defaultgrade>1.0000000</defaultgrade>
<penalty>1.0000000</penalty>
<hidden>0</hidden>
<answer fraction="100" format="moodle_auto_format">
<text>true</text>
<feedback format="html">
<text></text>
</feedback>
</answer>
<answer fraction="0" format="moodle_auto_format">
<text>false</text>
<feedback format="html">
<text></text>
</feedback>
</answer>
</question>';
$xmldata = xmlize($xml);
$importer = new qformat_xml();
$q = $importer->import_truefalse($xmldata['question']);
$draftitemid = $q->questiontextitemid;
$files = file_get_drafarea_files($draftitemid, '/myfolder/');
$this->assertEquals(1, count($files->list));
$file = $files->list[0];
$this->assertEquals('moodle.txt', $file->filename);
$this->assertEquals('/myfolder/', $file->filepath);
$this->assertEquals(6, $file->size);
}
public function test_create_dummy_question(): void {
$testobject = new mock_qformat_xml();
$categoryname = 'name1';
$categoryinfo = new \stdClass();
$categoryinfo->info = 'info1';
$categoryinfo->infoformat = 'infoformat1';
$categoryinfo->idnumber = null;
$dummyquestion = $testobject->mock_create_dummy_question_representing_category($categoryname, $categoryinfo);
$this->assertEquals('category', $dummyquestion->qtype);
$this->assertEquals($categoryname, $dummyquestion->category);
$this->assertEquals($categoryinfo->info, $dummyquestion->info);
$this->assertEquals($categoryinfo->infoformat, $dummyquestion->infoformat);
$this->assertEquals('Switch category to ' . $categoryname, $dummyquestion->name);
$this->assertEquals(0, $dummyquestion->id);
$this->assertEquals('', $dummyquestion->questiontextformat);
$this->assertEquals(0, $dummyquestion->contextid);
}
}
/**
* Class mock_qformat_xml exists only to enable testing of the create dummy question category.
* @package qformat_xml
* @copyright 2018 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mock_qformat_xml extends qformat_xml {
/**
* Make public an otherwise protected function.
* @param string $categoryname the name of the category
* @param object $categoryinfo description of the category
*/
public function mock_create_dummy_question_representing_category(string $categoryname, $categoryinfo) {
return $this->create_dummy_question_representing_category($categoryname, $categoryinfo);
}
}