MDL-61410 question: fix unit tests failures
[moodle.git] / question / format / xml / tests / xmlformat_test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
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.
8 //
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.
13 //
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/>.
17 /**
18  * Unit tests for the Moodle XML format.
19  *
20  * @package    qformat_xml
21  * @copyright  2010 The Open University
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
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');
34 /**
35  * Unit tests for the matching question definition class.
36  *
37  * @copyright  2009 The Open University
38  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39  */
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));
44     }
46     public function make_test_question() {
47         global $USER;
48         $q = new stdClass();
49         $q->id = 0;
50         $q->contextid = 0;
51         $q->category = 0;
52         $q->parent = 0;
53         $q->questiontextformat = FORMAT_HTML;
54         $q->generalfeedbackformat = FORMAT_HTML;
55         $q->defaultmark = 1;
56         $q->penalty = 0.3333333;
57         $q->length = 1;
58         $q->stamp = make_unique_id_code();
59         $q->version = make_unique_id_code();
60         $q->hidden = 0;
61         $q->timecreated = time();
62         $q->timemodified = time();
63         $q->createdby = $USER->id;
64         $q->modifiedby = $USER->id;
65         return $q;
66     }
68     /**
69      * The data the XML import format sends to save_question is not exactly
70      * the same as the data returned from the editing form, so this method
71      * makes necessary changes to the return value of
72      * test_question_maker::get_question_form_data so that the tests can work.
73      * @param object $expectedq as returned by get_question_form_data.
74      * @return object one more likely to match the return value of import_...().
75      */
76     public function remove_irrelevant_form_data_fields($expectedq) {
77         return $this->itemid_to_files($expectedq);
78     }
80     /**
81      * Becuase XML import uses a files array instead of an itemid integer to
82      * handle saving files with a question, we need to covert the output of
83      * test_question_maker::get_question_form_data to match. This method recursively
84      * replaces all array elements with key itemid with an array entry with
85      * key files and value an empty array.
86      *
87      * @param mixed $var any data structure.
88      * @return mixed an equivalent structure with the relacements made.
89      */
90     protected function itemid_to_files($var) {
91         if (is_object($var)) {
92             $newvar = new stdClass();
93             foreach (get_object_vars($var) as $field => $value) {
94                 $newvar->$field = $this->itemid_to_files($value);
95             }
97         } else if (is_array($var)) {
98             $newvar = array();
99             foreach ($var as $index => $value) {
100                 if ($index === 'itemid') {
101                     $newvar['files'] = array();
102                 } else {
103                     $newvar[$index] = $this->itemid_to_files($value);
104                 }
105             }
107         } else {
108             $newvar = $var;
109         }
111         return $newvar;
112     }
114     public function test_xml_escape_simple_input_not_escaped() {
115         $exporter = new qformat_xml();
116         $string = 'Nothing funny here. Even if we go to a café or to 日本.';
117         $this->assertEquals($string, $exporter->xml_escape($string));
118     }
120     public function test_xml_escape_html_wrapped_in_cdata() {
121         $exporter = new qformat_xml();
122         $string = '<p>Nothing <b>funny<b> here. Even if we go to a café or to 日本.</p>';
123         $this->assertEquals('<![CDATA[' . $string . ']]>', $exporter->xml_escape($string));
124     }
126     public function test_xml_escape_script_tag_handled_ok() {
127         $exporter = new qformat_xml();
128         $input = '<script><![CDATA[alert(1<2);]]></script>';
129         $expected = '<![CDATA[<script><![CDATA[alert(1<2);]]]]><![CDATA[></script>]]>';
130         $this->assertEquals($expected, $exporter->xml_escape($input));
132         // Check that parsing the expected result does give the input again.
133         $parsed = simplexml_load_string('<div>' . $expected . '</div>');
134         $this->assertEquals($input, $parsed->xpath('//div')[0]);
135     }
137     public function test_xml_escape_code_that_looks_like_cdata_end_ok() {
138         $exporter = new qformat_xml();
139         $input = "if (x[[0]]>a) print('hah');";
140         $expected = "<![CDATA[if (x[[0]]]]><![CDATA[>a) print('hah');]]>";
141         $this->assertEquals($expected, $exporter->xml_escape($input));
143         // Check that parsing the expected result does give the input again.
144         $parsed = simplexml_load_string('<div>' . $expected . '</div>');
145         $this->assertEquals($input, $parsed->xpath('//div')[0]);
146     }
148     public function test_write_hint_basic() {
149         $q = $this->make_test_question();
150         $q->contextid = \context_system::instance()->id;
151         $q->name = 'Short answer question';
152         $q->questiontext = 'Name an amphibian: __________';
153         $q->generalfeedback = 'Generalfeedback: frog or toad would have been OK.';
154         if (!isset($q->options)) {
155             $q->options = new stdClass();
156         }
157         $q->options->usecase = false;
158         $q->options->answers = array(
159             13 => new question_answer(13, 'frog', 1.0, 'Frog is a very good answer.', FORMAT_HTML),
160             14 => new question_answer(14, 'toad', 0.8, 'Toad is an OK good answer.', FORMAT_HTML),
161             15 => new question_answer(15, '*', 0.0, 'That is a bad answer.', FORMAT_HTML),
162         );
163         $q->qtype = 'shortanswer';
164         $q->hints = array(
165             new question_hint(0, 'This is the first hint.', FORMAT_MOODLE),
166         );
168         $exporter = new qformat_xml();
169         $xml = $exporter->writequestion($q);
171         $this->assertRegExp('|<hint format=\"moodle_auto_format\">\s*<text>\s*' .
172                 'This is the first hint\.\s*</text>\s*</hint>|', $xml);
173         $this->assertNotRegExp('|<shownumcorrect/>|', $xml);
174         $this->assertNotRegExp('|<clearwrong/>|', $xml);
175         $this->assertNotRegExp('|<options>|', $xml);
176     }
178     public function test_write_hint_with_parts() {
179         $q = $this->make_test_question();
180         $q->contextid = \context_system::instance()->id;
181         $q->name = 'Matching question';
182         $q->questiontext = 'Classify the animals.';
183         $q->generalfeedback = 'Frogs and toads are amphibians, the others are mammals.';
184         $q->qtype = 'match';
186         if (!isset($q->options)) {
187             $q->options = new stdClass();
188         }
189         $q->options->shuffleanswers = 1;
190         $q->options->correctfeedback = '';
191         $q->options->correctfeedbackformat = FORMAT_HTML;
192         $q->options->partiallycorrectfeedback = '';
193         $q->options->partiallycorrectfeedbackformat = FORMAT_HTML;
194         $q->options->incorrectfeedback = '';
195         $q->options->incorrectfeedbackformat = FORMAT_HTML;
197         $q->options->subquestions = array();
198         $q->hints = array(
199             new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, false, true),
200             new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, false),
201         );
203         $exporter = new qformat_xml();
204         $xml = $exporter->writequestion($q);
206         $this->assertRegExp(
207                 '|<hint format=\"html\">\s*<text>\s*This is the first hint\.\s*</text>|', $xml);
208         $this->assertRegExp(
209                 '|<hint format=\"html\">\s*<text>\s*This is the second hint\.\s*</text>|', $xml);
210         list($ignored, $hint1, $hint2) = explode('<hint', $xml);
211         $this->assertNotRegExp('|<shownumcorrect/>|', $hint1);
212         $this->assertRegExp('|<clearwrong/>|', $hint1);
213         $this->assertRegExp('|<shownumcorrect/>|', $hint2);
214         $this->assertNotRegExp('|<clearwrong/>|', $hint2);
215         $this->assertNotRegExp('|<options>|', $xml);
216     }
218     public function test_import_hints_no_parts() {
219         $xml = <<<END
220 <question>
221     <hint>
222         <text>This is the first hint</text>
223         <clearwrong/>
224     </hint>
225     <hint>
226         <text>This is the second hint</text>
227         <shownumcorrect/>
228     </hint>
229 </question>
230 END;
232         $questionxml = xmlize($xml);
233         $qo = new stdClass();
235         $importer = new qformat_xml();
236         $importer->import_hints($qo, $questionxml['question'], false, false, 'html');
238         $this->assertEquals(array(
239                 array('text' => 'This is the first hint',
240                         'format' => FORMAT_HTML),
241                 array('text' => 'This is the second hint',
242                         'format' => FORMAT_HTML),
243                 ), $qo->hint);
244         $this->assertFalse(isset($qo->hintclearwrong));
245         $this->assertFalse(isset($qo->hintshownumcorrect));
246     }
248     public function test_import_hints_with_parts() {
249         $xml = <<<END
250 <question>
251     <hint>
252         <text>This is the first hint</text>
253         <clearwrong/>
254     </hint>
255     <hint>
256         <text>This is the second hint</text>
257         <shownumcorrect/>
258     </hint>
259 </question>
260 END;
262         $questionxml = xmlize($xml);
263         $qo = new stdClass();
265         $importer = new qformat_xml();
266         $importer->import_hints($qo, $questionxml['question'], true, true, 'html');
268         $this->assertEquals(array(
269                 array('text' => 'This is the first hint',
270                         'format' => FORMAT_HTML),
271                 array('text' => 'This is the second hint',
272                         'format' => FORMAT_HTML),
273                 ), $qo->hint);
274         $this->assertEquals(array(1, 0), $qo->hintclearwrong);
275         $this->assertEquals(array(0, 1), $qo->hintshownumcorrect);
276     }
278     public function test_import_no_hints_no_error() {
279         $xml = <<<END
280 <question>
281 </question>
282 END;
284         $questionxml = xmlize($xml);
285         $qo = new stdClass();
287         $importer = new qformat_xml();
288         $importer->import_hints($qo, $questionxml['question'], 'html');
290         $this->assertFalse(isset($qo->hint));
291     }
293     public function test_import_description() {
294         $xml = '  <question type="description">
295     <name>
296       <text>A description</text>
297     </name>
298     <questiontext format="html">
299       <text>The question text.</text>
300     </questiontext>
301     <generalfeedback>
302       <text>Here is some general feedback.</text>
303     </generalfeedback>
304     <defaultgrade>0</defaultgrade>
305     <penalty>0</penalty>
306     <hidden>0</hidden>
307     <tags>
308       <tag><text>tagDescription</text></tag>
309       <tag><text>tagTest</text></tag>
310     </tags>
311   </question>';
312         $xmldata = xmlize($xml);
314         $importer = new qformat_xml();
315         $q = $importer->import_description($xmldata['question']);
317         $expectedq = new stdClass();
318         $expectedq->qtype = 'description';
319         $expectedq->name = 'A description';
320         $expectedq->questiontext = 'The question text.';
321         $expectedq->questiontextformat = FORMAT_HTML;
322         $expectedq->generalfeedback = 'Here is some general feedback.';
323         $expectedq->defaultmark = 0;
324         $expectedq->length = 0;
325         $expectedq->penalty = 0;
326         $expectedq->tags = array('tagDescription', 'tagTest');
328         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
329     }
331     public function test_export_description() {
332         $qdata = new stdClass();
333         $qdata->id = 123;
334         $qdata->contextid = \context_system::instance()->id;
335         $qdata->qtype = 'description';
336         $qdata->name = 'A description';
337         $qdata->questiontext = 'The question text.';
338         $qdata->questiontextformat = FORMAT_HTML;
339         $qdata->generalfeedback = 'Here is some general feedback.';
340         $qdata->generalfeedbackformat = FORMAT_HTML;
341         $qdata->defaultmark = 0;
342         $qdata->length = 0;
343         $qdata->penalty = 0;
344         $qdata->hidden = 0;
346         $exporter = new qformat_xml();
347         $xml = $exporter->writequestion($qdata);
349         $expectedxml = '<!-- question: 123  -->
350   <question type="description">
351     <name>
352       <text>A description</text>
353     </name>
354     <questiontext format="html">
355       <text>The question text.</text>
356     </questiontext>
357     <generalfeedback format="html">
358       <text>Here is some general feedback.</text>
359     </generalfeedback>
360     <defaultgrade>0</defaultgrade>
361     <penalty>0</penalty>
362     <hidden>0</hidden>
363   </question>
364 ';
366         $this->assert_same_xml($expectedxml, $xml);
367     }
369     public function test_import_essay_20() {
370         $xml = '  <question type="essay">
371     <name>
372       <text>An essay</text>
373     </name>
374     <questiontext format="moodle_auto_format">
375       <text>Write something.</text>
376     </questiontext>
377     <generalfeedback>
378       <text>I hope you wrote something interesting.</text>
379     </generalfeedback>
380     <defaultgrade>1</defaultgrade>
381     <penalty>0</penalty>
382     <hidden>0</hidden>
383     <tags>
384       <tag><text>tagEssay</text></tag>
385       <tag><text>tagEssay20</text></tag>
386       <tag><text>tagTest</text></tag>
387     </tags>
388   </question>';
389         $xmldata = xmlize($xml);
391         $importer = new qformat_xml();
392         $q = $importer->import_essay($xmldata['question']);
394         $expectedq = new stdClass();
395         $expectedq->qtype = 'essay';
396         $expectedq->name = 'An essay';
397         $expectedq->questiontext = 'Write something.';
398         $expectedq->questiontextformat = FORMAT_MOODLE;
399         $expectedq->generalfeedback = 'I hope you wrote something interesting.';
400         $expectedq->defaultmark = 1;
401         $expectedq->length = 1;
402         $expectedq->penalty = 0;
403         $expectedq->responseformat = 'editor';
404         $expectedq->responserequired = 1;
405         $expectedq->responsefieldlines = 15;
406         $expectedq->attachments = 0;
407         $expectedq->attachmentsrequired = 0;
408         $expectedq->graderinfo['text'] = '';
409         $expectedq->graderinfo['format'] = FORMAT_MOODLE;
410         $expectedq->responsetemplate['text'] = '';
411         $expectedq->responsetemplate['format'] = FORMAT_MOODLE;
412         $expectedq->tags = array('tagEssay', 'tagEssay20', 'tagTest');
414         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
415     }
417     public function test_import_essay_21() {
418         $xml = '  <question type="essay">
419     <name>
420       <text>An essay</text>
421     </name>
422     <questiontext format="moodle_auto_format">
423       <text>Write something.</text>
424     </questiontext>
425     <generalfeedback>
426       <text>I hope you wrote something interesting.</text>
427     </generalfeedback>
428     <defaultgrade>1</defaultgrade>
429     <penalty>0</penalty>
430     <hidden>0</hidden>
431     <responseformat>monospaced</responseformat>
432     <responserequired>0</responserequired>
433     <responsefieldlines>42</responsefieldlines>
434     <attachments>-1</attachments>
435     <attachmentsrequired>1</attachmentsrequired>
436     <graderinfo format="html">
437         <text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text>
438     </graderinfo>
439     <responsetemplate format="html">
440         <text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text>
441     </responsetemplate>
442     <tags>
443       <tag><text>tagEssay</text></tag>
444       <tag><text>tagEssay21</text></tag>
445       <tag><text>tagTest</text></tag>
446     </tags>
447   </question>';
448         $xmldata = xmlize($xml);
450         $importer = new qformat_xml();
451         $q = $importer->import_essay($xmldata['question']);
453         $expectedq = new stdClass();
454         $expectedq->qtype = 'essay';
455         $expectedq->name = 'An essay';
456         $expectedq->questiontext = 'Write something.';
457         $expectedq->questiontextformat = FORMAT_MOODLE;
458         $expectedq->generalfeedback = 'I hope you wrote something interesting.';
459         $expectedq->defaultmark = 1;
460         $expectedq->length = 1;
461         $expectedq->penalty = 0;
462         $expectedq->responseformat = 'monospaced';
463         $expectedq->responserequired = 0;
464         $expectedq->responsefieldlines = 42;
465         $expectedq->attachments = -1;
466         $expectedq->attachmentsrequired = 1;
467         $expectedq->graderinfo['text'] = '<p>Grade <b>generously</b>!</p>';
468         $expectedq->graderinfo['format'] = FORMAT_HTML;
469         $expectedq->responsetemplate['text'] = '<p>Here is something <b>really</b> interesting.</p>';
470         $expectedq->responsetemplate['format'] = FORMAT_HTML;
471         $expectedq->tags = array('tagEssay', 'tagEssay21', 'tagTest');
473         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
474     }
476     public function test_export_essay() {
477         $qdata = new stdClass();
478         $qdata->id = 123;
479         $qdata->contextid = \context_system::instance()->id;
480         $qdata->qtype = 'essay';
481         $qdata->name = 'An essay';
482         $qdata->questiontext = 'Write something.';
483         $qdata->questiontextformat = FORMAT_MOODLE;
484         $qdata->generalfeedback = 'I hope you wrote something interesting.';
485         $qdata->generalfeedbackformat = FORMAT_MOODLE;
486         $qdata->defaultmark = 1;
487         $qdata->length = 1;
488         $qdata->penalty = 0;
489         $qdata->hidden = 0;
490         $qdata->options = new stdClass();
491         $qdata->options->id = 456;
492         $qdata->options->questionid = 123;
493         $qdata->options->responseformat = 'monospaced';
494         $qdata->options->responserequired = 0;
495         $qdata->options->responsefieldlines = 42;
496         $qdata->options->attachments = -1;
497         $qdata->options->attachmentsrequired = 1;
498         $qdata->options->graderinfo = '<p>Grade <b>generously</b>!</p>';
499         $qdata->options->graderinfoformat = FORMAT_HTML;
500         $qdata->options->responsetemplate = '<p>Here is something <b>really</b> interesting.</p>';
501         $qdata->options->responsetemplateformat = FORMAT_HTML;
502         $exporter = new qformat_xml();
503         $xml = $exporter->writequestion($qdata);
505         $expectedxml = '<!-- question: 123  -->
506   <question type="essay">
507     <name>
508       <text>An essay</text>
509     </name>
510     <questiontext format="moodle_auto_format">
511       <text>Write something.</text>
512     </questiontext>
513     <generalfeedback format="moodle_auto_format">
514       <text>I hope you wrote something interesting.</text>
515     </generalfeedback>
516     <defaultgrade>1</defaultgrade>
517     <penalty>0</penalty>
518     <hidden>0</hidden>
519     <responseformat>monospaced</responseformat>
520     <responserequired>0</responserequired>
521     <responsefieldlines>42</responsefieldlines>
522     <attachments>-1</attachments>
523     <attachmentsrequired>1</attachmentsrequired>
524     <graderinfo format="html">
525       <text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text>
526     </graderinfo>
527     <responsetemplate format="html">
528       <text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text>
529     </responsetemplate>
530   </question>
531 ';
533         $this->assert_same_xml($expectedxml, $xml);
534     }
536     public function test_import_match_19() {
537         $xml = '  <question type="matching">
538     <name>
539       <text>Matching question</text>
540     </name>
541     <questiontext format="html">
542       <text>Match the upper and lower case letters.</text>
543     </questiontext>
544     <generalfeedback>
545       <text>The answer is A -> a, B -> b and C -> c.</text>
546     </generalfeedback>
547     <defaultgrade>1</defaultgrade>
548     <penalty>0.3333333</penalty>
549     <hidden>0</hidden>
550     <shuffleanswers>false</shuffleanswers>
551     <correctfeedback>
552       <text>Well done.</text>
553     </correctfeedback>
554     <partiallycorrectfeedback>
555       <text>Not entirely.</text>
556     </partiallycorrectfeedback>
557     <incorrectfeedback>
558       <text>Completely wrong!</text>
559     </incorrectfeedback>
560     <subquestion>
561       <text>A</text>
562       <answer>
563         <text>a</text>
564       </answer>
565     </subquestion>
566     <subquestion>
567       <text>B</text>
568       <answer>
569         <text>b</text>
570       </answer>
571     </subquestion>
572     <subquestion>
573       <text>C</text>
574       <answer>
575         <text>c</text>
576       </answer>
577     </subquestion>
578     <subquestion>
579       <text></text>
580       <answer>
581         <text>d</text>
582       </answer>
583     </subquestion>
584     <hint>
585       <text>Hint 1</text>
586       <shownumcorrect />
587     </hint>
588     <hint>
589       <text></text>
590       <shownumcorrect />
591       <clearwrong />
592     </hint>
593     <tags>
594       <tag><text>tagMatching</text></tag>
595       <tag><text>tagTest</text></tag>
596     </tags>
597   </question>';
598         $xmldata = xmlize($xml);
600         $importer = new qformat_xml();
601         $q = $importer->import_match($xmldata['question']);
603         $expectedq = new stdClass();
604         $expectedq->qtype = 'match';
605         $expectedq->name = 'Matching question';
606         $expectedq->questiontext = 'Match the upper and lower case letters.';
607         $expectedq->questiontextformat = FORMAT_HTML;
608         $expectedq->correctfeedback = array('text' => 'Well done.',
609                 'format' => FORMAT_HTML);
610         $expectedq->partiallycorrectfeedback = array('text' => 'Not entirely.',
611                 'format' => FORMAT_HTML);
612         $expectedq->shownumcorrect = false;
613         $expectedq->incorrectfeedback = array('text' => 'Completely wrong!',
614                 'format' => FORMAT_HTML);
615         $expectedq->generalfeedback = 'The answer is A -> a, B -> b and C -> c.';
616         $expectedq->generalfeedbackformat = FORMAT_HTML;
617         $expectedq->defaultmark = 1;
618         $expectedq->length = 1;
619         $expectedq->penalty = 0.3333333;
620         $expectedq->shuffleanswers = 0;
621         $expectedq->subquestions = array(
622             array('text' => 'A', 'format' => FORMAT_HTML),
623             array('text' => 'B', 'format' => FORMAT_HTML),
624             array('text' => 'C', 'format' => FORMAT_HTML),
625             array('text' => '', 'format' => FORMAT_HTML));
626         $expectedq->subanswers = array('a', 'b', 'c', 'd');
627         $expectedq->hint = array(
628             array('text' => 'Hint 1', 'format' => FORMAT_HTML),
629             array('text' => '', 'format' => FORMAT_HTML),
630         );
631         $expectedq->hintshownumcorrect = array(true, true);
632         $expectedq->hintclearwrong = array(false, true);
633         $expectedq->tags = array('tagMatching', 'tagTest');
635         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
636     }
638     public function test_export_match() {
639         $qdata = new stdClass();
640         $qdata->id = 123;
641         $qdata->contextid = \context_system::instance()->id;
642         $qdata->qtype = 'match';
643         $qdata->name = 'Matching question';
644         $qdata->questiontext = 'Match the upper and lower case letters.';
645         $qdata->questiontextformat = FORMAT_HTML;
646         $qdata->generalfeedback = 'The answer is A -> a, B -> b and C -> c.';
647         $qdata->generalfeedbackformat = FORMAT_HTML;
648         $qdata->defaultmark = 1;
649         $qdata->length = 1;
650         $qdata->penalty = 0.3333333;
651         $qdata->hidden = 0;
653         $qdata->options = new stdClass();
654         $qdata->options->shuffleanswers = 1;
655         $qdata->options->correctfeedback = 'Well done.';
656         $qdata->options->correctfeedbackformat = FORMAT_HTML;
657         $qdata->options->partiallycorrectfeedback = 'Not entirely.';
658         $qdata->options->partiallycorrectfeedbackformat = FORMAT_HTML;
659         $qdata->options->shownumcorrect = false;
660         $qdata->options->incorrectfeedback = 'Completely wrong!';
661         $qdata->options->incorrectfeedbackformat = FORMAT_HTML;
663         $subq1 = new stdClass();
664         $subq1->id = -4;
665         $subq1->questiontext = 'A';
666         $subq1->questiontextformat = FORMAT_HTML;
667         $subq1->answertext = 'a';
669         $subq2 = new stdClass();
670         $subq2->id = -3;
671         $subq2->questiontext = 'B';
672         $subq2->questiontextformat = FORMAT_HTML;
673         $subq2->answertext = 'b';
675         $subq3 = new stdClass();
676         $subq3->id = -2;
677         $subq3->questiontext = 'C';
678         $subq3->questiontextformat = FORMAT_HTML;
679         $subq3->answertext = 'c';
681         $subq4 = new stdClass();
682         $subq4->id = -1;
683         $subq4->questiontext = '';
684         $subq4->questiontextformat = FORMAT_HTML;
685         $subq4->answertext = 'd';
687         $qdata->options->subquestions = array(
688                 $subq1, $subq2, $subq3, $subq4);
690         $qdata->hints = array(
691             new question_hint_with_parts(0, 'Hint 1', FORMAT_HTML, true, false),
692             new question_hint_with_parts(0, '', FORMAT_HTML, true, true),
693         );
695         $exporter = new qformat_xml();
696         $xml = $exporter->writequestion($qdata);
698         $expectedxml = '<!-- question: 123  -->
699   <question type="matching">
700     <name>
701       <text>Matching question</text>
702     </name>
703     <questiontext format="html">
704       <text>Match the upper and lower case letters.</text>
705     </questiontext>
706     <generalfeedback format="html">
707       <text><![CDATA[The answer is A -> a, B -> b and C -> c.]]></text>
708     </generalfeedback>
709     <defaultgrade>1</defaultgrade>
710     <penalty>0.3333333</penalty>
711     <hidden>0</hidden>
712     <shuffleanswers>true</shuffleanswers>
713     <correctfeedback format="html">
714       <text>Well done.</text>
715     </correctfeedback>
716     <partiallycorrectfeedback format="html">
717       <text>Not entirely.</text>
718     </partiallycorrectfeedback>
719     <incorrectfeedback format="html">
720       <text>Completely wrong!</text>
721     </incorrectfeedback>
722     <subquestion format="html">
723       <text>A</text>
724       <answer>
725         <text>a</text>
726       </answer>
727     </subquestion>
728     <subquestion format="html">
729       <text>B</text>
730       <answer>
731         <text>b</text>
732       </answer>
733     </subquestion>
734     <subquestion format="html">
735       <text>C</text>
736       <answer>
737         <text>c</text>
738       </answer>
739     </subquestion>
740     <subquestion format="html">
741       <text></text>
742       <answer>
743         <text>d</text>
744       </answer>
745     </subquestion>
746     <hint format="html">
747       <text>Hint 1</text>
748       <shownumcorrect/>
749     </hint>
750     <hint format="html">
751       <text></text>
752       <shownumcorrect/>
753       <clearwrong/>
754     </hint>
755   </question>
756 ';
758         $this->assert_same_xml($expectedxml, $xml);
759     }
761     public function test_import_multichoice_19() {
762         $xml = '  <question type="multichoice">
763     <name>
764       <text>Multiple choice question</text>
765     </name>
766     <questiontext format="html">
767       <text>Which are the even numbers?</text>
768     </questiontext>
769     <generalfeedback>
770       <text>The even numbers are 2 and 4.</text>
771     </generalfeedback>
772     <defaultgrade>2</defaultgrade>
773     <penalty>0.3333333</penalty>
774     <hidden>0</hidden>
775     <single>false</single>
776     <shuffleanswers>false</shuffleanswers>
777     <answernumbering>abc</answernumbering>
778     <correctfeedback>
779       <text><![CDATA[<p>Your answer is correct.</p>]]></text>
780     </correctfeedback>
781     <partiallycorrectfeedback>
782       <text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
783     </partiallycorrectfeedback>
784     <incorrectfeedback>
785       <text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
786     </incorrectfeedback>
787     <shownumcorrect/>
788     <answer fraction="0">
789       <text>1</text>
790       <feedback>
791         <text></text>
792       </feedback>
793     </answer>
794     <answer fraction="100">
795       <text>2</text>
796       <feedback>
797         <text></text>
798       </feedback>
799     </answer>
800     <answer fraction="0">
801       <text>3</text>
802       <feedback>
803         <text></text>
804       </feedback>
805     </answer>
806     <answer fraction="100">
807       <text>4</text>
808       <feedback>
809         <text></text>
810       </feedback>
811     </answer>
812     <hint>
813       <text>Hint 1.</text>
814     </hint>
815     <hint>
816       <text>Hint 2.</text>
817     </hint>
818   </question>';
819         $xmldata = xmlize($xml);
821         $importer = new qformat_xml();
822         $q = $importer->import_multichoice($xmldata['question']);
824         $expectedq = new stdClass();
825         $expectedq->qtype = 'multichoice';
826         $expectedq->name = 'Multiple choice question';
827         $expectedq->questiontext = 'Which are the even numbers?';
828         $expectedq->questiontextformat = FORMAT_HTML;
829         $expectedq->correctfeedback = array(
830                 'text'   => '<p>Your answer is correct.</p>',
831                 'format' => FORMAT_HTML);
832         $expectedq->shownumcorrect = false;
833         $expectedq->partiallycorrectfeedback = array(
834                 'text'   => '<p>Your answer is partially correct.</p>',
835                 'format' => FORMAT_HTML);
836         $expectedq->shownumcorrect = true;
837         $expectedq->incorrectfeedback = array(
838                 'text'   => '<p>Your answer is incorrect.</p>',
839                 'format' => FORMAT_HTML);
840         $expectedq->generalfeedback = 'The even numbers are 2 and 4.';
841         $expectedq->defaultmark = 2;
842         $expectedq->length = 1;
843         $expectedq->penalty = 0.3333333;
844         $expectedq->shuffleanswers = 0;
845         $expectedq->single = false;
847         $expectedq->answer = array(
848             array('text' => '1', 'format' => FORMAT_HTML),
849             array('text' => '2', 'format' => FORMAT_HTML),
850             array('text' => '3', 'format' => FORMAT_HTML),
851             array('text' => '4', 'format' => FORMAT_HTML));
852         $expectedq->fraction = array(0, 1, 0, 1);
853         $expectedq->feedback = array(
854             array('text' => '', 'format' => FORMAT_HTML),
855             array('text' => '', 'format' => FORMAT_HTML),
856             array('text' => '', 'format' => FORMAT_HTML),
857             array('text' => '', 'format' => FORMAT_HTML));
859         $expectedq->hint = array(
860             array('text' => 'Hint 1.', 'format' => FORMAT_HTML),
861             array('text' => 'Hint 2.', 'format' => FORMAT_HTML),
862         );
863         $expectedq->hintshownumcorrect = array(false, false);
864         $expectedq->hintclearwrong = array(false, false);
866         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
867     }
869     public function test_export_multichoice() {
870         $qdata = new stdClass();
871         $qdata->id = 123;
872         $qdata->contextid = \context_system::instance()->id;
873         $qdata->qtype = 'multichoice';
874         $qdata->name = 'Multiple choice question';
875         $qdata->questiontext = 'Which are the even numbers?';
876         $qdata->questiontextformat = FORMAT_HTML;
877         $qdata->generalfeedback = 'The even numbers are 2 and 4.';
878         $qdata->generalfeedbackformat = FORMAT_HTML;
879         $qdata->defaultmark = 2;
880         $qdata->length = 1;
881         $qdata->penalty = 0.3333333;
882         $qdata->hidden = 0;
884         $qdata->options = new stdClass();
885         $qdata->options->single = 0;
886         $qdata->options->shuffleanswers = 0;
887         $qdata->options->answernumbering = 'abc';
888         $qdata->options->correctfeedback = '<p>Your answer is correct.</p>';
889         $qdata->options->correctfeedbackformat = FORMAT_HTML;
890         $qdata->options->partiallycorrectfeedback = '<p>Your answer is partially correct.</p>';
891         $qdata->options->partiallycorrectfeedbackformat = FORMAT_HTML;
892         $qdata->options->shownumcorrect = 1;
893         $qdata->options->incorrectfeedback = '<p>Your answer is incorrect.</p>';
894         $qdata->options->incorrectfeedbackformat = FORMAT_HTML;
896         $qdata->options->answers = array(
897             13 => new question_answer(13, '1', 0, '', FORMAT_HTML),
898             14 => new question_answer(14, '2', 1, '', FORMAT_HTML),
899             15 => new question_answer(15, '3', 0, '', FORMAT_HTML),
900             16 => new question_answer(16, '4', 1, '', FORMAT_HTML),
901         );
903         $qdata->hints = array(
904             new question_hint_with_parts(0, 'Hint 1.', FORMAT_HTML, false, false),
905             new question_hint_with_parts(0, 'Hint 2.', FORMAT_HTML, false, false),
906         );
908         $exporter = new qformat_xml();
909         $xml = $exporter->writequestion($qdata);
911         $expectedxml = '<!-- question: 123  -->
912   <question type="multichoice">
913     <name>
914       <text>Multiple choice question</text>
915     </name>
916     <questiontext format="html">
917       <text>Which are the even numbers?</text>
918     </questiontext>
919     <generalfeedback format="html">
920       <text>The even numbers are 2 and 4.</text>
921     </generalfeedback>
922     <defaultgrade>2</defaultgrade>
923     <penalty>0.3333333</penalty>
924     <hidden>0</hidden>
925     <single>false</single>
926     <shuffleanswers>false</shuffleanswers>
927     <answernumbering>abc</answernumbering>
928     <correctfeedback format="html">
929       <text><![CDATA[<p>Your answer is correct.</p>]]></text>
930     </correctfeedback>
931     <partiallycorrectfeedback format="html">
932       <text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
933     </partiallycorrectfeedback>
934     <incorrectfeedback format="html">
935       <text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
936     </incorrectfeedback>
937     <shownumcorrect/>
938     <answer fraction="0" format="plain_text">
939       <text>1</text>
940       <feedback format="html">
941         <text></text>
942       </feedback>
943     </answer>
944     <answer fraction="100" format="plain_text">
945       <text>2</text>
946       <feedback format="html">
947         <text></text>
948       </feedback>
949     </answer>
950     <answer fraction="0" format="plain_text">
951       <text>3</text>
952       <feedback format="html">
953         <text></text>
954       </feedback>
955     </answer>
956     <answer fraction="100" format="plain_text">
957       <text>4</text>
958       <feedback format="html">
959         <text></text>
960       </feedback>
961     </answer>
962     <hint format="html">
963       <text>Hint 1.</text>
964     </hint>
965     <hint format="html">
966       <text>Hint 2.</text>
967     </hint>
968   </question>
969 ';
971         $this->assert_same_xml($expectedxml, $xml);
972     }
974     public function test_import_numerical_19() {
975         $xml = '  <question type="numerical">
976     <name>
977       <text>Numerical question</text>
978     </name>
979     <questiontext format="html">
980       <text>What is the answer?</text>
981     </questiontext>
982     <generalfeedback>
983       <text>General feedback: Think Hitch-hikers guide to the Galaxy.</text>
984     </generalfeedback>
985     <defaultgrade>1</defaultgrade>
986     <penalty>0.1</penalty>
987     <hidden>0</hidden>
988     <answer fraction="100">
989       <text>42</text>
990       <feedback>
991         <text>Well done!</text>
992       </feedback>
993       <tolerance>0.001</tolerance>
994     </answer>
995     <answer fraction="0">
996       <text>13</text>
997       <feedback>
998         <text>What were you thinking?!</text>
999       </feedback>
1000       <tolerance>1</tolerance>
1001     </answer>
1002     <answer fraction="0">
1003       <text>*</text>
1004       <feedback>
1005         <text>Completely wrong.</text>
1006       </feedback>
1007       <tolerance></tolerance>
1008     </answer>
1009   </question>';
1010         $xmldata = xmlize($xml);
1012         $importer = new qformat_xml();
1013         $q = $importer->import_numerical($xmldata['question']);
1015         $expectedq = new stdClass();
1016         $expectedq->qtype = 'numerical';
1017         $expectedq->name = 'Numerical question';
1018         $expectedq->questiontext = 'What is the answer?';
1019         $expectedq->questiontextformat = FORMAT_HTML;
1020         $expectedq->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.';
1021         $expectedq->generalfeedbackformat = FORMAT_HTML;
1022         $expectedq->defaultmark = 1;
1023         $expectedq->length = 1;
1024         $expectedq->penalty = 0.1;
1026         $expectedq->answer = array('42', '13', '*');
1027         $expectedq->fraction = array(1, 0, 0);
1028         $expectedq->feedback = array(
1029             array('text' => 'Well done!',
1030                     'format' => FORMAT_HTML),
1031             array('text' => 'What were you thinking?!',
1032                     'format' => FORMAT_HTML),
1033             array('text' => 'Completely wrong.',
1034                     'format' => FORMAT_HTML));
1035         $expectedq->tolerance = array(0.001, 1, 0);
1037         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
1038     }
1040     public function test_export_numerical() {
1041         question_bank::load_question_definition_classes('numerical');
1043         $qdata = new stdClass();
1044         $qdata->id = 123;
1045         $qdata->contextid = \context_system::instance()->id;
1046         $qdata->qtype = 'numerical';
1047         $qdata->name = 'Numerical question';
1048         $qdata->questiontext = 'What is the answer?';
1049         $qdata->questiontextformat = FORMAT_HTML;
1050         $qdata->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.';
1051         $qdata->generalfeedbackformat = FORMAT_HTML;
1052         $qdata->defaultmark = 1;
1053         $qdata->length = 1;
1054         $qdata->penalty = 0.1;
1055         $qdata->hidden = 0;
1057         $qdata->options = new stdClass();
1058         $qdata->options->answers = array(
1059             13 => new qtype_numerical_answer(13, '42', 1, 'Well done!',
1060                     FORMAT_HTML, 0.001),
1061             14 => new qtype_numerical_answer(14, '13', 0, 'What were you thinking?!',
1062                     FORMAT_HTML, 1),
1063             15 => new qtype_numerical_answer(15, '*', 0, 'Completely wrong.',
1064                     FORMAT_HTML, ''),
1065         );
1067         $qdata->options->units = array();
1069         $exporter = new qformat_xml();
1070         $xml = $exporter->writequestion($qdata);
1072         $expectedxml = '<!-- question: 123  -->
1073   <question type="numerical">
1074     <name>
1075       <text>Numerical question</text>
1076     </name>
1077     <questiontext format="html">
1078       <text>What is the answer?</text>
1079     </questiontext>
1080     <generalfeedback format="html">
1081       <text>General feedback: Think Hitch-hikers guide to the Galaxy.</text>
1082     </generalfeedback>
1083     <defaultgrade>1</defaultgrade>
1084     <penalty>0.1</penalty>
1085     <hidden>0</hidden>
1086     <answer fraction="100" format="plain_text">
1087       <text>42</text>
1088       <feedback format="html">
1089         <text>Well done!</text>
1090       </feedback>
1091       <tolerance>0.001</tolerance>
1092     </answer>
1093     <answer fraction="0" format="plain_text">
1094       <text>13</text>
1095       <feedback format="html">
1096         <text>What were you thinking?!</text>
1097       </feedback>
1098       <tolerance>1</tolerance>
1099     </answer>
1100     <answer fraction="0" format="plain_text">
1101       <text>*</text>
1102       <feedback format="html">
1103         <text>Completely wrong.</text>
1104       </feedback>
1105       <tolerance>0</tolerance>
1106     </answer>
1107   </question>
1108 ';
1110         $this->assert_same_xml($expectedxml, $xml);
1111     }
1113     public function test_import_shortanswer_19() {
1114         $xml = '  <question type="shortanswer">
1115     <name>
1116       <text>Short answer question</text>
1117     </name>
1118     <questiontext format="html">
1119       <text>Fill in the gap in this sequence: Alpha, ________, Gamma.</text>
1120     </questiontext>
1121     <generalfeedback>
1122       <text>The answer is Beta.</text>
1123     </generalfeedback>
1124     <defaultgrade>1</defaultgrade>
1125     <penalty>0.3333333</penalty>
1126     <hidden>0</hidden>
1127     <usecase>0</usecase>
1128     <answer fraction="100" format="plain_text">
1129       <text>Beta</text>
1130       <feedback>
1131         <text>Well done!</text>
1132       </feedback>
1133     </answer>
1134     <answer fraction="0" format="plain_text">
1135       <text>*</text>
1136       <feedback>
1137         <text>Doh!</text>
1138       </feedback>
1139     </answer>
1140     <hint>
1141       <text>Hint 1</text>
1142     </hint>
1143     <hint>
1144       <text>Hint 2</text>
1145     </hint>
1146   </question>';
1147         $xmldata = xmlize($xml);
1149         $importer = new qformat_xml();
1150         $q = $importer->import_shortanswer($xmldata['question']);
1152         $expectedq = new stdClass();
1153         $expectedq->qtype = 'shortanswer';
1154         $expectedq->name = 'Short answer question';
1155         $expectedq->questiontext = 'Fill in the gap in this sequence: Alpha, ________, Gamma.';
1156         $expectedq->questiontextformat = FORMAT_HTML;
1157         $expectedq->generalfeedback = 'The answer is Beta.';
1158         $expectedq->usecase = false;
1159         $expectedq->defaultmark = 1;
1160         $expectedq->length = 1;
1161         $expectedq->penalty = 0.3333333;
1163         $expectedq->answer = array('Beta', '*');
1164         $expectedq->fraction = array(1, 0);
1165         $expectedq->feedback = array(
1166             array('text' => 'Well done!', 'format' => FORMAT_HTML),
1167             array('text' => 'Doh!', 'format' => FORMAT_HTML));
1169         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
1170     }
1172     public function test_export_shortanswer() {
1173         $qdata = new stdClass();
1174         $qdata->id = 123;
1175         $qdata->contextid = \context_system::instance()->id;
1176         $qdata->qtype = 'shortanswer';
1177         $qdata->name = 'Short answer question';
1178         $qdata->questiontext = 'Fill in the gap in this sequence: Alpha, ________, Gamma.';
1179         $qdata->questiontextformat = FORMAT_HTML;
1180         $qdata->generalfeedback = 'The answer is Beta.';
1181         $qdata->generalfeedbackformat = FORMAT_HTML;
1182         $qdata->defaultmark = 1;
1183         $qdata->length = 1;
1184         $qdata->penalty = 0.3333333;
1185         $qdata->hidden = 0;
1187         $qdata->options = new stdClass();
1188         $qdata->options->usecase = 0;
1190         $qdata->options->answers = array(
1191             13 => new question_answer(13, 'Beta', 1, 'Well done!', FORMAT_HTML),
1192             14 => new question_answer(14, '*', 0, 'Doh!', FORMAT_HTML),
1193         );
1195         $qdata->hints = array(
1196             new question_hint(0, 'Hint 1', FORMAT_HTML),
1197             new question_hint(0, 'Hint 2', FORMAT_HTML),
1198         );
1200         $exporter = new qformat_xml();
1201         $xml = $exporter->writequestion($qdata);
1203         $expectedxml = '<!-- question: 123  -->
1204   <question type="shortanswer">
1205     <name>
1206       <text>Short answer question</text>
1207     </name>
1208     <questiontext format="html">
1209       <text>Fill in the gap in this sequence: Alpha, ________, Gamma.</text>
1210     </questiontext>
1211     <generalfeedback format="html">
1212       <text>The answer is Beta.</text>
1213     </generalfeedback>
1214     <defaultgrade>1</defaultgrade>
1215     <penalty>0.3333333</penalty>
1216     <hidden>0</hidden>
1217     <usecase>0</usecase>
1218     <answer fraction="100" format="plain_text">
1219       <text>Beta</text>
1220       <feedback format="html">
1221         <text>Well done!</text>
1222       </feedback>
1223     </answer>
1224     <answer fraction="0" format="plain_text">
1225       <text>*</text>
1226       <feedback format="html">
1227         <text>Doh!</text>
1228       </feedback>
1229     </answer>
1230     <hint format="html">
1231       <text>Hint 1</text>
1232     </hint>
1233     <hint format="html">
1234       <text>Hint 2</text>
1235     </hint>
1236   </question>
1237 ';
1239         $this->assert_same_xml($expectedxml, $xml);
1240     }
1242     public function test_import_truefalse_19() {
1243         $xml = '  <question type="truefalse">
1244     <name>
1245       <text>True false question</text>
1246     </name>
1247     <questiontext format="html">
1248       <text>The answer is true.</text>
1249     </questiontext>
1250     <generalfeedback>
1251       <text>General feedback: You should have chosen true.</text>
1252     </generalfeedback>
1253     <defaultgrade>1</defaultgrade>
1254     <penalty>1</penalty>
1255     <hidden>0</hidden>
1256     <answer fraction="100">
1257       <text>true</text>
1258       <feedback>
1259         <text>Well done!</text>
1260       </feedback>
1261     </answer>
1262     <answer fraction="0">
1263       <text>false</text>
1264       <feedback>
1265         <text>Doh!</text>
1266       </feedback>
1267     </answer>
1268   </question>';
1269         $xmldata = xmlize($xml);
1271         $importer = new qformat_xml();
1272         $q = $importer->import_truefalse($xmldata['question']);
1274         $expectedq = new stdClass();
1275         $expectedq->qtype = 'truefalse';
1276         $expectedq->name = 'True false question';
1277         $expectedq->questiontext = 'The answer is true.';
1278         $expectedq->questiontextformat = FORMAT_HTML;
1279         $expectedq->generalfeedback = 'General feedback: You should have chosen true.';
1280         $expectedq->defaultmark = 1;
1281         $expectedq->length = 1;
1282         $expectedq->penalty = 1;
1284         $expectedq->feedbacktrue = array('text' => 'Well done!',
1285                 'format' => FORMAT_HTML);
1286         $expectedq->feedbackfalse = array('text' => 'Doh!',
1287                 'format' => FORMAT_HTML);
1288         $expectedq->correctanswer = true;
1290         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
1291     }
1293     public function test_export_truefalse() {
1294         $qdata = new stdClass();
1295         $qdata->id = 12;
1296         $qdata->contextid = \context_system::instance()->id;
1297         $qdata->qtype = 'truefalse';
1298         $qdata->name = 'True false question';
1299         $qdata->questiontext = 'The answer is true.';
1300         $qdata->questiontextformat = FORMAT_HTML;
1301         $qdata->generalfeedback = 'General feedback: You should have chosen true.';
1302         $qdata->generalfeedbackformat = FORMAT_HTML;
1303         $qdata->defaultmark = 1;
1304         $qdata->length = 1;
1305         $qdata->penalty = 1;
1306         $qdata->hidden = 0;
1308         $qdata->options = new stdClass();
1309         $qdata->options->answers = array(
1310             1 => new question_answer(1, 'True', 1, 'Well done!', FORMAT_HTML),
1311             2 => new question_answer(2, 'False', 0, 'Doh!', FORMAT_HTML),
1312         );
1313         $qdata->options->trueanswer = 1;
1314         $qdata->options->falseanswer = 2;
1316         $exporter = new qformat_xml();
1317         $xml = $exporter->writequestion($qdata);
1319         $expectedxml = '<!-- question: 12  -->
1320   <question type="truefalse">
1321     <name>
1322       <text>True false question</text>
1323     </name>
1324     <questiontext format="html">
1325       <text>The answer is true.</text>
1326     </questiontext>
1327     <generalfeedback format="html">
1328       <text>General feedback: You should have chosen true.</text>
1329     </generalfeedback>
1330     <defaultgrade>1</defaultgrade>
1331     <penalty>1</penalty>
1332     <hidden>0</hidden>
1333     <answer fraction="100" format="plain_text">
1334       <text>true</text>
1335       <feedback format="html">
1336         <text>Well done!</text>
1337       </feedback>
1338     </answer>
1339     <answer fraction="0" format="plain_text">
1340       <text>false</text>
1341       <feedback format="html">
1342         <text>Doh!</text>
1343       </feedback>
1344     </answer>
1345   </question>
1346 ';
1348         $this->assert_same_xml($expectedxml, $xml);
1349     }
1351     public function test_import_multianswer() {
1352         $xml = '  <question type="cloze">
1353     <name>
1354       <text>Simple multianswer</text>
1355     </name>
1356     <questiontext format="html">
1357       <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>
1358     </questiontext>
1359     <generalfeedback format="html">
1360       <text><![CDATA[General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea".]]></text>
1361     </generalfeedback>
1362     <penalty>0.5</penalty>
1363     <hidden>0</hidden>
1364     <hint format="html">
1365       <text>Hint 1</text>
1366     </hint>
1367     <hint format="html">
1368       <text>Hint 2</text>
1369     </hint>
1370     <tags>
1371       <tag><text>tagCloze</text></tag>
1372       <tag><text>tagTest</text></tag>
1373     </tags>
1374   </question>
1375 ';
1376         $xmldata = xmlize($xml);
1378         $importer = new qformat_xml();
1379         $q = $importer->import_multianswer($xmldata['question']);
1381         // Annoyingly, import works in a weird way (it duplicates code, rather
1382         // than just calling save_question) so we cannot use
1383         // test_question_maker::get_question_form_data('multianswer', 'twosubq').
1384         $expectedqa = new stdClass();
1385         $expectedqa->name = 'Simple multianswer';
1386         $expectedqa->qtype = 'multianswer';
1387         $expectedqa->questiontext = 'Complete this opening line of verse: "The {#1} and the {#2} went to sea".';
1388         $expectedqa->generalfeedback =
1389                 'General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea".';
1390         $expectedqa->defaultmark = 2;
1391         $expectedqa->penalty = 0.5;
1393         $expectedqa->hint = array(
1394             array('text' => 'Hint 1', 'format' => FORMAT_HTML),
1395             array('text' => 'Hint 2', 'format' => FORMAT_HTML),
1396         );
1398         $sa = new stdClass();
1400         $sa->questiontext = array('text' => '{1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer}',
1401                 'format' => FORMAT_HTML, 'itemid' => null);
1402         $sa->generalfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
1403         $sa->defaultmark = 1.0;
1404         $sa->qtype = 'shortanswer';
1405         $sa->usecase = 0;
1407         $sa->answer = array('Dog', 'Owl', '*');
1408         $sa->fraction = array(0, 1, 0);
1409         $sa->feedback = array(
1410             array('text' => 'Wrong, silly!', 'format' => FORMAT_HTML, 'itemid' => null),
1411             array('text' => 'Well done!',    'format' => FORMAT_HTML, 'itemid' => null),
1412             array('text' => 'Wrong answer',  'format' => FORMAT_HTML, 'itemid' => null),
1413         );
1415         $mc = new stdClass();
1417         $mc->generalfeedback = '';
1418         $mc->questiontext = array('text' => '{1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~' .
1419                 'Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!}',
1420                 'format' => FORMAT_HTML, 'itemid' => null);
1421         $mc->generalfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
1422         $mc->defaultmark = 1.0;
1423         $mc->qtype = 'multichoice';
1425         $mc->layout = 0;
1426         $mc->single = 1;
1427         $mc->shuffleanswers = 0;
1428         $mc->correctfeedback =          array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
1429         $mc->partiallycorrectfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
1430         $mc->incorrectfeedback =        array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
1431         $mc->answernumbering = 0;
1433         $mc->answer = array(
1434             array('text' => 'Bow-wow',     'format' => FORMAT_HTML, 'itemid' => null),
1435             array('text' => 'Wiggly worm', 'format' => FORMAT_HTML, 'itemid' => null),
1436             array('text' => 'Pussy-cat',   'format' => FORMAT_HTML, 'itemid' => null),
1437         );
1438         $mc->fraction = array(0, 0, 1);
1439         $mc->feedback = array(
1440             array('text' => 'You seem to have a dog obsessions!', 'format' => FORMAT_HTML, 'itemid' => null),
1441             array('text' => 'Now you are just being ridiculous!', 'format' => FORMAT_HTML, 'itemid' => null),
1442             array('text' => 'Well done!',                         'format' => FORMAT_HTML, 'itemid' => null),
1443         );
1445         $expectedqa->options = new stdClass();
1446         $expectedqa->options->questions = array(
1447             1 => $sa,
1448             2 => $mc,
1449         );
1450         $expectedqa->tags = array('tagCloze', 'tagTest');
1452         $this->assertEquals($expectedqa->hint, $q->hint);
1453         $this->assertEquals($expectedqa->options->questions[1], $q->options->questions[1]);
1454         $this->assertEquals($expectedqa->options->questions[2], $q->options->questions[2]);
1455         $this->assert(new question_check_specified_fields_expectation($expectedqa), $q);
1456     }
1458     public function test_export_multianswer() {
1459         $qdata = test_question_maker::get_question_data('multianswer', 'twosubq');
1460         $qdata->contextid = \context_system::instance()->id;
1461         $exporter = new qformat_xml();
1462         $xml = $exporter->writequestion($qdata);
1464         $expectedxml = '<!-- question: 0  -->
1465   <question type="cloze">
1466     <name>
1467       <text>Simple multianswer</text>
1468     </name>
1469     <questiontext format="html">
1470       <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>
1471     </questiontext>
1472     <generalfeedback format="html">
1473       <text><![CDATA[General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea]]></text>
1474     </generalfeedback>
1475     <penalty>0.3333333</penalty>
1476     <hidden>0</hidden>
1477     <hint format="html">
1478       <text>Hint 1</text>
1479     </hint>
1480     <hint format="html">
1481       <text>Hint 2</text>
1482     </hint>
1483   </question>
1484 ';
1486         $this->assert_same_xml($expectedxml, $xml);
1487     }
1489     public function test_export_multianswer_withdollars() {
1490         $qdata = test_question_maker::get_question_data('multianswer', 'dollarsigns');
1491         $qdata->contextid = \context_system::instance()->id;
1492         $exporter = new qformat_xml();
1493         $xml = $exporter->writequestion($qdata);
1495         $expectedxml = '<!-- question: 0  -->
1496   <question type="cloze">
1497     <name>
1498       <text>Multianswer with $s</text>
1499     </name>
1500     <questiontext format="html">
1501       <text>Which is the right order? {1:MULTICHOICE:=y,y,$3~$3,y,y}</text>
1502     </questiontext>
1503     <generalfeedback format="html">
1504       <text></text>
1505     </generalfeedback>
1506     <penalty>0.3333333</penalty>
1507     <hidden>0</hidden>
1508   </question>
1509 ';
1511         $this->assert_same_xml($expectedxml, $xml);
1512     }
1514     public function test_import_files_as_draft() {
1515         $this->resetAfterTest();
1516         $this->setAdminUser();
1518         $xml = <<<END
1519 <questiontext format="html">
1520     <text><![CDATA[<p><a href="@@PLUGINFILE@@/moodle.txt">This text file</a> contains the word 'Moodle'.</p>]]></text>
1521     <file name="moodle.txt" encoding="base64">TW9vZGxl</file>
1522 </questiontext>
1523 END;
1525         $textxml = xmlize($xml);
1526         $qo = new stdClass();
1528         $importer = new qformat_xml();
1529         $draftitemid = $importer->import_files_as_draft($textxml['questiontext']['#']['file']);
1530         $files = file_get_drafarea_files($draftitemid);
1532         $this->assertEquals(1, count($files->list));
1534         $file = $files->list[0];
1535         $this->assertEquals('moodle.txt', $file->filename);
1536         $this->assertEquals('/',          $file->filepath);
1537         $this->assertEquals(6,            $file->size);
1538     }
1540     public function test_import_truefalse_wih_files() {
1541         $this->resetAfterTest();
1542         $this->setAdminUser();
1544         $xml = '<question type="truefalse">
1545     <name>
1546       <text>truefalse</text>
1547     </name>
1548     <questiontext format="html">
1549       <text><![CDATA[<p><a href="@@PLUGINFILE@@/myfolder/moodle.txt">This text file</a> contains the word Moodle.</p>]]></text>
1550 <file name="moodle.txt" path="/myfolder/" encoding="base64">TW9vZGxl</file>
1551     </questiontext>
1552     <generalfeedback format="html">
1553       <text><![CDATA[<p>For further information, see the documentation about Moodle.</p>]]></text>
1554 </generalfeedback>
1555     <defaultgrade>1.0000000</defaultgrade>
1556     <penalty>1.0000000</penalty>
1557     <hidden>0</hidden>
1558     <answer fraction="100" format="moodle_auto_format">
1559       <text>true</text>
1560       <feedback format="html">
1561         <text></text>
1562       </feedback>
1563     </answer>
1564     <answer fraction="0" format="moodle_auto_format">
1565       <text>false</text>
1566       <feedback format="html">
1567         <text></text>
1568       </feedback>
1569     </answer>
1570   </question>';
1571         $xmldata = xmlize($xml);
1573         $importer = new qformat_xml();
1574         $q = $importer->import_truefalse($xmldata['question']);
1576         $draftitemid = $q->questiontextitemid;
1577         $files = file_get_drafarea_files($draftitemid, '/myfolder/');
1579         $this->assertEquals(1, count($files->list));
1581         $file = $files->list[0];
1582         $this->assertEquals('moodle.txt', $file->filename);
1583         $this->assertEquals('/myfolder/', $file->filepath);
1584         $this->assertEquals(6,            $file->size);
1585     }