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