2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Unit tests for the Moodle XML format.
20 * @package qformat_xml
21 * @copyright 2010 The Open University
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die();
29 require_once($CFG->libdir . '/questionlib.php');
30 require_once($CFG->dirroot . '/question/format/xml/format.php');
31 require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
35 * Unit tests for the matching question definition class.
37 * @copyright 2009 The Open University
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 class qformat_xml_test extends question_testcase {
41 public function assert_same_xml($expectedxml, $xml) {
42 $this->assertEquals(str_replace("\r\n", "\n", $expectedxml),
43 str_replace("\r\n", "\n", $xml));
46 public function make_test_question() {
54 $q->questiontextformat = FORMAT_HTML;
55 $q->generalfeedbackformat = FORMAT_HTML;
57 $q->penalty = 0.3333333;
59 $q->stamp = make_unique_id_code();
60 $q->version = make_unique_id_code();
62 $q->timecreated = time();
63 $q->timemodified = time();
64 $q->createdby = $USER->id;
65 $q->modifiedby = $USER->id;
70 * The data the XML import format sends to save_question is not exactly
71 * the same as the data returned from the editing form, so this method
72 * makes necessary changes to the return value of
73 * test_question_maker::get_question_form_data so that the tests can work.
74 * @param object $expectedq as returned by get_question_form_data.
75 * @return object one more likely to match the return value of import_...().
77 public function remove_irrelevant_form_data_fields($expectedq) {
78 return $this->itemid_to_files($expectedq);
82 * Becuase XML import uses a files array instead of an itemid integer to
83 * handle saving files with a question, we need to covert the output of
84 * test_question_maker::get_question_form_data to match. This method recursively
85 * replaces all array elements with key itemid with an array entry with
86 * key files and value an empty array.
88 * @param mixed $var any data structure.
89 * @return mixed an equivalent structure with the relacements made.
91 protected function itemid_to_files($var) {
92 if (is_object($var)) {
93 $newvar = new stdClass();
94 foreach (get_object_vars($var) as $field => $value) {
95 $newvar->$field = $this->itemid_to_files($value);
98 } else if (is_array($var)) {
100 foreach ($var as $index => $value) {
101 if ($index === 'itemid') {
102 $newvar['files'] = array();
104 $newvar[$index] = $this->itemid_to_files($value);
115 public function test_xml_escape_simple_input_not_escaped() {
116 $exporter = new qformat_xml();
117 $string = 'Nothing funny here. Even if we go to a café or to 日本.';
118 $this->assertEquals($string, $exporter->xml_escape($string));
121 public function test_xml_escape_html_wrapped_in_cdata() {
122 $exporter = new qformat_xml();
123 $string = '<p>Nothing <b>funny<b> here. Even if we go to a café or to 日本.</p>';
124 $this->assertEquals('<![CDATA[' . $string . ']]>', $exporter->xml_escape($string));
127 public function test_xml_escape_script_tag_handled_ok() {
128 $exporter = new qformat_xml();
129 $input = '<script><![CDATA[alert(1<2);]]></script>';
130 $expected = '<![CDATA[<script><![CDATA[alert(1<2);]]]]><![CDATA[></script>]]>';
131 $this->assertEquals($expected, $exporter->xml_escape($input));
133 // Check that parsing the expected result does give the input again.
134 $parsed = simplexml_load_string('<div>' . $expected . '</div>');
135 $this->assertEquals($input, $parsed->xpath('//div')[0]);
138 public function test_xml_escape_code_that_looks_like_cdata_end_ok() {
139 $exporter = new qformat_xml();
140 $input = "if (x[[0]]>a) print('hah');";
141 $expected = "<![CDATA[if (x[[0]]]]><![CDATA[>a) print('hah');]]>";
142 $this->assertEquals($expected, $exporter->xml_escape($input));
144 // Check that parsing the expected result does give the input again.
145 $parsed = simplexml_load_string('<div>' . $expected . '</div>');
146 $this->assertEquals($input, $parsed->xpath('//div')[0]);
149 public function test_write_hint_basic() {
150 $q = $this->make_test_question();
151 $q->contextid = \context_system::instance()->id;
152 $q->name = 'Short answer question';
153 $q->questiontext = 'Name an amphibian: __________';
154 $q->generalfeedback = 'Generalfeedback: frog or toad would have been OK.';
155 if (!isset($q->options)) {
156 $q->options = new stdClass();
158 $q->options->usecase = false;
159 $q->options->answers = array(
160 13 => new question_answer(13, 'frog', 1.0, 'Frog is a very good answer.', FORMAT_HTML),
161 14 => new question_answer(14, 'toad', 0.8, 'Toad is an OK good answer.', FORMAT_HTML),
162 15 => new question_answer(15, '*', 0.0, 'That is a bad answer.', FORMAT_HTML),
164 $q->qtype = 'shortanswer';
166 new question_hint(0, 'This is the first hint.', FORMAT_MOODLE),
169 $exporter = new qformat_xml();
170 $xml = $exporter->writequestion($q);
172 $this->assertRegExp('|<hint format=\"moodle_auto_format\">\s*<text>\s*' .
173 'This is the first hint\.\s*</text>\s*</hint>|', $xml);
174 $this->assertNotRegExp('|<shownumcorrect/>|', $xml);
175 $this->assertNotRegExp('|<clearwrong/>|', $xml);
176 $this->assertNotRegExp('|<options>|', $xml);
179 public function test_write_hint_with_parts() {
180 $q = $this->make_test_question();
181 $q->contextid = \context_system::instance()->id;
182 $q->name = 'Matching question';
183 $q->questiontext = 'Classify the animals.';
184 $q->generalfeedback = 'Frogs and toads are amphibians, the others are mammals.';
187 if (!isset($q->options)) {
188 $q->options = new stdClass();
190 $q->options->shuffleanswers = 1;
191 $q->options->correctfeedback = '';
192 $q->options->correctfeedbackformat = FORMAT_HTML;
193 $q->options->partiallycorrectfeedback = '';
194 $q->options->partiallycorrectfeedbackformat = FORMAT_HTML;
195 $q->options->incorrectfeedback = '';
196 $q->options->incorrectfeedbackformat = FORMAT_HTML;
198 $q->options->subquestions = array();
200 new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, false, true),
201 new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, false),
204 $exporter = new qformat_xml();
205 $xml = $exporter->writequestion($q);
208 '|<hint format=\"html\">\s*<text>\s*This is the first hint\.\s*</text>|', $xml);
210 '|<hint format=\"html\">\s*<text>\s*This is the second hint\.\s*</text>|', $xml);
211 list($ignored, $hint1, $hint2) = explode('<hint', $xml);
212 $this->assertNotRegExp('|<shownumcorrect/>|', $hint1);
213 $this->assertRegExp('|<clearwrong/>|', $hint1);
214 $this->assertRegExp('|<shownumcorrect/>|', $hint2);
215 $this->assertNotRegExp('|<clearwrong/>|', $hint2);
216 $this->assertNotRegExp('|<options>|', $xml);
219 public function test_import_hints_no_parts() {
223 <text>This is the first hint</text>
227 <text>This is the second hint</text>
233 $questionxml = xmlize($xml);
234 $qo = new stdClass();
236 $importer = new qformat_xml();
237 $importer->import_hints($qo, $questionxml['question'], false, false, 'html');
239 $this->assertEquals(array(
240 array('text' => 'This is the first hint',
241 'format' => FORMAT_HTML),
242 array('text' => 'This is the second hint',
243 'format' => FORMAT_HTML),
245 $this->assertFalse(isset($qo->hintclearwrong));
246 $this->assertFalse(isset($qo->hintshownumcorrect));
249 public function test_import_hints_with_parts() {
253 <text>This is the first hint</text>
257 <text>This is the second hint</text>
263 $questionxml = xmlize($xml);
264 $qo = new stdClass();
266 $importer = new qformat_xml();
267 $importer->import_hints($qo, $questionxml['question'], true, true, 'html');
269 $this->assertEquals(array(
270 array('text' => 'This is the first hint',
271 'format' => FORMAT_HTML),
272 array('text' => 'This is the second hint',
273 'format' => FORMAT_HTML),
275 $this->assertEquals(array(1, 0), $qo->hintclearwrong);
276 $this->assertEquals(array(0, 1), $qo->hintshownumcorrect);
279 public function test_import_no_hints_no_error() {
285 $questionxml = xmlize($xml);
286 $qo = new stdClass();
288 $importer = new qformat_xml();
289 $importer->import_hints($qo, $questionxml['question'], 'html');
291 $this->assertFalse(isset($qo->hint));
294 public function test_import_description() {
295 $xml = ' <question type="description">
297 <text>A description</text>
299 <questiontext format="html">
300 <text>The question text.</text>
303 <text>Here is some general feedback.</text>
305 <defaultgrade>0</defaultgrade>
309 <tag><text>tagDescription</text></tag>
310 <tag><text>tagTest</text></tag>
313 $xmldata = xmlize($xml);
315 $importer = new qformat_xml();
316 $q = $importer->import_description($xmldata['question']);
318 $expectedq = new stdClass();
319 $expectedq->qtype = 'description';
320 $expectedq->name = 'A description';
321 $expectedq->questiontext = 'The question text.';
322 $expectedq->questiontextformat = FORMAT_HTML;
323 $expectedq->generalfeedback = 'Here is some general feedback.';
324 $expectedq->defaultmark = 0;
325 $expectedq->length = 0;
326 $expectedq->penalty = 0;
327 $expectedq->tags = array('tagDescription', 'tagTest');
329 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
332 public function test_export_description() {
333 $qdata = new stdClass();
335 $qdata->contextid = \context_system::instance()->id;
336 $qdata->qtype = 'description';
337 $qdata->name = 'A description';
338 $qdata->questiontext = 'The question text.';
339 $qdata->questiontextformat = FORMAT_HTML;
340 $qdata->generalfeedback = 'Here is some general feedback.';
341 $qdata->generalfeedbackformat = FORMAT_HTML;
342 $qdata->defaultmark = 0;
346 $qdata->idnumber = null;
348 $exporter = new qformat_xml();
349 $xml = $exporter->writequestion($qdata);
351 $expectedxml = '<!-- question: 123 -->
352 <question type="description">
354 <text>A description</text>
356 <questiontext format="html">
357 <text>The question text.</text>
359 <generalfeedback format="html">
360 <text>Here is some general feedback.</text>
362 <defaultgrade>0</defaultgrade>
365 <idnumber></idnumber>
369 $this->assert_same_xml($expectedxml, $xml);
372 public function test_import_essay_20() {
373 $xml = ' <question type="essay">
375 <text>An essay</text>
377 <questiontext format="moodle_auto_format">
378 <text>Write something.</text>
381 <text>I hope you wrote something interesting.</text>
383 <defaultgrade>1</defaultgrade>
387 <tag><text>tagEssay</text></tag>
388 <tag><text>tagEssay20</text></tag>
389 <tag><text>tagTest</text></tag>
392 $xmldata = xmlize($xml);
394 $importer = new qformat_xml();
395 $q = $importer->import_essay($xmldata['question']);
397 $expectedq = new stdClass();
398 $expectedq->qtype = 'essay';
399 $expectedq->name = 'An essay';
400 $expectedq->questiontext = 'Write something.';
401 $expectedq->questiontextformat = FORMAT_MOODLE;
402 $expectedq->generalfeedback = 'I hope you wrote something interesting.';
403 $expectedq->defaultmark = 1;
404 $expectedq->length = 1;
405 $expectedq->penalty = 0;
406 $expectedq->responseformat = 'editor';
407 $expectedq->responserequired = 1;
408 $expectedq->responsefieldlines = 15;
409 $expectedq->minwordlimit = null;
410 $expectedq->minwordenabled = false;
411 $expectedq->maxwordlimit = null;
412 $expectedq->maxwordenabled = false;
413 $expectedq->attachments = 0;
414 $expectedq->attachmentsrequired = 0;
415 $expectedq->maxbytes = 0;
416 $expectedq->filetypeslist = null;
417 $expectedq->graderinfo['text'] = '';
418 $expectedq->graderinfo['format'] = FORMAT_MOODLE;
419 $expectedq->responsetemplate['text'] = '';
420 $expectedq->responsetemplate['format'] = FORMAT_MOODLE;
421 $expectedq->tags = array('tagEssay', 'tagEssay20', 'tagTest');
423 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
426 public function test_import_essay_21() {
427 $xml = ' <question type="essay">
429 <text>An essay</text>
431 <questiontext format="moodle_auto_format">
432 <text>Write something.</text>
435 <text>I hope you wrote something interesting.</text>
437 <defaultgrade>1</defaultgrade>
440 <responseformat>monospaced</responseformat>
441 <responserequired>0</responserequired>
442 <responsefieldlines>42</responsefieldlines>
443 <attachments>-1</attachments>
444 <attachmentsrequired>1</attachmentsrequired>
445 <graderinfo format="html">
446 <text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text>
448 <responsetemplate format="html">
449 <text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text>
452 <tag><text>tagEssay</text></tag>
453 <tag><text>tagEssay21</text></tag>
454 <tag><text>tagTest</text></tag>
457 $xmldata = xmlize($xml);
459 $importer = new qformat_xml();
460 $q = $importer->import_essay($xmldata['question']);
462 $expectedq = new stdClass();
463 $expectedq->qtype = 'essay';
464 $expectedq->name = 'An essay';
465 $expectedq->questiontext = 'Write something.';
466 $expectedq->questiontextformat = FORMAT_MOODLE;
467 $expectedq->generalfeedback = 'I hope you wrote something interesting.';
468 $expectedq->defaultmark = 1;
469 $expectedq->length = 1;
470 $expectedq->penalty = 0;
471 $expectedq->responseformat = 'monospaced';
472 $expectedq->responserequired = 0;
473 $expectedq->responsefieldlines = 42;
474 $expectedq->minwordlimit = null;
475 $expectedq->minwordenabled = false;
476 $expectedq->maxwordlimit = null;
477 $expectedq->maxwordenabled = false;
478 $expectedq->attachments = -1;
479 $expectedq->attachmentsrequired = 1;
480 $expectedq->maxbytes = 0;
481 $expectedq->filetypeslist = null;
482 $expectedq->graderinfo['text'] = '<p>Grade <b>generously</b>!</p>';
483 $expectedq->graderinfo['format'] = FORMAT_HTML;
484 $expectedq->responsetemplate['text'] = '<p>Here is something <b>really</b> interesting.</p>';
485 $expectedq->responsetemplate['format'] = FORMAT_HTML;
486 $expectedq->tags = array('tagEssay', 'tagEssay21', 'tagTest');
488 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
491 public function test_import_essay_311() {
492 $xml = ' <question type="essay">
494 <text>An essay</text>
496 <questiontext format="moodle_auto_format">
497 <text>Write something.</text>
500 <text>I hope you wrote something interesting.</text>
502 <defaultgrade>1</defaultgrade>
505 <responseformat>monospaced</responseformat>
506 <responserequired>0</responserequired>
507 <responsefieldlines>42</responsefieldlines>
508 <minwordlimit>10</minwordlimit>
509 <maxwordlimit>20</maxwordlimit>
510 <attachments>-1</attachments>
511 <attachmentsrequired>1</attachmentsrequired>
512 <maxbytes>52428800</maxbytes>
513 <filetypeslist>.pdf,.zip.,.docx</filetypeslist>
514 <graderinfo format="html">
515 <text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text>
517 <responsetemplate format="html">
518 <text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text>
521 <tag><text>tagEssay</text></tag>
522 <tag><text>tagEssay21</text></tag>
523 <tag><text>tagTest</text></tag>
526 $xmldata = xmlize($xml);
528 $importer = new qformat_xml();
529 $q = $importer->import_essay($xmldata['question']);
531 $expectedq = new stdClass();
532 $expectedq->qtype = 'essay';
533 $expectedq->name = 'An essay';
534 $expectedq->questiontext = 'Write something.';
535 $expectedq->questiontextformat = FORMAT_MOODLE;
536 $expectedq->generalfeedback = 'I hope you wrote something interesting.';
537 $expectedq->defaultmark = 1;
538 $expectedq->length = 1;
539 $expectedq->penalty = 0;
540 $expectedq->responseformat = 'monospaced';
541 $expectedq->responserequired = 0;
542 $expectedq->responsefieldlines = 42;
543 $expectedq->minwordlimit = 10;
544 $expectedq->minwordenabled = true;
545 $expectedq->maxwordlimit = 20;
546 $expectedq->maxwordenabled = true;
547 $expectedq->attachments = -1;
548 $expectedq->attachmentsrequired = 1;
549 $expectedq->maxbytes = 52428800; // 50MB.
550 $expectedq->filetypeslist = '.pdf,.zip.,.docx';
551 $expectedq->graderinfo['text'] = '<p>Grade <b>generously</b>!</p>';
552 $expectedq->graderinfo['format'] = FORMAT_HTML;
553 $expectedq->responsetemplate['text'] = '<p>Here is something <b>really</b> interesting.</p>';
554 $expectedq->responsetemplate['format'] = FORMAT_HTML;
555 $expectedq->tags = array('tagEssay', 'tagEssay21', 'tagTest');
557 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
560 public function test_export_essay() {
561 $qdata = new stdClass();
563 $qdata->contextid = \context_system::instance()->id;
564 $qdata->qtype = 'essay';
565 $qdata->name = 'An essay';
566 $qdata->questiontext = 'Write something.';
567 $qdata->questiontextformat = FORMAT_MOODLE;
568 $qdata->generalfeedback = 'I hope you wrote something interesting.';
569 $qdata->generalfeedbackformat = FORMAT_MOODLE;
570 $qdata->defaultmark = 1;
574 $qdata->idnumber = null;
575 $qdata->options = new stdClass();
576 $qdata->options->id = 456;
577 $qdata->options->questionid = 123;
578 $qdata->options->responseformat = 'monospaced';
579 $qdata->options->responserequired = 0;
580 $qdata->options->responsefieldlines = 42;
581 $qdata->options->minwordlimit = 10;
582 $qdata->options->maxwordlimit = 20;
583 $qdata->options->attachments = -1;
584 $qdata->options->attachmentsrequired = 1;
585 $qdata->options->graderinfo = '<p>Grade <b>generously</b>!</p>';
586 $qdata->options->graderinfoformat = FORMAT_HTML;
587 $qdata->options->responsetemplate = '<p>Here is something <b>really</b> interesting.</p>';
588 $qdata->options->responsetemplateformat = FORMAT_HTML;
589 $qdata->options->maxbytes = 52428800; // 50MB.
590 $qdata->options->filetypeslist = '.pdf,.zip.,.docx';
591 $exporter = new qformat_xml();
592 $xml = $exporter->writequestion($qdata);
594 $expectedxml = '<!-- question: 123 -->
595 <question type="essay">
597 <text>An essay</text>
599 <questiontext format="moodle_auto_format">
600 <text>Write something.</text>
602 <generalfeedback format="moodle_auto_format">
603 <text>I hope you wrote something interesting.</text>
605 <defaultgrade>1</defaultgrade>
608 <idnumber></idnumber>
609 <responseformat>monospaced</responseformat>
610 <responserequired>0</responserequired>
611 <responsefieldlines>42</responsefieldlines>
612 <minwordlimit>10</minwordlimit>
613 <maxwordlimit>20</maxwordlimit>
614 <attachments>-1</attachments>
615 <attachmentsrequired>1</attachmentsrequired>
616 <maxbytes>52428800</maxbytes>
617 <filetypeslist>.pdf,.zip.,.docx</filetypeslist>
618 <graderinfo format="html">
619 <text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text>
621 <responsetemplate format="html">
622 <text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text>
627 $this->assert_same_xml($expectedxml, $xml);
630 public function test_import_match_19() {
631 $xml = ' <question type="matching">
633 <text>Matching question</text>
635 <questiontext format="html">
636 <text>Match the upper and lower case letters.</text>
639 <text>The answer is A -> a, B -> b and C -> c.</text>
641 <defaultgrade>1</defaultgrade>
642 <penalty>0.3333333</penalty>
644 <shuffleanswers>false</shuffleanswers>
646 <text>Well done.</text>
648 <partiallycorrectfeedback>
649 <text>Not entirely.</text>
650 </partiallycorrectfeedback>
652 <text>Completely wrong!</text>
688 <tag><text>tagMatching</text></tag>
689 <tag><text>tagTest</text></tag>
692 $xmldata = xmlize($xml);
694 $importer = new qformat_xml();
695 $q = $importer->import_match($xmldata['question']);
697 $expectedq = new stdClass();
698 $expectedq->qtype = 'match';
699 $expectedq->name = 'Matching question';
700 $expectedq->questiontext = 'Match the upper and lower case letters.';
701 $expectedq->questiontextformat = FORMAT_HTML;
702 $expectedq->correctfeedback = array('text' => 'Well done.',
703 'format' => FORMAT_HTML);
704 $expectedq->partiallycorrectfeedback = array('text' => 'Not entirely.',
705 'format' => FORMAT_HTML);
706 $expectedq->shownumcorrect = false;
707 $expectedq->incorrectfeedback = array('text' => 'Completely wrong!',
708 'format' => FORMAT_HTML);
709 $expectedq->generalfeedback = 'The answer is A -> a, B -> b and C -> c.';
710 $expectedq->generalfeedbackformat = FORMAT_HTML;
711 $expectedq->defaultmark = 1;
712 $expectedq->length = 1;
713 $expectedq->penalty = 0.3333333;
714 $expectedq->shuffleanswers = 0;
715 $expectedq->subquestions = array(
716 array('text' => 'A', 'format' => FORMAT_HTML),
717 array('text' => 'B', 'format' => FORMAT_HTML),
718 array('text' => 'C', 'format' => FORMAT_HTML),
719 array('text' => '', 'format' => FORMAT_HTML));
720 $expectedq->subanswers = array('a', 'b', 'c', 'd');
721 $expectedq->hint = array(
722 array('text' => 'Hint 1', 'format' => FORMAT_HTML),
723 array('text' => '', 'format' => FORMAT_HTML),
725 $expectedq->hintshownumcorrect = array(true, true);
726 $expectedq->hintclearwrong = array(false, true);
727 $expectedq->tags = array('tagMatching', 'tagTest');
729 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
732 public function test_export_match() {
733 $qdata = new stdClass();
735 $qdata->contextid = \context_system::instance()->id;
736 $qdata->qtype = 'match';
737 $qdata->name = 'Matching question';
738 $qdata->questiontext = 'Match the upper and lower case letters.';
739 $qdata->questiontextformat = FORMAT_HTML;
740 $qdata->generalfeedback = 'The answer is A -> a, B -> b and C -> c.';
741 $qdata->generalfeedbackformat = FORMAT_HTML;
742 $qdata->defaultmark = 1;
744 $qdata->penalty = 0.3333333;
746 $qdata->idnumber = null;
748 $qdata->options = new stdClass();
749 $qdata->options->shuffleanswers = 1;
750 $qdata->options->correctfeedback = 'Well done.';
751 $qdata->options->correctfeedbackformat = FORMAT_HTML;
752 $qdata->options->partiallycorrectfeedback = 'Not entirely.';
753 $qdata->options->partiallycorrectfeedbackformat = FORMAT_HTML;
754 $qdata->options->shownumcorrect = false;
755 $qdata->options->incorrectfeedback = 'Completely wrong!';
756 $qdata->options->incorrectfeedbackformat = FORMAT_HTML;
758 $subq1 = new stdClass();
760 $subq1->questiontext = 'A';
761 $subq1->questiontextformat = FORMAT_HTML;
762 $subq1->answertext = 'a';
764 $subq2 = new stdClass();
766 $subq2->questiontext = 'B';
767 $subq2->questiontextformat = FORMAT_HTML;
768 $subq2->answertext = 'b';
770 $subq3 = new stdClass();
772 $subq3->questiontext = 'C';
773 $subq3->questiontextformat = FORMAT_HTML;
774 $subq3->answertext = 'c';
776 $subq4 = new stdClass();
778 $subq4->questiontext = '';
779 $subq4->questiontextformat = FORMAT_HTML;
780 $subq4->answertext = 'd';
782 $qdata->options->subquestions = array(
783 $subq1, $subq2, $subq3, $subq4);
785 $qdata->hints = array(
786 new question_hint_with_parts(0, 'Hint 1', FORMAT_HTML, true, false),
787 new question_hint_with_parts(0, '', FORMAT_HTML, true, true),
790 $exporter = new qformat_xml();
791 $xml = $exporter->writequestion($qdata);
793 $expectedxml = '<!-- question: 123 -->
794 <question type="matching">
796 <text>Matching question</text>
798 <questiontext format="html">
799 <text>Match the upper and lower case letters.</text>
801 <generalfeedback format="html">
802 <text><![CDATA[The answer is A -> a, B -> b and C -> c.]]></text>
804 <defaultgrade>1</defaultgrade>
805 <penalty>0.3333333</penalty>
807 <idnumber></idnumber>
808 <shuffleanswers>true</shuffleanswers>
809 <correctfeedback format="html">
810 <text>Well done.</text>
812 <partiallycorrectfeedback format="html">
813 <text>Not entirely.</text>
814 </partiallycorrectfeedback>
815 <incorrectfeedback format="html">
816 <text>Completely wrong!</text>
818 <subquestion format="html">
824 <subquestion format="html">
830 <subquestion format="html">
836 <subquestion format="html">
854 $this->assert_same_xml($expectedxml, $xml);
857 public function test_import_multichoice_19() {
858 $xml = ' <question type="multichoice">
860 <text>Multiple choice question</text>
862 <questiontext format="html">
863 <text>Which are the even numbers?</text>
866 <text>The even numbers are 2 and 4.</text>
868 <defaultgrade>2</defaultgrade>
869 <penalty>0.3333333</penalty>
871 <single>false</single>
872 <shuffleanswers>false</shuffleanswers>
873 <answernumbering>abc</answernumbering>
875 <text><![CDATA[<p>Your answer is correct.</p>]]></text>
877 <partiallycorrectfeedback>
878 <text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
879 </partiallycorrectfeedback>
881 <text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
884 <answer fraction="0">
890 <answer fraction="100">
896 <answer fraction="0">
902 <answer fraction="100">
915 $xmldata = xmlize($xml);
917 $importer = new qformat_xml();
918 $q = $importer->import_multichoice($xmldata['question']);
920 $expectedq = new stdClass();
921 $expectedq->qtype = 'multichoice';
922 $expectedq->name = 'Multiple choice question';
923 $expectedq->questiontext = 'Which are the even numbers?';
924 $expectedq->questiontextformat = FORMAT_HTML;
925 $expectedq->correctfeedback = array(
926 'text' => '<p>Your answer is correct.</p>',
927 'format' => FORMAT_HTML);
928 $expectedq->shownumcorrect = false;
929 $expectedq->partiallycorrectfeedback = array(
930 'text' => '<p>Your answer is partially correct.</p>',
931 'format' => FORMAT_HTML);
932 $expectedq->shownumcorrect = true;
933 $expectedq->incorrectfeedback = array(
934 'text' => '<p>Your answer is incorrect.</p>',
935 'format' => FORMAT_HTML);
936 $expectedq->generalfeedback = 'The even numbers are 2 and 4.';
937 $expectedq->defaultmark = 2;
938 $expectedq->length = 1;
939 $expectedq->penalty = 0.3333333;
940 $expectedq->shuffleanswers = 0;
941 $expectedq->single = false;
943 $expectedq->answer = array(
944 array('text' => '1', 'format' => FORMAT_HTML),
945 array('text' => '2', 'format' => FORMAT_HTML),
946 array('text' => '3', 'format' => FORMAT_HTML),
947 array('text' => '4', 'format' => FORMAT_HTML));
948 $expectedq->fraction = array(0, 1, 0, 1);
949 $expectedq->feedback = array(
950 array('text' => '', 'format' => FORMAT_HTML),
951 array('text' => '', 'format' => FORMAT_HTML),
952 array('text' => '', 'format' => FORMAT_HTML),
953 array('text' => '', 'format' => FORMAT_HTML));
955 $expectedq->hint = array(
956 array('text' => 'Hint 1.', 'format' => FORMAT_HTML),
957 array('text' => 'Hint 2.', 'format' => FORMAT_HTML),
959 $expectedq->hintshownumcorrect = array(false, false);
960 $expectedq->hintclearwrong = array(false, false);
962 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
965 public function test_export_multichoice() {
966 $qdata = new stdClass();
968 $qdata->contextid = \context_system::instance()->id;
969 $qdata->qtype = 'multichoice';
970 $qdata->name = 'Multiple choice question';
971 $qdata->questiontext = 'Which are the even numbers?';
972 $qdata->questiontextformat = FORMAT_HTML;
973 $qdata->generalfeedback = 'The even numbers are 2 and 4.';
974 $qdata->generalfeedbackformat = FORMAT_HTML;
975 $qdata->defaultmark = 2;
977 $qdata->penalty = 0.3333333;
979 $qdata->idnumber = null;
981 $qdata->options = new stdClass();
982 $qdata->options->single = 0;
983 $qdata->options->shuffleanswers = 0;
984 $qdata->options->answernumbering = 'abc';
985 $qdata->options->showstandardinstruction = 0;
986 $qdata->options->correctfeedback = '<p>Your answer is correct.</p>';
987 $qdata->options->correctfeedbackformat = FORMAT_HTML;
988 $qdata->options->partiallycorrectfeedback = '<p>Your answer is partially correct.</p>';
989 $qdata->options->partiallycorrectfeedbackformat = FORMAT_HTML;
990 $qdata->options->shownumcorrect = 1;
991 $qdata->options->incorrectfeedback = '<p>Your answer is incorrect.</p>';
992 $qdata->options->incorrectfeedbackformat = FORMAT_HTML;
994 $qdata->options->answers = array(
995 13 => new question_answer(13, '1', 0, '', FORMAT_HTML),
996 14 => new question_answer(14, '2', 1, '', FORMAT_HTML),
997 15 => new question_answer(15, '3', 0, '', FORMAT_HTML),
998 16 => new question_answer(16, '4', 1, '', FORMAT_HTML),
1001 $qdata->hints = array(
1002 new question_hint_with_parts(0, 'Hint 1.', FORMAT_HTML, false, false),
1003 new question_hint_with_parts(0, 'Hint 2.', FORMAT_HTML, false, false),
1006 $exporter = new qformat_xml();
1007 $xml = $exporter->writequestion($qdata);
1009 $expectedxml = '<!-- question: 123 -->
1010 <question type="multichoice">
1012 <text>Multiple choice question</text>
1014 <questiontext format="html">
1015 <text>Which are the even numbers?</text>
1017 <generalfeedback format="html">
1018 <text>The even numbers are 2 and 4.</text>
1020 <defaultgrade>2</defaultgrade>
1021 <penalty>0.3333333</penalty>
1023 <idnumber></idnumber>
1024 <single>false</single>
1025 <shuffleanswers>false</shuffleanswers>
1026 <answernumbering>abc</answernumbering>
1027 <showstandardinstruction>0</showstandardinstruction>
1028 <correctfeedback format="html">
1029 <text><![CDATA[<p>Your answer is correct.</p>]]></text>
1031 <partiallycorrectfeedback format="html">
1032 <text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
1033 </partiallycorrectfeedback>
1034 <incorrectfeedback format="html">
1035 <text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
1036 </incorrectfeedback>
1038 <answer fraction="0" format="plain_text">
1040 <feedback format="html">
1044 <answer fraction="100" format="plain_text">
1046 <feedback format="html">
1050 <answer fraction="0" format="plain_text">
1052 <feedback format="html">
1056 <answer fraction="100" format="plain_text">
1058 <feedback format="html">
1062 <hint format="html">
1063 <text>Hint 1.</text>
1065 <hint format="html">
1066 <text>Hint 2.</text>
1071 $this->assert_same_xml($expectedxml, $xml);
1074 public function test_import_numerical_19() {
1075 $xml = ' <question type="numerical">
1077 <text>Numerical question</text>
1079 <questiontext format="html">
1080 <text>What is the answer?</text>
1083 <text>General feedback: Think Hitch-hikers guide to the Galaxy.</text>
1085 <defaultgrade>1</defaultgrade>
1086 <penalty>0.1</penalty>
1088 <answer fraction="100">
1091 <text>Well done!</text>
1093 <tolerance>0.001</tolerance>
1095 <answer fraction="0">
1098 <text>What were you thinking?!</text>
1100 <tolerance>1</tolerance>
1102 <answer fraction="0">
1105 <text>Completely wrong.</text>
1107 <tolerance></tolerance>
1110 $xmldata = xmlize($xml);
1112 $importer = new qformat_xml();
1113 $q = $importer->import_numerical($xmldata['question']);
1115 $expectedq = new stdClass();
1116 $expectedq->qtype = 'numerical';
1117 $expectedq->name = 'Numerical question';
1118 $expectedq->questiontext = 'What is the answer?';
1119 $expectedq->questiontextformat = FORMAT_HTML;
1120 $expectedq->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.';
1121 $expectedq->generalfeedbackformat = FORMAT_HTML;
1122 $expectedq->defaultmark = 1;
1123 $expectedq->length = 1;
1124 $expectedq->penalty = 0.1;
1126 $expectedq->answer = array('42', '13', '*');
1127 $expectedq->fraction = array(1, 0, 0);
1128 $expectedq->feedback = array(
1129 array('text' => 'Well done!',
1130 'format' => FORMAT_HTML),
1131 array('text' => 'What were you thinking?!',
1132 'format' => FORMAT_HTML),
1133 array('text' => 'Completely wrong.',
1134 'format' => FORMAT_HTML));
1135 $expectedq->tolerance = array(0.001, 1, '');
1137 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
1140 public function test_export_numerical() {
1141 question_bank::load_question_definition_classes('numerical');
1143 $qdata = new stdClass();
1145 $qdata->contextid = \context_system::instance()->id;
1146 $qdata->qtype = 'numerical';
1147 $qdata->name = 'Numerical question';
1148 $qdata->questiontext = 'What is the answer?';
1149 $qdata->questiontextformat = FORMAT_HTML;
1150 $qdata->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.';
1151 $qdata->generalfeedbackformat = FORMAT_HTML;
1152 $qdata->defaultmark = 1;
1154 $qdata->penalty = 0.1;
1156 $qdata->idnumber = null;
1158 $qdata->options = new stdClass();
1159 $qdata->options->answers = array(
1160 13 => new qtype_numerical_answer(13, '42', 1, 'Well done!',
1161 FORMAT_HTML, 0.001),
1162 14 => new qtype_numerical_answer(14, '13', 0, 'What were you thinking?!',
1164 15 => new qtype_numerical_answer(15, '*', 0, 'Completely wrong.',
1168 $qdata->options->units = array();
1170 $exporter = new qformat_xml();
1171 $xml = $exporter->writequestion($qdata);
1173 $expectedxml = '<!-- question: 123 -->
1174 <question type="numerical">
1176 <text>Numerical question</text>
1178 <questiontext format="html">
1179 <text>What is the answer?</text>
1181 <generalfeedback format="html">
1182 <text>General feedback: Think Hitch-hikers guide to the Galaxy.</text>
1184 <defaultgrade>1</defaultgrade>
1185 <penalty>0.1</penalty>
1187 <idnumber></idnumber>
1188 <answer fraction="100" format="plain_text">
1190 <feedback format="html">
1191 <text>Well done!</text>
1193 <tolerance>0.001</tolerance>
1195 <answer fraction="0" format="plain_text">
1197 <feedback format="html">
1198 <text>What were you thinking?!</text>
1200 <tolerance>1</tolerance>
1202 <answer fraction="0" format="plain_text">
1204 <feedback format="html">
1205 <text>Completely wrong.</text>
1207 <tolerance>0</tolerance>
1212 $this->assert_same_xml($expectedxml, $xml);
1215 public function test_import_shortanswer_19() {
1216 $xml = ' <question type="shortanswer">
1218 <text>Short answer question</text>
1220 <questiontext format="html">
1221 <text>Fill in the gap in this sequence: Alpha, ________, Gamma.</text>
1224 <text>The answer is Beta.</text>
1226 <defaultgrade>1</defaultgrade>
1227 <penalty>0.3333333</penalty>
1229 <usecase>0</usecase>
1230 <answer fraction="100" format="plain_text">
1233 <text>Well done!</text>
1236 <answer fraction="0" format="plain_text">
1249 $xmldata = xmlize($xml);
1251 $importer = new qformat_xml();
1252 $q = $importer->import_shortanswer($xmldata['question']);
1254 $expectedq = new stdClass();
1255 $expectedq->qtype = 'shortanswer';
1256 $expectedq->name = 'Short answer question';
1257 $expectedq->questiontext = 'Fill in the gap in this sequence: Alpha, ________, Gamma.';
1258 $expectedq->questiontextformat = FORMAT_HTML;
1259 $expectedq->generalfeedback = 'The answer is Beta.';
1260 $expectedq->usecase = false;
1261 $expectedq->defaultmark = 1;
1262 $expectedq->length = 1;
1263 $expectedq->penalty = 0.3333333;
1265 $expectedq->answer = array('Beta', '*');
1266 $expectedq->fraction = array(1, 0);
1267 $expectedq->feedback = array(
1268 array('text' => 'Well done!', 'format' => FORMAT_HTML),
1269 array('text' => 'Doh!', 'format' => FORMAT_HTML));
1271 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
1274 public function test_export_shortanswer() {
1275 $qdata = new stdClass();
1277 $qdata->contextid = \context_system::instance()->id;
1278 $qdata->qtype = 'shortanswer';
1279 $qdata->name = 'Short answer question';
1280 $qdata->questiontext = 'Fill in the gap in this sequence: Alpha, ________, Gamma.';
1281 $qdata->questiontextformat = FORMAT_HTML;
1282 $qdata->generalfeedback = 'The answer is Beta.';
1283 $qdata->generalfeedbackformat = FORMAT_HTML;
1284 $qdata->defaultmark = 1;
1286 $qdata->penalty = 0.3333333;
1288 $qdata->idnumber = null;
1290 $qdata->options = new stdClass();
1291 $qdata->options->usecase = 0;
1293 $qdata->options->answers = array(
1294 13 => new question_answer(13, 'Beta', 1, 'Well done!', FORMAT_HTML),
1295 14 => new question_answer(14, '*', 0, 'Doh!', FORMAT_HTML),
1298 $qdata->hints = array(
1299 new question_hint(0, 'Hint 1', FORMAT_HTML),
1300 new question_hint(0, 'Hint 2', FORMAT_HTML),
1303 $exporter = new qformat_xml();
1304 $xml = $exporter->writequestion($qdata);
1306 $expectedxml = '<!-- question: 123 -->
1307 <question type="shortanswer">
1309 <text>Short answer question</text>
1311 <questiontext format="html">
1312 <text>Fill in the gap in this sequence: Alpha, ________, Gamma.</text>
1314 <generalfeedback format="html">
1315 <text>The answer is Beta.</text>
1317 <defaultgrade>1</defaultgrade>
1318 <penalty>0.3333333</penalty>
1320 <idnumber></idnumber>
1321 <usecase>0</usecase>
1322 <answer fraction="100" format="plain_text">
1324 <feedback format="html">
1325 <text>Well done!</text>
1328 <answer fraction="0" format="plain_text">
1330 <feedback format="html">
1334 <hint format="html">
1337 <hint format="html">
1343 $this->assert_same_xml($expectedxml, $xml);
1346 public function test_import_truefalse_19() {
1347 $xml = ' <question type="truefalse">
1349 <text>True false question</text>
1351 <questiontext format="html">
1352 <text>The answer is true.</text>
1355 <text>General feedback: You should have chosen true.</text>
1357 <defaultgrade>1</defaultgrade>
1358 <penalty>1</penalty>
1360 <answer fraction="100">
1363 <text>Well done!</text>
1366 <answer fraction="0">
1373 $xmldata = xmlize($xml);
1375 $importer = new qformat_xml();
1376 $q = $importer->import_truefalse($xmldata['question']);
1378 $expectedq = new stdClass();
1379 $expectedq->qtype = 'truefalse';
1380 $expectedq->name = 'True false question';
1381 $expectedq->questiontext = 'The answer is true.';
1382 $expectedq->questiontextformat = FORMAT_HTML;
1383 $expectedq->generalfeedback = 'General feedback: You should have chosen true.';
1384 $expectedq->defaultmark = 1;
1385 $expectedq->length = 1;
1386 $expectedq->penalty = 1;
1388 $expectedq->feedbacktrue = array('text' => 'Well done!',
1389 'format' => FORMAT_HTML);
1390 $expectedq->feedbackfalse = array('text' => 'Doh!',
1391 'format' => FORMAT_HTML);
1392 $expectedq->correctanswer = true;
1394 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
1397 public function test_import_truefalse_with_idnumber() {
1398 $xml = ' <question type="truefalse">
1400 <text>True false question</text>
1402 <questiontext format="html">
1403 <text>The answer is true.</text>
1406 <text>General feedback: You should have chosen true.</text>
1408 <defaultgrade>1</defaultgrade>
1409 <penalty>1</penalty>
1411 <idnumber>TestIdNum1</idnumber>
1412 <answer fraction="100">
1415 <text>Well done!</text>
1418 <answer fraction="0">
1425 $xmldata = xmlize($xml);
1427 $importer = new qformat_xml();
1428 $q = $importer->import_truefalse($xmldata['question']);
1430 $expectedq = new stdClass();
1431 $expectedq->qtype = 'truefalse';
1432 $expectedq->name = 'True false question';
1433 $expectedq->questiontext = 'The answer is true.';
1434 $expectedq->questiontextformat = FORMAT_HTML;
1435 $expectedq->generalfeedback = 'General feedback: You should have chosen true.';
1436 $expectedq->defaultmark = 1;
1437 $expectedq->length = 1;
1438 $expectedq->penalty = 1;
1439 $expectedq->idnumber = 'TestIdNum1';
1441 $expectedq->feedbacktrue = array('text' => 'Well done!',
1442 'format' => FORMAT_HTML);
1443 $expectedq->feedbackfalse = array('text' => 'Doh!',
1444 'format' => FORMAT_HTML);
1445 $expectedq->correctanswer = true;
1447 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
1450 public function test_export_truefalse() {
1451 $qdata = new stdClass();
1453 $qdata->contextid = \context_system::instance()->id;
1454 $qdata->qtype = 'truefalse';
1455 $qdata->name = 'True false question';
1456 $qdata->questiontext = 'The answer is true.';
1457 $qdata->questiontextformat = FORMAT_HTML;
1458 $qdata->generalfeedback = 'General feedback: You should have chosen true.';
1459 $qdata->generalfeedbackformat = FORMAT_HTML;
1460 $qdata->defaultmark = 1;
1462 $qdata->penalty = 1;
1464 $qdata->idnumber = null;
1466 $qdata->options = new stdClass();
1467 $qdata->options->answers = array(
1468 1 => new question_answer(1, 'True', 1, 'Well done!', FORMAT_HTML),
1469 2 => new question_answer(2, 'False', 0, 'Doh!', FORMAT_HTML),
1471 $qdata->options->trueanswer = 1;
1472 $qdata->options->falseanswer = 2;
1474 $exporter = new qformat_xml();
1475 $xml = $exporter->writequestion($qdata);
1477 $expectedxml = '<!-- question: 12 -->
1478 <question type="truefalse">
1480 <text>True false question</text>
1482 <questiontext format="html">
1483 <text>The answer is true.</text>
1485 <generalfeedback format="html">
1486 <text>General feedback: You should have chosen true.</text>
1488 <defaultgrade>1</defaultgrade>
1489 <penalty>1</penalty>
1491 <idnumber></idnumber>
1492 <answer fraction="100" format="plain_text">
1494 <feedback format="html">
1495 <text>Well done!</text>
1498 <answer fraction="0" format="plain_text">
1500 <feedback format="html">
1507 $this->assert_same_xml($expectedxml, $xml);
1510 public function test_export_truefalse_with_idnumber() {
1511 $qdata = new stdClass();
1513 $qdata->contextid = \context_system::instance()->id;
1514 $qdata->qtype = 'truefalse';
1515 $qdata->name = 'True false question';
1516 $qdata->questiontext = 'The answer is true.';
1517 $qdata->questiontextformat = FORMAT_HTML;
1518 $qdata->generalfeedback = 'General feedback: You should have chosen true.';
1519 $qdata->generalfeedbackformat = FORMAT_HTML;
1520 $qdata->defaultmark = 1;
1522 $qdata->penalty = 1;
1524 $qdata->idnumber = 'TestIDNum2';
1526 $qdata->options = new stdClass();
1527 $qdata->options->answers = array(
1528 1 => new question_answer(1, 'True', 1, 'Well done!', FORMAT_HTML),
1529 2 => new question_answer(2, 'False', 0, 'Doh!', FORMAT_HTML),
1531 $qdata->options->trueanswer = 1;
1532 $qdata->options->falseanswer = 2;
1534 $exporter = new qformat_xml();
1535 $xml = $exporter->writequestion($qdata);
1537 $expectedxml = '<!-- question: 12 -->
1538 <question type="truefalse">
1540 <text>True false question</text>
1542 <questiontext format="html">
1543 <text>The answer is true.</text>
1545 <generalfeedback format="html">
1546 <text>General feedback: You should have chosen true.</text>
1548 <defaultgrade>1</defaultgrade>
1549 <penalty>1</penalty>
1551 <idnumber>TestIDNum2</idnumber>
1552 <answer fraction="100" format="plain_text">
1554 <feedback format="html">
1555 <text>Well done!</text>
1558 <answer fraction="0" format="plain_text">
1560 <feedback format="html">
1567 $this->assert_same_xml($expectedxml, $xml);
1570 public function test_import_multianswer() {
1571 $xml = ' <question type="cloze">
1573 <text>Simple multianswer</text>
1575 <questiontext format="html">
1576 <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>
1578 <generalfeedback format="html">
1579 <text><![CDATA[General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea".]]></text>
1581 <penalty>0.5</penalty>
1583 <hint format="html">
1586 <hint format="html">
1590 <tag><text>tagCloze</text></tag>
1591 <tag><text>tagTest</text></tag>
1595 $xmldata = xmlize($xml);
1597 $importer = new qformat_xml();
1598 $q = $importer->import_multianswer($xmldata['question']);
1600 // Annoyingly, import works in a weird way (it duplicates code, rather
1601 // than just calling save_question) so we cannot use
1602 // test_question_maker::get_question_form_data('multianswer', 'twosubq').
1603 $expectedqa = new stdClass();
1604 $expectedqa->name = 'Simple multianswer';
1605 $expectedqa->qtype = 'multianswer';
1606 $expectedqa->questiontext = 'Complete this opening line of verse: "The {#1} and the {#2} went to sea".';
1607 $expectedqa->generalfeedback =
1608 'General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea".';
1609 $expectedqa->defaultmark = 2;
1610 $expectedqa->penalty = 0.5;
1612 $expectedqa->hint = array(
1613 array('text' => 'Hint 1', 'format' => FORMAT_HTML),
1614 array('text' => 'Hint 2', 'format' => FORMAT_HTML),
1617 $sa = new stdClass();
1619 $sa->questiontext = array('text' => '{1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer}',
1620 'format' => FORMAT_HTML, 'itemid' => null);
1621 $sa->generalfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
1622 $sa->defaultmark = 1.0;
1623 $sa->qtype = 'shortanswer';
1626 $sa->answer = array('Dog', 'Owl', '*');
1627 $sa->fraction = array(0, 1, 0);
1628 $sa->feedback = array(
1629 array('text' => 'Wrong, silly!', 'format' => FORMAT_HTML, 'itemid' => null),
1630 array('text' => 'Well done!', 'format' => FORMAT_HTML, 'itemid' => null),
1631 array('text' => 'Wrong answer', 'format' => FORMAT_HTML, 'itemid' => null),
1634 $mc = new stdClass();
1636 $mc->generalfeedback = '';
1637 $mc->questiontext = array('text' => '{1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~' .
1638 'Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!}',
1639 'format' => FORMAT_HTML, 'itemid' => null);
1640 $mc->generalfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
1641 $mc->defaultmark = 1.0;
1642 $mc->qtype = 'multichoice';
1646 $mc->shuffleanswers = 0;
1647 $mc->correctfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
1648 $mc->partiallycorrectfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
1649 $mc->incorrectfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
1650 $mc->answernumbering = 0;
1652 $mc->answer = array(
1653 array('text' => 'Bow-wow', 'format' => FORMAT_HTML, 'itemid' => null),
1654 array('text' => 'Wiggly worm', 'format' => FORMAT_HTML, 'itemid' => null),
1655 array('text' => 'Pussy-cat', 'format' => FORMAT_HTML, 'itemid' => null),
1657 $mc->fraction = array(0, 0, 1);
1658 $mc->feedback = array(
1659 array('text' => 'You seem to have a dog obsessions!', 'format' => FORMAT_HTML, 'itemid' => null),
1660 array('text' => 'Now you are just being ridiculous!', 'format' => FORMAT_HTML, 'itemid' => null),
1661 array('text' => 'Well done!', 'format' => FORMAT_HTML, 'itemid' => null),
1664 $expectedqa->options = new stdClass();
1665 $expectedqa->options->questions = array(
1669 $expectedqa->tags = array('tagCloze', 'tagTest');
1671 $this->assertEquals($expectedqa->hint, $q->hint);
1672 $this->assertEquals($expectedqa->options->questions[1], $q->options->questions[1]);
1673 $this->assertEquals($expectedqa->options->questions[2], $q->options->questions[2]);
1674 $this->assert(new question_check_specified_fields_expectation($expectedqa), $q);
1677 public function test_export_multianswer() {
1678 $qdata = test_question_maker::get_question_data('multianswer', 'twosubq');
1679 $qdata->contextid = \context_system::instance()->id;
1680 $exporter = new qformat_xml();
1681 $xml = $exporter->writequestion($qdata);
1683 $expectedxml = '<!-- question: 0 -->
1684 <question type="cloze">
1686 <text>Simple multianswer</text>
1688 <questiontext format="html">
1689 <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>
1691 <generalfeedback format="html">
1692 <text><![CDATA[General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea]]></text>
1694 <penalty>0.3333333</penalty>
1696 <idnumber></idnumber>
1697 <hint format="html">
1700 <hint format="html">
1706 $this->assert_same_xml($expectedxml, $xml);
1709 public function test_export_multianswer_withdollars() {
1710 $qdata = test_question_maker::get_question_data('multianswer', 'dollarsigns');
1711 $qdata->contextid = \context_system::instance()->id;
1712 $exporter = new qformat_xml();
1713 $xml = $exporter->writequestion($qdata);
1715 $expectedxml = '<!-- question: 0 -->
1716 <question type="cloze">
1718 <text>Multianswer with $s</text>
1720 <questiontext format="html">
1721 <text>Which is the right order? {1:MULTICHOICE:=y,y,$3~$3,y,y}</text>
1723 <generalfeedback format="html">
1726 <penalty>0.3333333</penalty>
1728 <idnumber></idnumber>
1732 $this->assert_same_xml($expectedxml, $xml);
1735 public function test_import_files_as_draft() {
1736 $this->resetAfterTest();
1737 $this->setAdminUser();
1740 <questiontext format="html">
1741 <text><![CDATA[<p><a href="@@PLUGINFILE@@/moodle.txt">This text file</a> contains the word 'Moodle'.</p>]]></text>
1742 <file name="moodle.txt" encoding="base64">TW9vZGxl</file>
1746 $textxml = xmlize($xml);
1747 $qo = new stdClass();
1749 $importer = new qformat_xml();
1750 $draftitemid = $importer->import_files_as_draft($textxml['questiontext']['#']['file']);
1751 $files = file_get_drafarea_files($draftitemid);
1753 $this->assertEquals(1, count($files->list));
1755 $file = $files->list[0];
1756 $this->assertEquals('moodle.txt', $file->filename);
1757 $this->assertEquals('/', $file->filepath);
1758 $this->assertEquals(6, $file->size);
1761 public function test_import_truefalse_wih_files() {
1762 $this->resetAfterTest();
1763 $this->setAdminUser();
1765 $xml = '<question type="truefalse">
1767 <text>truefalse</text>
1769 <questiontext format="html">
1770 <text><![CDATA[<p><a href="@@PLUGINFILE@@/myfolder/moodle.txt">This text file</a> contains the word Moodle.</p>]]></text>
1771 <file name="moodle.txt" path="/myfolder/" encoding="base64">TW9vZGxl</file>
1773 <generalfeedback format="html">
1774 <text><![CDATA[<p>For further information, see the documentation about Moodle.</p>]]></text>
1776 <defaultgrade>1.0000000</defaultgrade>
1777 <penalty>1.0000000</penalty>
1779 <answer fraction="100" format="moodle_auto_format">
1781 <feedback format="html">
1785 <answer fraction="0" format="moodle_auto_format">
1787 <feedback format="html">
1792 $xmldata = xmlize($xml);
1794 $importer = new qformat_xml();
1795 $q = $importer->import_truefalse($xmldata['question']);
1797 $draftitemid = $q->questiontextitemid;
1798 $files = file_get_drafarea_files($draftitemid, '/myfolder/');
1800 $this->assertEquals(1, count($files->list));
1802 $file = $files->list[0];
1803 $this->assertEquals('moodle.txt', $file->filename);
1804 $this->assertEquals('/myfolder/', $file->filepath);
1805 $this->assertEquals(6, $file->size);
1808 public function test_create_dummy_question() {
1810 $testobject = new mock_qformat_xml();
1811 $categoryname = 'name1';
1812 $categoryinfo = new stdClass();
1813 $categoryinfo->info = 'info1';
1814 $categoryinfo->infoformat = 'infoformat1';
1815 $categoryinfo->idnumber = null;
1816 $dummyquestion = $testobject->mock_create_dummy_question_representing_category($categoryname, $categoryinfo);
1818 $this->assertEquals('category', $dummyquestion->qtype);
1819 $this->assertEquals($categoryname, $dummyquestion->category);
1820 $this->assertEquals($categoryinfo->info, $dummyquestion->info);
1821 $this->assertEquals($categoryinfo->infoformat, $dummyquestion->infoformat);
1822 $this->assertEquals('Switch category to ' . $categoryname, $dummyquestion->name);
1823 $this->assertEquals(0, $dummyquestion->id);
1824 $this->assertEquals('', $dummyquestion->questiontextformat);
1825 $this->assertEquals(0, $dummyquestion->contextid);
1830 * Class mock_qformat_xml exists only to enable testing of the create dummy question category.
1831 * @package qformat_xml
1832 * @copyright 2018 The Open University
1833 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1835 class mock_qformat_xml extends qformat_xml {
1837 * Make public an otherwise protected function.
1838 * @param string $categoryname the name of the category
1839 * @param object $categoryinfo description of the category
1841 public function mock_create_dummy_question_representing_category(string $categoryname, $categoryinfo) {
1842 return $this->create_dummy_question_representing_category($categoryname, $categoryinfo);