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 GIFT format.
22 * @copyright 2010 The Open University
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') || die();
30 require_once($CFG->libdir . '/questionlib.php');
31 require_once($CFG->dirroot . '/question/format.php');
32 require_once($CFG->dirroot . '/question/format/gift/format.php');
33 require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
37 * Unit tests for the GIFT import/export format.
39 * @copyright 2010 The Open University
40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42 class qformat_gift_test extends question_testcase {
43 public function assert_same_gift($expectedtext, $text) {
44 $this->assertEquals(str_replace("\r\n", "\n", $expectedtext),
45 str_replace("\r\n", "\n", $text));
48 public function test_import_essay() {
51 ::Q8:: How are you? {}';
52 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
54 $importer = new qformat_gift();
55 $q = $importer->readquestion($lines);
57 $expectedq = (object) array(
59 'questiontext' => 'How are you?',
60 'questiontextformat' => FORMAT_MOODLE,
61 'generalfeedback' => '',
62 'generalfeedbackformat' => FORMAT_MOODLE,
65 'penalty' => 0.3333333,
67 'responseformat' => 'editor',
68 'responsefieldlines' => 15,
70 'graderinfo' => array(
72 'format' => FORMAT_HTML,
76 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
79 public function test_export_essay() {
80 $qdata = (object) array(
83 'questiontext' => 'How are you?',
84 'questiontextformat' => FORMAT_MOODLE,
85 'generalfeedback' => '',
86 'generalfeedbackformat' => FORMAT_MOODLE,
88 'penalty' => 0.3333333,
91 'options' => (object) array(
92 'responseformat' => 'editor',
93 'responsefieldlines' => 15,
96 'graderinfoformat' => FORMAT_HTML,
100 $exporter = new qformat_gift();
101 $gift = $exporter->writequestion($qdata);
103 $expectedgift = "// question: 666 name: Q8
108 $this->assert_same_gift($expectedgift, $gift);
111 public function test_import_match() {
113 // question: 2 name: Moodle activities
114 ::Moodle activities::[html]Match the <b>activity</b> to the description.{
115 =[html]An activity supporting asynchronous discussions. -> Forum
116 =[moodle]A teacher asks a question and specifies a choice of multiple responses. -> Choice
117 =[plain]A bank of record entries which participants can add to. -> Database
118 =[markdown]A collection of web pages that anyone can add to or edit. -> Wiki
121 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
123 $importer = new qformat_gift();
124 $q = $importer->readquestion($lines);
126 $expectedq = (object) array(
127 'name' => 'Moodle activities',
128 'questiontext' => 'Match the <b>activity</b> to the description.',
129 'questiontextformat' => FORMAT_HTML,
130 'generalfeedback' => '',
131 'generalfeedbackformat' => FORMAT_HTML,
134 'penalty' => 0.3333333,
136 'shuffleanswers' => '1',
137 'correctfeedback' => array(
139 'format' => FORMAT_HTML,
142 'partiallycorrectfeedback' => array(
144 'format' => FORMAT_HTML,
147 'incorrectfeedback' => array(
149 'format' => FORMAT_HTML,
152 'subquestions' => array(
154 'text' => 'An activity supporting asynchronous discussions.',
155 'format' => FORMAT_HTML,
159 'text' => 'A teacher asks a question and specifies a choice of multiple responses.',
160 'format' => FORMAT_MOODLE,
164 'text' => 'A bank of record entries which participants can add to.',
165 'format' => FORMAT_PLAIN,
169 'text' => 'A collection of web pages that anyone can add to or edit.',
170 'format' => FORMAT_MARKDOWN,
175 'format' => FORMAT_HTML,
179 'subanswers' => array(
188 // Repeated test for better failure messages.
189 $this->assertEquals($expectedq->subquestions, $q->subquestions);
190 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
193 public function test_export_match() {
194 $qdata = (object) array(
196 'name' => 'Moodle activities',
197 'questiontext' => 'Match the <b>activity</b> to the description.',
198 'questiontextformat' => FORMAT_HTML,
199 'generalfeedback' => '',
200 'generalfeedbackformat' => FORMAT_HTML,
202 'penalty' => 0.3333333,
205 'options' => (object) array(
208 'shuffleanswers' => 1,
209 'subquestions' => array(
210 42 => (object) array(
214 'questiontext' => '<div class="frog">An activity supporting asynchronous discussions.</div>',
215 'questiontextformat' => FORMAT_HTML,
216 'answertext' => 'Forum',
218 43 => (object) array(
222 'questiontext' => 'A teacher asks a question and specifies a choice of multiple responses.',
223 'questiontextformat' => FORMAT_MOODLE,
224 'answertext' => 'Choice',
226 44 => (object) array(
230 'questiontext' => 'A bank of record entries which participants can add to.',
231 'questiontextformat' => FORMAT_PLAIN,
232 'answertext' => 'Database',
234 45 => (object) array(
238 'questiontext' => 'A collection of web pages that anyone can add to or edit.',
239 'questiontextformat' => FORMAT_MARKDOWN,
240 'answertext' => 'Wiki',
242 46 => (object) array(
246 'questiontext' => '',
247 'questiontextformat' => FORMAT_MARKDOWN,
248 'answertext' => 'Chat',
254 $exporter = new qformat_gift();
255 $gift = $exporter->writequestion($qdata);
257 $expectedgift = "// question: 666 name: Moodle activities
258 ::Moodle activities::[html]Match the <b>activity</b> to the description.{
259 \t=<div class\\=\"frog\">An activity supporting asynchronous discussions.</div> -> Forum
260 \t=[moodle]A teacher asks a question and specifies a choice of multiple responses. -> Choice
261 \t=[plain]A bank of record entries which participants can add to. -> Database
262 \t=[markdown]A collection of web pages that anyone can add to or edit. -> Wiki
268 $this->assert_same_gift($expectedgift, $gift);
271 public function test_import_multichoice() {
273 // multiple choice with specified feedback for right and wrong answers
274 ::Q2:: What's between orange and green in the spectrum?
276 =yellow # right; good!
277 ~red # [html]wrong, it's yellow
278 ~[plain]blue # wrong, it's yellow
280 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
282 $importer = new qformat_gift();
283 $q = $importer->readquestion($lines);
285 $expectedq = (object) array(
287 'questiontext' => "What's between orange and green in the spectrum?",
288 'questiontextformat' => FORMAT_MOODLE,
289 'generalfeedback' => '',
290 'generalfeedbackformat' => FORMAT_MOODLE,
291 'qtype' => 'multichoice',
293 'penalty' => 0.3333333,
296 'shuffleanswers' => '1',
297 'answernumbering' => 'abc',
298 'correctfeedback' => array(
300 'format' => FORMAT_MOODLE,
303 'partiallycorrectfeedback' => array(
305 'format' => FORMAT_MOODLE,
308 'incorrectfeedback' => array(
310 'format' => FORMAT_MOODLE,
316 'format' => FORMAT_MOODLE,
321 'format' => FORMAT_MOODLE,
326 'format' => FORMAT_PLAIN,
330 'fraction' => array(1, 0, 0),
333 'text' => 'right; good!',
334 'format' => FORMAT_MOODLE,
338 'text' => "wrong, it's yellow",
339 'format' => FORMAT_HTML,
343 'text' => "wrong, it's yellow",
344 'format' => FORMAT_MOODLE,
350 // Repeated test for better failure messages.
351 $this->assertEquals($expectedq->answer, $q->answer);
352 $this->assertEquals($expectedq->feedback, $q->feedback);
353 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
356 public function test_import_multichoice_multi() {
358 // multiple choice, multiple response with specified feedback for right and wrong answers
359 ::colours:: What's between orange and green in the spectrum?
361 ~%50%yellow # right; good!
362 ~%-100%red # [html]wrong
363 ~%50%off-beige # right; good!
364 ~%-100%[plain]blue # wrong
366 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
368 $importer = new qformat_gift();
369 $q = $importer->readquestion($lines);
371 $expectedq = (object) array(
373 'questiontext' => "What's between orange and green in the spectrum?",
374 'questiontextformat' => FORMAT_MOODLE,
375 'generalfeedback' => '',
376 'generalfeedbackformat' => FORMAT_MOODLE,
377 'qtype' => 'multichoice',
379 'penalty' => 0.3333333,
382 'shuffleanswers' => '1',
383 'answernumbering' => 'abc',
384 'correctfeedback' => array(
386 'format' => FORMAT_MOODLE,
389 'partiallycorrectfeedback' => array(
391 'format' => FORMAT_MOODLE,
394 'incorrectfeedback' => array(
396 'format' => FORMAT_MOODLE,
402 'format' => FORMAT_MOODLE,
407 'format' => FORMAT_MOODLE,
411 'text' => 'off-beige',
412 'format' => FORMAT_MOODLE,
417 'format' => FORMAT_PLAIN,
421 'fraction' => array(0.5, -1, 0.5, -1),
424 'text' => 'right; good!',
425 'format' => FORMAT_MOODLE,
430 'format' => FORMAT_HTML,
434 'text' => "right; good!",
435 'format' => FORMAT_MOODLE,
440 'format' => FORMAT_MOODLE,
446 // Repeated test for better failure messages.
447 $this->assertEquals($expectedq->answer, $q->answer);
448 $this->assertEquals($expectedq->feedback, $q->feedback);
449 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
452 public function test_export_multichoice() {
453 $qdata = (object) array(
456 'questiontext' => "What's between orange and green in the spectrum?",
457 'questiontextformat' => FORMAT_MOODLE,
458 'generalfeedback' => '',
459 'generalfeedbackformat' => FORMAT_MOODLE,
461 'penalty' => 0.3333333,
463 'qtype' => 'multichoice',
464 'options' => (object) array(
466 'shuffleanswers' => '1',
467 'answernumbering' => 'abc',
468 'correctfeedback' => '',
469 'correctfeedbackformat' => FORMAT_MOODLE,
470 'partiallycorrectfeedback' => '',
471 'partiallycorrectfeedbackformat' => FORMAT_MOODLE,
472 'incorrectfeedback' => '',
473 'incorrectfeedbackformat' => FORMAT_MOODLE,
475 123 => (object) array(
477 'answer' => 'yellow',
478 'answerformat' => FORMAT_MOODLE,
480 'feedback' => 'right; good!',
481 'feedbackformat' => FORMAT_MOODLE,
483 124 => (object) array(
486 'answerformat' => FORMAT_MOODLE,
488 'feedback' => "wrong, it's yellow",
489 'feedbackformat' => FORMAT_HTML,
491 125 => (object) array(
494 'answerformat' => FORMAT_PLAIN,
496 'feedback' => "wrong, it's yellow",
497 'feedbackformat' => FORMAT_MOODLE,
503 $exporter = new qformat_gift();
504 $gift = $exporter->writequestion($qdata);
506 $expectedgift = "// question: 666 name: Q8
507 ::Q8::What's between orange and green in the spectrum?{
508 \t=yellow#right; good!
509 \t~red#[html]wrong, it's yellow
510 \t~[plain]blue#wrong, it's yellow
515 $this->assert_same_gift($expectedgift, $gift);
518 public function test_import_numerical() {
520 // math range question
521 ::Q5:: What is a number from 1 to 5? {#3:2~#Completely wrong}";
522 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
524 $importer = new qformat_gift();
525 $q = $importer->readquestion($lines);
527 $expectedq = (object) array(
529 'questiontext' => "What is a number from 1 to 5?",
530 'questiontextformat' => FORMAT_MOODLE,
531 'generalfeedback' => '',
532 'generalfeedbackformat' => FORMAT_MOODLE,
533 'qtype' => 'numerical',
535 'penalty' => 0.3333333,
541 'fraction' => array(1, 0),
545 'format' => FORMAT_MOODLE,
549 'text' => "Completely wrong",
550 'format' => FORMAT_MOODLE,
554 'tolerance' => array(2, 0),
557 // Repeated test for better failure messages.
558 $this->assertEquals($expectedq->answer, $q->answer);
559 $this->assertEquals($expectedq->fraction, $q->fraction);
560 $this->assertEquals($expectedq->feedback, $q->feedback);
561 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
564 public function test_export_numerical() {
565 $qdata = (object) array(
568 'questiontext' => "What is a number from 1 to 5?",
569 'questiontextformat' => FORMAT_MOODLE,
570 'generalfeedback' => '',
571 'generalfeedbackformat' => FORMAT_MOODLE,
575 'qtype' => 'numerical',
576 'options' => (object) array(
582 'unitgradingtype' => 0,
592 'feedbackformat' => FORMAT_MOODLE,
600 'feedback' => "Completely wrong",
601 'feedbackformat' => FORMAT_MOODLE,
607 $exporter = new qformat_gift();
608 $gift = $exporter->writequestion($qdata);
610 $expectedgift = "// question: 666 name: Q5
611 ::Q5::What is a number from 1 to 5?{#
618 $this->assert_same_gift($expectedgift, $gift);
621 public function test_import_shortanswer() {
623 // question: 666 name: Shortanswer
624 ::Shortanswer::Which is the best animal?{
626 =%50%Cat#What is it with Moodlers and cats?
627 =%0%*#Completely wrong
629 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
631 $importer = new qformat_gift();
632 $q = $importer->readquestion($lines);
634 $expectedq = (object) array(
635 'name' => 'Shortanswer',
636 'questiontext' => "Which is the best animal?",
637 'questiontextformat' => FORMAT_MOODLE,
638 'generalfeedback' => '',
639 'generalfeedbackformat' => FORMAT_MOODLE,
640 'qtype' => 'shortanswer',
642 'penalty' => 0.3333333,
649 'fraction' => array(1, 0.5, 0),
653 'format' => FORMAT_MOODLE,
657 'text' => "What is it with Moodlers and cats?",
658 'format' => FORMAT_MOODLE,
662 'text' => "Completely wrong",
663 'format' => FORMAT_MOODLE,
669 // Repeated test for better failure messages.
670 $this->assertEquals($expectedq->answer, $q->answer);
671 $this->assertEquals($expectedq->fraction, $q->fraction);
672 $this->assertEquals($expectedq->feedback, $q->feedback);
673 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
676 public function test_export_shortanswer() {
677 $qdata = (object) array(
679 'name' => 'Shortanswer',
680 'questiontext' => "Which is the best animal?",
681 'questiontextformat' => FORMAT_MOODLE,
682 'generalfeedback' => '',
683 'generalfeedbackformat' => FORMAT_MOODLE,
687 'qtype' => 'shortanswer',
688 'options' => (object) array(
698 'feedback' => 'Good!',
699 'feedbackformat' => FORMAT_MOODLE,
706 'feedback' => "What is it with Moodlers and cats?",
707 'feedbackformat' => FORMAT_MOODLE,
714 'feedback' => "Completely wrong",
715 'feedbackformat' => FORMAT_MOODLE,
721 $exporter = new qformat_gift();
722 $gift = $exporter->writequestion($qdata);
724 $expectedgift = "// question: 666 name: Shortanswer
725 ::Shortanswer::Which is the best animal?{
727 \t=%50%Cat#What is it with Moodlers and cats?
728 \t=%0%*#Completely wrong
733 $this->assert_same_gift($expectedgift, $gift);
736 public function test_import_truefalse() {
739 ::Q1:: 42 is the Absolute Answer to everything.{
740 FALSE#42 is the Ultimate Answer.#You gave the right answer.}";
741 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
743 $importer = new qformat_gift();
744 $q = $importer->readquestion($lines);
746 $expectedq = (object) array(
748 'questiontext' => "42 is the Absolute Answer to everything.",
749 'questiontextformat' => FORMAT_MOODLE,
750 'generalfeedback' => '',
751 'generalfeedbackformat' => FORMAT_MOODLE,
752 'qtype' => 'truefalse',
756 'correctanswer' => 0,
757 'feedbacktrue' => array(
758 'text' => '42 is the Ultimate Answer.',
759 'format' => FORMAT_MOODLE,
762 'feedbackfalse' => array(
763 'text' => 'You gave the right answer.',
764 'format' => FORMAT_MOODLE,
769 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
772 public function test_import_truefalse_true_answer1() {
773 $gift = "// name 0-11
774 ::2-08 TSL::TSL is blablabla.{T}";
775 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
777 $importer = new qformat_gift();
778 $q = $importer->readquestion($lines);
780 $expectedq = (object) array(
781 'name' => '2-08 TSL',
782 'questiontext' => "TSL is blablabla.",
783 'questiontextformat' => FORMAT_MOODLE,
784 'generalfeedback' => '',
785 'generalfeedbackformat' => FORMAT_MOODLE,
786 'qtype' => 'truefalse',
790 'correctanswer' => 1,
791 'feedbacktrue' => array(
793 'format' => FORMAT_MOODLE,
796 'feedbackfalse' => array(
798 'format' => FORMAT_MOODLE,
803 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
806 public function test_import_truefalse_true_answer2() {
807 $gift = "// name 0-11
808 ::2-08 TSL::TSL is blablabla.{TRUE}";
809 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
811 $importer = new qformat_gift();
812 $q = $importer->readquestion($lines);
814 $expectedq = (object) array(
815 'name' => '2-08 TSL',
816 'questiontext' => "TSL is blablabla.",
817 'questiontextformat' => FORMAT_MOODLE,
818 'generalfeedback' => '',
819 'generalfeedbackformat' => FORMAT_MOODLE,
820 'qtype' => 'truefalse',
824 'correctanswer' => 1,
825 'feedbacktrue' => array(
827 'format' => FORMAT_MOODLE,
830 'feedbackfalse' => array(
832 'format' => FORMAT_MOODLE,
837 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
840 public function test_export_truefalse() {
841 $qdata = (object) array(
844 'questiontext' => "42 is the Absolute Answer to everything.",
845 'questiontextformat' => FORMAT_MOODLE,
846 'generalfeedback' => '',
847 'generalfeedbackformat' => FORMAT_MOODLE,
851 'qtype' => 'truefalse',
852 'options' => (object) array(
863 'feedback' => 'You gave the right answer.',
864 'feedbackformat' => FORMAT_MOODLE,
871 'feedback' => "42 is the Ultimate Answer.",
872 'feedbackformat' => FORMAT_HTML,
878 $exporter = new qformat_gift();
879 $gift = $exporter->writequestion($qdata);
881 $expectedgift = "// question: 666 name: Q1
882 ::Q1::42 is the Absolute Answer to everything.{TRUE#[html]42 is the Ultimate Answer.#You gave the right answer.}
886 $this->assert_same_gift($expectedgift, $gift);
889 public function test_export_backslash() {
890 // There was a bug (MDL-34171) where \\ was getting exported as \\, not
891 // \\\\, and on import, \\ in converted to \.
892 // We need \\\\ in the test code, because of PHPs string escaping rules.
893 $qdata = (object) array(
895 'name' => 'backslash',
896 'questiontext' => 'A \\ B \\\\ C',
897 'questiontextformat' => FORMAT_MOODLE,
898 'generalfeedback' => '',
899 'generalfeedbackformat' => FORMAT_MOODLE,
901 'penalty' => 0.3333333,
904 'options' => (object) array(
905 'responseformat' => 'editor',
906 'responsefieldlines' => 15,
909 'graderinfoformat' => FORMAT_HTML,
913 $exporter = new qformat_gift();
914 $gift = $exporter->writequestion($qdata);
916 $expectedgift = "// question: 666 name: backslash
917 ::backslash::A \\\\ B \\\\\\\\ C{}
921 $this->assert_same_gift($expectedgift, $gift);
924 public function test_import_backslash() {
925 // There was a bug (MDL-34171) where \\ in the import was getting changed
926 // to \. This test checks for that.
927 // We need \\\\ in the test code, because of PHPs string escaping rules.
930 ::double backslash:: A \\\\ B \\\\\\\\ C{}';
931 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
933 $importer = new qformat_gift();
934 $q = $importer->readquestion($lines);
936 $expectedq = (object) array(
937 'name' => 'double backslash',
938 'questiontext' => 'A \\ B \\\\ C',
939 'questiontextformat' => FORMAT_MOODLE,
940 'generalfeedback' => '',
941 'generalfeedbackformat' => FORMAT_MOODLE,
944 'penalty' => 0.3333333,
946 'responseformat' => 'editor',
947 'responsefieldlines' => 15,
949 'graderinfo' => array(
951 'format' => FORMAT_HTML,
955 $this->assert(new question_check_specified_fields_expectation($expectedq), $q);