diff --git a/question/format/gift/format.php b/question/format/gift/format.php index d8d037ddcd9..1949d84b5fc 100755 --- a/question/format/gift/format.php +++ b/question/format/gift/format.php @@ -54,29 +54,31 @@ class qformat_gift extends qformat_default { return $answer_weight; } - function commentparser(&$answer) { - if (strpos($answer,"#") > 0) { - $hashpos = strpos($answer,"#"); - $comment = substr($answer, $hashpos+1); - $comment = trim($this->escapedchar_post($comment)); - $answer = substr($answer, 0, $hashpos); + function commentparser($answer, $defaultformat) { + $bits = explode('#', $answer, 2); + $ans = $this->parse_text_with_format(trim($bits[0]), $defaultformat); + if (count($bits) > 1) { + $feedback = $this->parse_text_with_format(trim($bits[1]), $defaultformat); } else { - $comment = " "; + $feedback = array('text' => '', 'format' => $defaultformat, 'files' => array()); } - return $comment; + return array($ans, $feedback); } - function split_truefalse_comment($comment) { - // splits up comment around # marks - // returns an array of true/false feedback - $bits = explode('#',$comment); - $feedback = array('wrong' => $bits[0]); - if (count($bits) >= 2) { - $feedback['right'] = $bits[1]; + function split_truefalse_comment($answer, $defaultformat) { + $bits = explode('#', $answer, 3); + $ans = $this->parse_text_with_format(trim($bits[0]), $defaultformat); + if (count($bits) > 1) { + $wrongfeedback = $this->parse_text_with_format(trim($bits[1]), $defaultformat); } else { - $feedback['right'] = ''; + $wrongfeedback = array('text' => '', 'format' => $defaultformat, 'files' => array()); } - return $feedback; + if (count($bits) > 2) { + $rightfeedback = $this->parse_text_with_format(trim($bits[2]), $defaultformat); + } else { + $rightfeedback = array('text' => '', 'format' => $defaultformat, 'files' => array()); + } + return array($ans, $wrongfeedback, $rightfeedback); } function escapedchar_pre($string) { @@ -110,6 +112,24 @@ class qformat_gift extends qformat_default { return true; } + protected function parse_text_with_format($text, $defaultformat = FORMAT_MOODLE) { + $result = array( + 'text' => $text, + 'format' => $defaultformat, + 'files' => array(), + ); + if (strpos($text, '[') === 0) { + $formatend = strpos($text, ']'); + $result['format'] = $this->format_name_to_const(substr($text, 1, $formatend - 1)); + if ($result['format'] == -1) { + $result['format'] = $defaultformat; + } else { + $result['text'] = substr($text, $formatend + 1); + } + } + $result['text'] = trim($this->escapedchar_post($result['text'])); + return $result; + } function readquestion($lines) { // Given an array of lines known to define a question in this format, this function @@ -196,18 +216,10 @@ class qformat_gift extends qformat_default { } // Get questiontext format from questiontext - $questiontextformat = FORMAT_MOODLE; - if (strpos($questiontext, '[') === 0) { - $formatend = strpos($questiontext, ']'); - $questiontextformat = $this->format_name_to_const(substr($questiontext, 1, $formatend)); - if ($questiontextformat == -1) { - $questiontextformat = FORMAT_MOODLE; - } else { - $questiontext = substr($questiontext, $formatend + 1); - } - } - $question->questiontextformat = $questiontextformat; - $question->questiontext = trim($this->escapedchar_post($questiontext)); + $text = $this->parse_text_with_format($questiontext); + $question->questiontextformat = $text['format']; + $question->generalfeedbackformat = $text['format']; + $question->questiontext = $text['text']; // set question name if not already set if ($question->name === false) { @@ -279,7 +291,7 @@ class qformat_gift extends qformat_default { case ESSAY: $question->fraction = 0; $question->feedback['text'] = ''; - $question->feedback['format'] = $questiontextformat; + $question->feedback['format'] = $question->questiontextformat; $question->feedback['files'] = array(); return $question; break; @@ -290,13 +302,13 @@ class qformat_gift extends qformat_default { $question->single = 1; // only one answer allowed (the default) } $question->correctfeedback['text'] = ''; - $question->correctfeedback['format'] = $questiontextformat; + $question->correctfeedback['format'] = $question->questiontextformat; $question->correctfeedback['files'] = array(); $question->partiallycorrectfeedback['text'] = ''; - $question->partiallycorrectfeedback['format'] = $questiontextformat; + $question->partiallycorrectfeedback['format'] = $question->questiontextformat; $question->partiallycorrectfeedback['files'] = array(); $question->incorrectfeedback['text'] = ''; - $question->incorrectfeedback['format'] = $questiontextformat; + $question->incorrectfeedback['format'] = $question->questiontextformat; $question->incorrectfeedback['files'] = array(); $answertext = str_replace("=", "~=", $answertext); @@ -329,14 +341,9 @@ class qformat_gift extends qformat_default { } else { //default, i.e., wrong anwer $answer_weight = 0; } - $question->answer[$key]['text'] = - $this->escapedchar_post($answer); - $question->answer[$key]['format'] = $questiontextformat; - $question->answer[$key]['files'] = array(); + list($question->answer[$key], $question->feedback[$key]) = + $this->commentparser($answer, $question->questiontextformat); $question->fraction[$key] = $answer_weight; - $question->feedback[$key]['text'] = $this->commentparser($answer); // commentparser also removes comment from $answer - $question->feedback[$key]['format'] = $questiontextformat; - $question->feedback[$key]['files'] = array(); } // end foreach answer //$question->defaultgrade = 1; @@ -368,38 +375,30 @@ class qformat_gift extends qformat_default { } $marker = strpos($answer, '->'); - $question->subquestions[$key]['text'] = - trim($this->escapedchar_post(substr($answer, 0, $marker))); - $question->subquestions[$key]['format'] = $questiontextformat; - $question->subquestions[$key]['files'] = array(); - $question->subanswers[$key] = - trim($this->escapedchar_post(substr($answer, $marker + 2))); - } // end foreach answer + $question->subquestions[$key] = $this->parse_text_with_format( + substr($answer, 0, $marker), $question->questiontextformat); + $question->subanswers[$key] = trim($this->escapedchar_post( + substr($answer, $marker + 2))); + } return $question; break; case TRUEFALSE: - $answer = $answertext; - $comment = $this->commentparser($answer); // commentparser also removes comment from $answer - $feedback = $this->split_truefalse_comment($comment); + list($answer, $wrongfeedback, $rightfeedback) = + $this->split_truefalse_comment($answertext, $question->questiontextformat); if ($answer == "T" OR $answer == "TRUE") { - $question->answer = 1; - $question->feedbacktrue['text'] = $feedback['right']; - $question->feedbackfalse['text'] = $feedback['wrong']; + $question->correctanswer = 1; + $question->feedbacktrue = $rightfeedback; + $question->feedbackfalse = $wrongfeedback; } else { - $question->answer = 0; - $question->feedbackfalse['text'] = $feedback['right']; - $question->feedbacktrue['text'] = $feedback['wrong']; + $question->correctanswer = 0; + $question->feedbacktrue = $wrongfeedback; + $question->feedbackfalse = $rightfeedback; } - $question->feedbackfalse['format'] = $questiontextformat; - $question->feedbacktrue['format'] = $questiontextformat; - $question->feedbackfalse['files'] = array(); - $question->feedbacktrue['files'] = array(); $question->penalty = 1; - $question->correctanswer = $question->answer; return $question; break; @@ -414,7 +413,7 @@ class qformat_gift extends qformat_default { array_shift($answers); } - if (!$this->check_answer_count(1,$answers,$text)) { + if (!$this->check_answer_count(1, $answers, $text)) { return false; break; } @@ -422,22 +421,20 @@ class qformat_gift extends qformat_default { foreach ($answers as $key => $answer) { $answer = trim($answer); - // Answer Weight + // Answer weight if (preg_match($gift_answerweight_regex, $answer)) { // check for properly formatted answer weight $answer_weight = $this->answerweightparser($answer); } else { //default, i.e., full-credit anwer $answer_weight = 1; } - $question->answer[$key] = $this->escapedchar_post($answer); - $question->fraction[$key] = $answer_weight; - $question->feedback[$key]['text'] = $this->commentparser($answer); //commentparser also removes comment from $answer - $question->feedback[$key]['format'] = $questiontextformat; - $question->feedback[$key]['files'] = array(); - } // end foreach - //$question->usecase = 0; // Ignore case - //$question->defaultgrade = 1; - //$question->image = ""; // No images with this format + list($answer, $question->feedback[$key]) = $this->commentparser( + $answer, $question->questiontextformat); + + $question->answer[$key] = $answer['text']; + $question->fraction[$key] = $answer_weight; + } + return $question; break; @@ -478,10 +475,11 @@ class qformat_gift extends qformat_default { } else { //default, i.e., full-credit anwer $answer_weight = 1; } + + list($answer, $question->feedback[$key]) = $this->commentparser( + $answer, $question->questiontextformat); $question->fraction[$key] = $answer_weight; - $question->feedback[$key]['text'] = $this->commentparser($answer); //commentparser also removes comment from $answer - $question->feedback[$key]['format'] = $questiontextformat; - $question->feedback[$key]['files'] = array(); + $answer = $answer['text']; //Calculate Answer and Min/Max values if (strpos($answer,"..") > 0) { // optional [min]..[max] format @@ -509,14 +507,13 @@ class qformat_gift extends qformat_default { // store results $question->answer[$key] = $ans; $question->tolerance[$key] = $tol; - } // end foreach + } if ($wrongfeedback) { $key += 1; $question->fraction[$key] = 0; - $question->feedback[$key]['text'] = $this->commentparser($wrongfeedback); - $question->feedback[$key]['format'] = $questiontextformat; - $question->feedback[$key]['files'] = array(); + list($notused, $question->feedback[$key]) = $this->commentparser( + $wrongfeedback, $question->questiontextformat); $question->answer[$key] = '*'; $question->tolerance[$key] = ''; } @@ -526,7 +523,7 @@ class qformat_gift extends qformat_default { default: $this->error(get_string('giftnovalidquestion', 'quiz'), $text); - return false; + return fale; break; } @@ -569,7 +566,7 @@ class qformat_gift extends qformat_default { if ($format == 'moodle') { return FORMAT_MOODLE; } else if ($format == 'html') { - return FORMAT_PLAIN; + return FORMAT_HTML; } else if ($format == 'plain') { return FORMAT_PLAIN; } else if ($format == 'markdown') { @@ -583,9 +580,9 @@ class qformat_gift extends qformat_default { return '::' . $this->repchar($name) . '::'; } - public function write_questiontext($text, $format) { + public function write_questiontext($text, $format, $defaultformat = FORMAT_MOODLE) { $output = ''; - if ($format != FORMAT_MOODLE) { + if ($text != '' && $format != $defaultformat) { $output .= '[' . $this->format_const_to_name($format) . ']'; } $output .= $this->repchar($text, $format); @@ -596,7 +593,7 @@ class qformat_gift extends qformat_default { global $QTYPES, $OUTPUT; // Start with a comment - $expout = "// question: $question->id name: $question->name \n"; + $expout = "// question: $question->id name: $question->name\n"; // output depends on question type switch($question->qtype) { @@ -622,27 +619,28 @@ class qformat_gift extends qformat_default { $falseanswer = $question->options->answers[$question->options->falseanswer]; if ($trueanswer->fraction == 1) { $answertext = 'TRUE'; - $right_feedback = $trueanswer->feedback; - $wrong_feedback = $falseanswer->feedback; + $rightfeedback = $this->write_questiontext($trueanswer->feedback, + $trueanswer->feedbackformat, $question->questiontextformat); + $wrongfeedback = $this->write_questiontext($falseanswer->feedback, + $falseanswer->feedbackformat, $question->questiontextformat); } else { $answertext = 'FALSE'; - $right_feedback = $falseanswer->feedback; - $wrong_feedback = $trueanswer->feedback; + $rightfeedback = $this->write_questiontext($falseanswer->feedback, + $falseanswer->feedbackformat, $question->questiontextformat); + $wrongfeedback = $this->write_questiontext($trueanswer->feedback, + $trueanswer->feedbackformat, $question->questiontextformat); } - $wrong_feedback = $this->repchar($wrong_feedback); - $right_feedback = $this->repchar($right_feedback); - $expout .= $this->write_name($question->name); $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); $expout .= '{' . $this->repchar($answertext); - if ($wrong_feedback) { - $expout .= '#' . $wrong_feedback; - } else if ($right_feedback) { + if ($wrongfeedback) { + $expout .= '#' . $wrongfeedback; + } else if ($rightfeedback) { $expout .= '#'; } - if ($right_feedback) { - $expout .= '#' . $right_feedback; + if ($rightfeedback) { + $expout .= '#' . $rightfeedback; } $expout .= "}\n"; break; @@ -660,9 +658,11 @@ class qformat_gift extends qformat_default { $weight = $answer->fraction * 100; $answertext = '~%' . $weight . '%'; } - $expout .= "\t" . $answertext . $this->repchar($answer->answer); + $expout .= "\t" . $answertext . $this->write_questiontext($answer->answer, + $answer->answerformat, $question->questiontextformat); if ($answer->feedback != '') { - $expout .= '#' . $this->repchar($answer->feedback); + $expout .= '#' . $this->write_questiontext($answer->feedback, + $answer->feedbackformat, $question->questiontextformat); } $expout .= "\n"; } @@ -676,7 +676,8 @@ class qformat_gift extends qformat_default { foreach($question->options->answers as $answer) { $weight = 100 * $answer->fraction; $expout .= "\t=%" . $weight . '%' . $this->repchar($answer->answer) . - '#' . $this->repchar($answer->feedback) . "\n"; + '#' . $this->write_questiontext($answer->feedback, + $answer->feedbackformat, $question->questiontextformat) . "\n"; } $expout .= "}\n"; break; @@ -689,9 +690,11 @@ class qformat_gift extends qformat_default { if ($answer->answer != '' && $answer->answer != '*') { $weight = 100 * $answer->fraction; $expout .= "\t=%" . $weight . '%' . $answer->answer . ':' . - (float)$answer->tolerance . '#' . $this->repchar($answer->feedback) . "\n"; + (float)$answer->tolerance . '#' . $this->write_questiontext($answer->feedback, + $answer->feedbackformat, $question->questiontextformat) . "\n"; } else { - $expout .= "\t~#" . $this->repchar($answer->feedback) . "\n"; + $expout .= "\t~#" . $this->write_questiontext($answer->feedback, + $answer->feedbackformat, $question->questiontextformat) . "\n"; } } $expout .= "}\n"; @@ -702,7 +705,7 @@ class qformat_gift extends qformat_default { $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); $expout .= "{\n"; foreach($question->options->subquestions as $subquestion) { - $expout .= "\t=" . $this->repchar($subquestion->questiontext) . + $expout .= "\t=" . $this->repchar($this->write_questiontext($subquestion->questiontext, $subquestion->questiontextformat, $question->questiontextformat)) . ' -> ' . $this->repchar($subquestion->answertext) . "\n"; } $expout .= "}\n"; diff --git a/question/format/gift/simpletest/testgiftformat.php b/question/format/gift/simpletest/testgiftformat.php new file mode 100644 index 00000000000..da66ac7e4b0 --- /dev/null +++ b/question/format/gift/simpletest/testgiftformat.php @@ -0,0 +1,710 @@ +. + + +/** + * Unit tests for the Moodle GIFT format. + * + * @package qformat_xml + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once($CFG->libdir . '/questionlib.php'); +require_once($CFG->dirroot . '/question/format.php'); +require_once($CFG->dirroot . '/question/format/gift/format.php'); + + +/** + * Unit tests for the GIFT import/export format. + * + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qformat_gift_test extends UnitTestCase { + public function assert_same_gift($expectedtext, $text) { + $this->assertEqual(str_replace("\r\n", "\n", $expectedtext), + str_replace("\r\n", "\n", $text)); + } + + public function test_import_essay() { + $gift = ' +// essay +::Q8:: How are you? {}'; + $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); + + $importer = new qformat_gift(); + $q = $importer->readquestion($lines); + + $expectedq = (object) array( + 'name' => 'Q8', + 'questiontext' => 'How are you?', + 'questiontextformat' => FORMAT_MOODLE, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_MOODLE, + 'qtype' => 'essay', + 'defaultgrade' => 1, + 'penalty' => 0.1, + 'length' => 1, + 'feedback' => array( + 'text' => '', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + ); + + $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); + } + + public function test_export_essay() { + $qdata = (object) array( + 'id' => 666 , + 'name' => 'Q8', + 'questiontext' => 'How are you?', + 'questiontextformat' => FORMAT_MOODLE, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_MOODLE, + 'defaultgrade' => 1, + 'penalty' => 0.1, + 'length' => 1, + 'qtype' => 'essay', + 'options' => (object) array( + 'answers' => array( + 123 => (object) array( + 'id' => 123, + 'answer' => 666, + 'answerformat' => FORMAT_MOODLE, + 'fraction' => 0, + 'feedback' => '', + 'feedbackformat' => FORMAT_MOODLE, + ), + ), + ), + ); + + $exporter = new qformat_gift(); + $gift = $exporter->writequestion($qdata); + + $expectedgift = "// question: 666 name: Q8 +::Q8::How are you?{} + +"; + + $this->assert_same_gift($expectedgift, $gift); + } + + public function test_import_match() { + $gift = ' +// question: 2 name: Moodle activities +::Moodle activities::[html]Match the activity to the description.{ + =[html]An activity supporting asynchronous discussions. -> Forum + =[moodle]A teacher asks a question and specifies a choice of multiple responses. -> Choice + =[plain]A bank of record entries which participants can add to. -> Database + =[markdown]A collection of web pages that anyone can add to or edit. -> Wiki + = -> Chat +}'; + $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); + + $importer = new qformat_gift(); + $q = $importer->readquestion($lines); + + $expectedq = (object) array( + 'name' => 'Moodle activities', + 'questiontext' => 'Match the activity to the description.', + 'questiontextformat' => FORMAT_HTML, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_HTML, + 'qtype' => 'match', + 'defaultgrade' => 1, + 'penalty' => 0.1, + 'length' => 1, + 'shuffleanswers' => '1', + 'subquestions' => array( + 0 => array( + 'text' => 'An activity supporting asynchronous discussions.', + 'format' => FORMAT_HTML, + 'files' => array(), + ), + 1 => array( + 'text' => 'A teacher asks a question and specifies a choice of multiple responses.', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 2 => array( + 'text' => 'A bank of record entries which participants can add to.', + 'format' => FORMAT_PLAIN, + 'files' => array(), + ), + 3 => array( + 'text' => 'A collection of web pages that anyone can add to or edit.', + 'format' => FORMAT_MARKDOWN, + 'files' => array(), + ), + 4 => array( + 'text' => '', + 'format' => FORMAT_HTML, + 'files' => array(), + ), + ), + 'subanswers' => array( + 0 => 'Forum', + 1 => 'Choice', + 2 => 'Database', + 3 => 'Wiki', + 4 => 'Chat', + ), + ); + + // Repeated test for better failure messages. + $this->assertEqual($expectedq->subquestions, $q->subquestions); + $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); + } + + public function test_export_match() { + $qdata = (object) array( + 'id' => 666 , + 'name' => 'Moodle activities', + 'questiontext' => 'Match the activity to the description.', + 'questiontextformat' => FORMAT_HTML, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_HTML, + 'defaultgrade' => 1, + 'penalty' => 0.1, + 'length' => 1, + 'qtype' => 'match', + 'options' => (object) array( + 'id' => 123, + 'question' => 666, + 'shuffleanswers' => 1, + 'subquestions' => array( + 42 => (object) array( + 'id' => 1234, + 'code' => 12341234, + 'question' => 666, + 'questiontext' => 'An activity supporting asynchronous discussions.', + 'questiontextformat' => FORMAT_HTML, + 'answertext' => 'Forum', + ), + 43 => (object) array( + 'id' => 1234, + 'code' => 12341234, + 'question' => 666, + 'questiontext' => 'A teacher asks a question and specifies a choice of multiple responses.', + 'questiontextformat' => FORMAT_MOODLE, + 'answertext' => 'Choice', + ), + 44 => (object) array( + 'id' => 1234, + 'code' => 12341234, + 'question' => 666, + 'questiontext' => 'A bank of record entries which participants can add to.', + 'questiontextformat' => FORMAT_PLAIN, + 'answertext' => 'Database', + ), + 45 => (object) array( + 'id' => 1234, + 'code' => 12341234, + 'question' => 666, + 'questiontext' => 'A collection of web pages that anyone can add to or edit.', + 'questiontextformat' => FORMAT_MARKDOWN, + 'answertext' => 'Wiki', + ), + 46 => (object) array( + 'id' => 1234, + 'code' => 12341234, + 'question' => 666, + 'questiontext' => '', + 'questiontextformat' => FORMAT_MARKDOWN, + 'answertext' => 'Chat', + ), + ), + ), + ); + + $exporter = new qformat_gift(); + $gift = $exporter->writequestion($qdata); + + $expectedgift = "// question: 666 name: Moodle activities +::Moodle activities::[html]Match the activity to the description.{ +\t=An activity supporting asynchronous discussions. -> Forum +\t=[moodle]A teacher asks a question and specifies a choice of multiple responses. -> Choice +\t=[plain]A bank of record entries which participants can add to. -> Database +\t=[markdown]A collection of web pages that anyone can add to or edit. -> Wiki +\t= -> Chat +} + +"; + + $this->assert_same_gift($expectedgift, $gift); + } + + public function test_import_multichoice() { + $gift = " +// multiple choice with specified feedback for right and wrong answers +::Q2:: What's between orange and green in the spectrum? +{ + =yellow # right; good! + ~red # [html]wrong, it's yellow + ~[plain]blue # wrong, it's yellow +}"; + $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); + + $importer = new qformat_gift(); + $q = $importer->readquestion($lines); + + $expectedq = (object) array( + 'name' => 'Q2', + 'questiontext' => "What's between orange and green in the spectrum?", + 'questiontextformat' => FORMAT_MOODLE, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_MOODLE, + 'qtype' => 'multichoice', + 'defaultgrade' => 1, + 'penalty' => 0.1, + 'length' => 1, + 'single' => 1, + 'shuffleanswers' => '1', + 'answernumbering' => 'abc', + 'correctfeedback' => array( + 'text' => '', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 'partiallycorrectfeedback' => array( + 'text' => '', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 'incorrectfeedback' => array( + 'text' => '', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 'answer' => array( + 0 => array( + 'text' => 'yellow', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 1 => array( + 'text' => 'red', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 2 => array( + 'text' => 'blue', + 'format' => FORMAT_PLAIN, + 'files' => array(), + ), + ), + 'fraction' => array(1, 0, 0), + 'feedback' => array( + 0 => array( + 'text' => 'right; good!', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 1 => array( + 'text' => "wrong, it's yellow", + 'format' => FORMAT_HTML, + 'files' => array(), + ), + 2 => array( + 'text' => "wrong, it's yellow", + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + ), + ); + + // Repeated test for better failure messages. + $this->assertEqual($expectedq->answer, $q->answer); + $this->assertEqual($expectedq->feedback, $q->feedback); + $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); + } + + public function test_export_multichoice() { + $qdata = (object) array( + 'id' => 666 , + 'name' => 'Q8', + 'questiontext' => "What's between orange and green in the spectrum?", + 'questiontextformat' => FORMAT_MOODLE, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_MOODLE, + 'defaultgrade' => 1, + 'penalty' => 0.1, + 'length' => 1, + 'qtype' => 'multichoice', + 'options' => (object) array( + 'single' => 1, + 'shuffleanswers' => '1', + 'answernumbering' => 'abc', + 'correctfeedback' => '', + 'correctfeedbackformat' => FORMAT_MOODLE, + 'partiallycorrectfeedback' => '', + 'partiallycorrectfeedbackformat' => FORMAT_MOODLE, + 'incorrectfeedback' => '', + 'incorrectfeedbackformat' => FORMAT_MOODLE, + 'answers' => array( + 123 => (object) array( + 'id' => 123, + 'answer' => 'yellow', + 'answerformat' => FORMAT_MOODLE, + 'fraction' => 1, + 'feedback' => 'right; good!', + 'feedbackformat' => FORMAT_MOODLE, + ), + 124 => (object) array( + 'id' => 124, + 'answer' => 'red', + 'answerformat' => FORMAT_MOODLE, + 'fraction' => 0, + 'feedback' => "wrong, it's yellow", + 'feedbackformat' => FORMAT_HTML, + ), + 125 => (object) array( + 'id' => 125, + 'answer' => 'blue', + 'answerformat' => FORMAT_PLAIN, + 'fraction' => 0, + 'feedback' => "wrong, it's yellow", + 'feedbackformat' => FORMAT_MOODLE, + ), + ), + ), + ); + + $exporter = new qformat_gift(); + $gift = $exporter->writequestion($qdata); + + $expectedgift = "// question: 666 name: Q8 +::Q8::What's between orange and green in the spectrum?{ +\t=yellow#right; good! +\t~red#[html]wrong, it's yellow +\t~[plain]blue#wrong, it's yellow +} + +"; + + $this->assert_same_gift($expectedgift, $gift); + } + + public function test_import_numerical() { + $gift = " +// math range question +::Q5:: What is a number from 1 to 5? {#3:2~#Completely wrong}"; + $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); + + $importer = new qformat_gift(); + $q = $importer->readquestion($lines); + + $expectedq = (object) array( + 'name' => 'Q5', + 'questiontext' => "What is a number from 1 to 5?", + 'questiontextformat' => FORMAT_MOODLE, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_MOODLE, + 'qtype' => 'numerical', + 'defaultgrade' => 1, + 'penalty' => 0.1, + 'length' => 1, + 'answer' => array( + '3', + '*', + ), + 'fraction' => array(1, 0), + 'feedback' => array( + 0 => array( + 'text' => '', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 1 => array( + 'text' => "Completely wrong", + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + ), + 'tolerance' => array(2, 0), + ); + + // Repeated test for better failure messages. + $this->assertEqual($expectedq->answer, $q->answer); + $this->assertEqual($expectedq->fraction, $q->fraction); + $this->assertEqual($expectedq->feedback, $q->feedback); + $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); + } + + public function test_export_numerical() { + $qdata = (object) array( + 'id' => 666 , + 'name' => 'Q5', + 'questiontext' => "What is a number from 1 to 5?", + 'questiontextformat' => FORMAT_MOODLE, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_MOODLE, + 'defaultgrade' => 1, + 'penalty' => 1, + 'length' => 1, + 'qtype' => 'numerical', + 'options' => (object) array( + 'id' => 123, + 'question' => 666, + 'instructions' => '', + 'instructionsformat' => FORMAT_MOODLE, + 'showunits' => 0, + 'unitsleft' => 0, + 'showunits' => 2, + 'unitgradingtype' => 0, + 'unitpenalty' => 0, + 'answers' => array( + 1 => (object) array( + 'id' => 123, + 'answer' => '3', + 'answerformat' => 0, + 'fraction' => 1, + 'tolerance' => 2, + 'feedback' => '', + 'feedbackformat' => FORMAT_MOODLE, + ), + 2 => (object) array( + 'id' => 124, + 'answer' => '*', + 'answerformat' => 0, + 'fraction' => 0, + 'tolerance' => 0, + 'feedback' => "Completely wrong", + 'feedbackformat' => FORMAT_MOODLE, + ), + ), + ), + ); + + $exporter = new qformat_gift(); + $gift = $exporter->writequestion($qdata); + + $expectedgift = "// question: 666 name: Q5 +::Q5::What is a number from 1 to 5?{# +\t=%100%3:2# +\t~#Completely wrong +} + +"; + + $this->assert_same_gift($expectedgift, $gift); + } + + public function test_import_shortanswer() { + $gift = " +// question: 666 name: Shortanswer +::Shortanswer::Which is the best animal?{ + =Frog#Good! + =%50%Cat#What is it with Moodlers and cats? + =%0%*#Completely wrong +}"; + $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); + + $importer = new qformat_gift(); + $q = $importer->readquestion($lines); + + $expectedq = (object) array( + 'name' => 'Shortanswer', + 'questiontext' => "Which is the best animal?", + 'questiontextformat' => FORMAT_MOODLE, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_MOODLE, + 'qtype' => 'shortanswer', + 'defaultgrade' => 1, + 'penalty' => 0.1, + 'length' => 1, + 'answer' => array( + 'Frog', + 'Cat', + '*', + ), + 'fraction' => array(1, 0.5, 0), + 'feedback' => array( + 0 => array( + 'text' => 'Good!', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 1 => array( + 'text' => "What is it with Moodlers and cats?", + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 2 => array( + 'text' => "Completely wrong", + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + ), + ); + + // Repeated test for better failure messages. + $this->assertEqual($expectedq->answer, $q->answer); + $this->assertEqual($expectedq->fraction, $q->fraction); + $this->assertEqual($expectedq->feedback, $q->feedback); + $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); + } + + public function test_export_shortanswer() { + $qdata = (object) array( + 'id' => 666 , + 'name' => 'Shortanswer', + 'questiontext' => "Which is the best animal?", + 'questiontextformat' => FORMAT_MOODLE, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_MOODLE, + 'defaultgrade' => 1, + 'penalty' => 1, + 'length' => 1, + 'qtype' => 'shortanswer', + 'options' => (object) array( + 'id' => 123, + 'question' => 666, + 'usecase' => 1, + 'answers' => array( + 1 => (object) array( + 'id' => 1, + 'answer' => 'Frog', + 'answerformat' => 0, + 'fraction' => 1, + 'feedback' => 'Good!', + 'feedbackformat' => FORMAT_MOODLE, + ), + 2 => (object) array( + 'id' => 2, + 'answer' => 'Cat', + 'answerformat' => 0, + 'fraction' => 0.5, + 'feedback' => "What is it with Moodlers and cats?", + 'feedbackformat' => FORMAT_MOODLE, + ), + 3 => (object) array( + 'id' => 3, + 'answer' => '*', + 'answerformat' => 0, + 'fraction' => 0, + 'feedback' => "Completely wrong", + 'feedbackformat' => FORMAT_MOODLE, + ), + ), + ), + ); + + $exporter = new qformat_gift(); + $gift = $exporter->writequestion($qdata); + + $expectedgift = "// question: 666 name: Shortanswer +::Shortanswer::Which is the best animal?{ +\t=%100%Frog#Good! +\t=%50%Cat#What is it with Moodlers and cats? +\t=%0%*#Completely wrong +} + +"; + + $this->assert_same_gift($expectedgift, $gift); + } + + public function test_import_truefalse() { + $gift = " +// true/false +::Q1:: 42 is the Absolute Answer to everything.{ +FALSE#42 is the Ultimate Answer.#You gave the right answer.}"; + $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); + + $importer = new qformat_gift(); + $q = $importer->readquestion($lines); + + $expectedq = (object) array( + 'name' => 'Q1', + 'questiontext' => "42 is the Absolute Answer to everything.", + 'questiontextformat' => FORMAT_MOODLE, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_MOODLE, + 'qtype' => 'truefalse', + 'defaultgrade' => 1, + 'penalty' => 1, + 'length' => 1, + 'correctanswer' => 0, + 'feedbacktrue' => array( + 'text' => '42 is the Ultimate Answer.', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + 'feedbackfalse' => array( + 'text' => 'You gave the right answer.', + 'format' => FORMAT_MOODLE, + 'files' => array(), + ), + ); + + $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); + } + + public function test_export_truefalse() { + $qdata = (object) array( + 'id' => 666 , + 'name' => 'Q1', + 'questiontext' => "42 is the Absolute Answer to everything.", + 'questiontextformat' => FORMAT_MOODLE, + 'generalfeedback' => '', + 'generalfeedbackformat' => FORMAT_MOODLE, + 'defaultgrade' => 1, + 'penalty' => 1, + 'length' => 1, + 'qtype' => 'truefalse', + 'options' => (object) array( + 'id' => 123, + 'question' => 666, + 'trueanswer' => 1, + 'falseanswer' => 2, + 'answers' => array( + 1 => (object) array( + 'id' => 123, + 'answer' => 'True', + 'answerformat' => 0, + 'fraction' => 1, + 'feedback' => 'You gave the right answer.', + 'feedbackformat' => FORMAT_MOODLE, + ), + 2 => (object) array( + 'id' => 124, + 'answer' => 'False', + 'answerformat' => 0, + 'fraction' => 0, + 'feedback' => "42 is the Ultimate Answer.", + 'feedbackformat' => FORMAT_HTML, + ), + ), + ), + ); + + $exporter = new qformat_gift(); + $gift = $exporter->writequestion($qdata); + + $expectedgift = "// question: 666 name: Q1 +::Q1::42 is the Absolute Answer to everything.{TRUE#[html]42 is the Ultimate Answer.#You gave the right answer.} + +"; + + $this->assert_same_gift($expectedgift, $gift); + } +}