Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403]
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/>. 16 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 */ 24 25 namespace qformat_xml; 26 27 use qformat_xml; 28 use qtype_numerical_answer; 29 use question_answer; 30 use question_bank; 31 use question_check_specified_fields_expectation; 32 use question_hint; 33 use question_hint_with_parts; 34 35 defined('MOODLE_INTERNAL') || die(); 36 37 global $CFG; 38 require_once($CFG->libdir . '/questionlib.php'); 39 require_once($CFG->dirroot . '/question/format/xml/format.php'); 40 require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); 41 42 /** 43 * Unit tests for the matching question definition class. 44 * 45 * @package qformat_xml 46 * @copyright 2009 The Open University 47 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 48 */ 49 class xmlformat_test extends \question_testcase { 50 public function assert_same_xml($expectedxml, $xml) { 51 $this->assertEquals(str_replace("\r\n", "\n", $expectedxml), 52 str_replace("\r\n", "\n", $xml)); 53 } 54 55 public function make_test_question() { 56 global $USER; 57 $q = new \stdClass(); 58 $q->id = 0; 59 $q->contextid = 0; 60 $q->idnumber = null; 61 $q->category = 0; 62 $q->parent = 0; 63 $q->questiontextformat = FORMAT_HTML; 64 $q->generalfeedbackformat = FORMAT_HTML; 65 $q->defaultmark = 1; 66 $q->penalty = 0.3333333; 67 $q->length = 1; 68 $q->stamp = make_unique_id_code(); 69 $q->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; 70 $q->timecreated = time(); 71 $q->timemodified = time(); 72 $q->createdby = $USER->id; 73 $q->modifiedby = $USER->id; 74 return $q; 75 } 76 77 /** 78 * The data the XML import format sends to save_question is not exactly 79 * the same as the data returned from the editing form, so this method 80 * makes necessary changes to the return value of 81 * \test_question_maker::get_question_form_data so that the tests can work. 82 * @param object $expectedq as returned by get_question_form_data. 83 * @return object one more likely to match the return value of import_...(). 84 */ 85 public function remove_irrelevant_form_data_fields($expectedq) { 86 return $this->itemid_to_files($expectedq); 87 } 88 89 /** 90 * Becuase XML import uses a files array instead of an itemid integer to 91 * handle saving files with a question, we need to covert the output of 92 * \test_question_maker::get_question_form_data to match. This method recursively 93 * replaces all array elements with key itemid with an array entry with 94 * key files and value an empty array. 95 * 96 * @param mixed $var any data structure. 97 * @return mixed an equivalent structure with the relacements made. 98 */ 99 protected function itemid_to_files($var) { 100 if (is_object($var)) { 101 $newvar = new \stdClass(); 102 foreach (get_object_vars($var) as $field => $value) { 103 $newvar->$field = $this->itemid_to_files($value); 104 } 105 106 } else if (is_array($var)) { 107 $newvar = array(); 108 foreach ($var as $index => $value) { 109 if ($index === 'itemid') { 110 $newvar['files'] = array(); 111 } else { 112 $newvar[$index] = $this->itemid_to_files($value); 113 } 114 } 115 116 } else { 117 $newvar = $var; 118 } 119 120 return $newvar; 121 } 122 123 public function test_xml_escape_simple_input_not_escaped() { 124 $exporter = new qformat_xml(); 125 $string = 'Nothing funny here. Even if we go to a café or to 日本.'; 126 $this->assertEquals($string, $exporter->xml_escape($string)); 127 } 128 129 public function test_xml_escape_html_wrapped_in_cdata() { 130 $exporter = new qformat_xml(); 131 $string = '<p>Nothing <b>funny<b> here. Even if we go to a café or to 日本.</p>'; 132 $this->assertEquals('<![CDATA[' . $string . ']]>', $exporter->xml_escape($string)); 133 } 134 135 public function test_xml_escape_script_tag_handled_ok() { 136 $exporter = new qformat_xml(); 137 $input = '<script><![CDATA[alert(1<2);]]></script>'; 138 $expected = '<![CDATA[<script><![CDATA[alert(1<2);]]]]><![CDATA[></script>]]>'; 139 $this->assertEquals($expected, $exporter->xml_escape($input)); 140 141 // Check that parsing the expected result does give the input again. 142 $parsed = simplexml_load_string('<div>' . $expected . '</div>'); 143 $this->assertEquals($input, $parsed->xpath('//div')[0]); 144 } 145 146 public function test_xml_escape_code_that_looks_like_cdata_end_ok() { 147 $exporter = new qformat_xml(); 148 $input = "if (x[[0]]>a) print('hah');"; 149 $expected = "<![CDATA[if (x[[0]]]]><![CDATA[>a) print('hah');]]>"; 150 $this->assertEquals($expected, $exporter->xml_escape($input)); 151 152 // Check that parsing the expected result does give the input again. 153 $parsed = simplexml_load_string('<div>' . $expected . '</div>'); 154 $this->assertEquals($input, $parsed->xpath('//div')[0]); 155 } 156 157 public function test_write_hint_basic() { 158 $q = $this->make_test_question(); 159 $q->contextid = \context_system::instance()->id; 160 $q->name = 'Short answer question'; 161 $q->questiontext = 'Name an amphibian: __________'; 162 $q->generalfeedback = 'Generalfeedback: frog or toad would have been OK.'; 163 if (!isset($q->options)) { 164 $q->options = new \stdClass(); 165 } 166 $q->options->usecase = false; 167 $q->options->answers = array( 168 13 => new question_answer(13, 'frog', 1.0, 'Frog is a very good answer.', FORMAT_HTML), 169 14 => new question_answer(14, 'toad', 0.8, 'Toad is an OK good answer.', FORMAT_HTML), 170 15 => new question_answer(15, '*', 0.0, 'That is a bad answer.', FORMAT_HTML), 171 ); 172 $q->qtype = 'shortanswer'; 173 $q->hints = array( 174 new question_hint(0, 'This is the first hint.', FORMAT_MOODLE), 175 ); 176 177 $exporter = new qformat_xml(); 178 $xml = $exporter->writequestion($q); 179 180 $this->assertMatchesRegularExpression('|<hint format=\"moodle_auto_format\">\s*<text>\s*' . 181 'This is the first hint\.\s*</text>\s*</hint>|', $xml); 182 $this->assertDoesNotMatchRegularExpression('|<shownumcorrect/>|', $xml); 183 $this->assertDoesNotMatchRegularExpression('|<clearwrong/>|', $xml); 184 $this->assertDoesNotMatchRegularExpression('|<options>|', $xml); 185 } 186 187 public function test_write_hint_with_parts() { 188 $q = $this->make_test_question(); 189 $q->contextid = \context_system::instance()->id; 190 $q->name = 'Matching question'; 191 $q->questiontext = 'Classify the animals.'; 192 $q->generalfeedback = 'Frogs and toads are amphibians, the others are mammals.'; 193 $q->qtype = 'match'; 194 195 if (!isset($q->options)) { 196 $q->options = new \stdClass(); 197 } 198 $q->options->shuffleanswers = 1; 199 $q->options->correctfeedback = ''; 200 $q->options->correctfeedbackformat = FORMAT_HTML; 201 $q->options->partiallycorrectfeedback = ''; 202 $q->options->partiallycorrectfeedbackformat = FORMAT_HTML; 203 $q->options->incorrectfeedback = ''; 204 $q->options->incorrectfeedbackformat = FORMAT_HTML; 205 206 $q->options->subquestions = array(); 207 $q->hints = array( 208 new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, false, true), 209 new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, false), 210 ); 211 212 $exporter = new qformat_xml(); 213 $xml = $exporter->writequestion($q); 214 215 $this->assertMatchesRegularExpression( 216 '|<hint format=\"html\">\s*<text>\s*This is the first hint\.\s*</text>|', $xml); 217 $this->assertMatchesRegularExpression( 218 '|<hint format=\"html\">\s*<text>\s*This is the second hint\.\s*</text>|', $xml); 219 list($ignored, $hint1, $hint2) = explode('<hint', $xml); 220 $this->assertDoesNotMatchRegularExpression('|<shownumcorrect/>|', $hint1); 221 $this->assertMatchesRegularExpression('|<clearwrong/>|', $hint1); 222 $this->assertMatchesRegularExpression('|<shownumcorrect/>|', $hint2); 223 $this->assertDoesNotMatchRegularExpression('|<clearwrong/>|', $hint2); 224 $this->assertDoesNotMatchRegularExpression('|<options>|', $xml); 225 } 226 227 public function test_import_hints_no_parts() { 228 $xml = <<<END 229 <question> 230 <hint> 231 <text>This is the first hint</text> 232 <clearwrong/> 233 </hint> 234 <hint> 235 <text>This is the second hint</text> 236 <shownumcorrect/> 237 </hint> 238 </question> 239 END; 240 241 $questionxml = xmlize($xml); 242 $qo = new \stdClass(); 243 244 $importer = new qformat_xml(); 245 $importer->import_hints($qo, $questionxml['question'], false, false, 'html'); 246 247 $this->assertEquals(array( 248 array('text' => 'This is the first hint', 249 'format' => FORMAT_HTML), 250 array('text' => 'This is the second hint', 251 'format' => FORMAT_HTML), 252 ), $qo->hint); 253 $this->assertFalse(isset($qo->hintclearwrong)); 254 $this->assertFalse(isset($qo->hintshownumcorrect)); 255 } 256 257 public function test_import_hints_with_parts() { 258 $xml = <<<END 259 <question> 260 <hint> 261 <text>This is the first hint</text> 262 <clearwrong/> 263 </hint> 264 <hint> 265 <text>This is the second hint</text> 266 <shownumcorrect/> 267 </hint> 268 </question> 269 END; 270 271 $questionxml = xmlize($xml); 272 $qo = new \stdClass(); 273 274 $importer = new qformat_xml(); 275 $importer->import_hints($qo, $questionxml['question'], true, true, 'html'); 276 277 $this->assertEquals(array( 278 array('text' => 'This is the first hint', 279 'format' => FORMAT_HTML), 280 array('text' => 'This is the second hint', 281 'format' => FORMAT_HTML), 282 ), $qo->hint); 283 $this->assertEquals(array(1, 0), $qo->hintclearwrong); 284 $this->assertEquals(array(0, 1), $qo->hintshownumcorrect); 285 } 286 287 public function test_import_no_hints_no_error() { 288 $xml = <<<END 289 <question> 290 </question> 291 END; 292 293 $questionxml = xmlize($xml); 294 $qo = new \stdClass(); 295 296 $importer = new qformat_xml(); 297 $importer->import_hints($qo, $questionxml['question'], 'html'); 298 299 $this->assertFalse(isset($qo->hint)); 300 } 301 302 public function test_import_description() { 303 $xml = ' <question type="description"> 304 <name> 305 <text>A description</text> 306 </name> 307 <questiontext format="html"> 308 <text>The question text.</text> 309 </questiontext> 310 <generalfeedback> 311 <text>Here is some general feedback.</text> 312 </generalfeedback> 313 <defaultgrade>0</defaultgrade> 314 <penalty>0</penalty> 315 <hidden>0</hidden> 316 <tags> 317 <tag><text>tagDescription</text></tag> 318 <tag><text>tagTest</text></tag> 319 </tags> 320 </question>'; 321 $xmldata = xmlize($xml); 322 323 $importer = new qformat_xml(); 324 $q = $importer->import_description($xmldata['question']); 325 326 $expectedq = new \stdClass(); 327 $expectedq->qtype = 'description'; 328 $expectedq->name = 'A description'; 329 $expectedq->questiontext = 'The question text.'; 330 $expectedq->questiontextformat = FORMAT_HTML; 331 $expectedq->generalfeedback = 'Here is some general feedback.'; 332 $expectedq->defaultmark = 0; 333 $expectedq->length = 0; 334 $expectedq->penalty = 0; 335 $expectedq->tags = array('tagDescription', 'tagTest'); 336 337 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 338 } 339 340 public function test_export_description() { 341 $qdata = new \stdClass(); 342 $qdata->id = 123; 343 $qdata->contextid = \context_system::instance()->id; 344 $qdata->qtype = 'description'; 345 $qdata->name = 'A description'; 346 $qdata->questiontext = 'The question text.'; 347 $qdata->questiontextformat = FORMAT_HTML; 348 $qdata->generalfeedback = 'Here is some general feedback.'; 349 $qdata->generalfeedbackformat = FORMAT_HTML; 350 $qdata->defaultmark = 0; 351 $qdata->length = 0; 352 $qdata->penalty = 0; 353 $qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; 354 $qdata->idnumber = null; 355 356 $exporter = new qformat_xml(); 357 $xml = $exporter->writequestion($qdata); 358 359 $expectedxml = '<!-- question: 123 --> 360 <question type="description"> 361 <name> 362 <text>A description</text> 363 </name> 364 <questiontext format="html"> 365 <text>The question text.</text> 366 </questiontext> 367 <generalfeedback format="html"> 368 <text>Here is some general feedback.</text> 369 </generalfeedback> 370 <defaultgrade>0</defaultgrade> 371 <penalty>0</penalty> 372 <hidden>0</hidden> 373 <idnumber></idnumber> 374 </question> 375 '; 376 377 $this->assert_same_xml($expectedxml, $xml); 378 } 379 380 public function test_import_essay_20() { 381 $xml = ' <question type="essay"> 382 <name> 383 <text>An essay</text> 384 </name> 385 <questiontext format="moodle_auto_format"> 386 <text>Write something.</text> 387 </questiontext> 388 <generalfeedback> 389 <text>I hope you wrote something interesting.</text> 390 </generalfeedback> 391 <defaultgrade>1</defaultgrade> 392 <penalty>0</penalty> 393 <hidden>0</hidden> 394 <tags> 395 <tag><text>tagEssay</text></tag> 396 <tag><text>tagEssay20</text></tag> 397 <tag><text>tagTest</text></tag> 398 </tags> 399 </question>'; 400 $xmldata = xmlize($xml); 401 402 $importer = new qformat_xml(); 403 $q = $importer->import_essay($xmldata['question']); 404 405 $expectedq = new \stdClass(); 406 $expectedq->qtype = 'essay'; 407 $expectedq->name = 'An essay'; 408 $expectedq->questiontext = 'Write something.'; 409 $expectedq->questiontextformat = FORMAT_MOODLE; 410 $expectedq->generalfeedback = 'I hope you wrote something interesting.'; 411 $expectedq->defaultmark = 1; 412 $expectedq->length = 1; 413 $expectedq->penalty = 0; 414 $expectedq->responseformat = 'editor'; 415 $expectedq->responserequired = 1; 416 $expectedq->responsefieldlines = 15; 417 $expectedq->minwordlimit = null; 418 $expectedq->minwordenabled = false; 419 $expectedq->maxwordlimit = null; 420 $expectedq->maxwordenabled = false; 421 $expectedq->attachments = 0; 422 $expectedq->attachmentsrequired = 0; 423 $expectedq->maxbytes = 0; 424 $expectedq->filetypeslist = null; 425 $expectedq->graderinfo['text'] = ''; 426 $expectedq->graderinfo['format'] = FORMAT_MOODLE; 427 $expectedq->responsetemplate['text'] = ''; 428 $expectedq->responsetemplate['format'] = FORMAT_MOODLE; 429 $expectedq->tags = array('tagEssay', 'tagEssay20', 'tagTest'); 430 431 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 432 } 433 434 public function test_import_essay_21() { 435 $xml = ' <question type="essay"> 436 <name> 437 <text>An essay</text> 438 </name> 439 <questiontext format="moodle_auto_format"> 440 <text>Write something.</text> 441 </questiontext> 442 <generalfeedback> 443 <text>I hope you wrote something interesting.</text> 444 </generalfeedback> 445 <defaultgrade>1</defaultgrade> 446 <penalty>0</penalty> 447 <hidden>0</hidden> 448 <responseformat>monospaced</responseformat> 449 <responserequired>0</responserequired> 450 <responsefieldlines>42</responsefieldlines> 451 <attachments>-1</attachments> 452 <attachmentsrequired>1</attachmentsrequired> 453 <graderinfo format="html"> 454 <text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text> 455 </graderinfo> 456 <responsetemplate format="html"> 457 <text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text> 458 </responsetemplate> 459 <tags> 460 <tag><text>tagEssay</text></tag> 461 <tag><text>tagEssay21</text></tag> 462 <tag><text>tagTest</text></tag> 463 </tags> 464 </question>'; 465 $xmldata = xmlize($xml); 466 467 $importer = new qformat_xml(); 468 $q = $importer->import_essay($xmldata['question']); 469 470 $expectedq = new \stdClass(); 471 $expectedq->qtype = 'essay'; 472 $expectedq->name = 'An essay'; 473 $expectedq->questiontext = 'Write something.'; 474 $expectedq->questiontextformat = FORMAT_MOODLE; 475 $expectedq->generalfeedback = 'I hope you wrote something interesting.'; 476 $expectedq->defaultmark = 1; 477 $expectedq->length = 1; 478 $expectedq->penalty = 0; 479 $expectedq->responseformat = 'monospaced'; 480 $expectedq->responserequired = 0; 481 $expectedq->responsefieldlines = 42; 482 $expectedq->minwordlimit = null; 483 $expectedq->minwordenabled = false; 484 $expectedq->maxwordlimit = null; 485 $expectedq->maxwordenabled = false; 486 $expectedq->attachments = -1; 487 $expectedq->attachmentsrequired = 1; 488 $expectedq->maxbytes = 0; 489 $expectedq->filetypeslist = null; 490 $expectedq->graderinfo['text'] = '<p>Grade <b>generously</b>!</p>'; 491 $expectedq->graderinfo['format'] = FORMAT_HTML; 492 $expectedq->responsetemplate['text'] = '<p>Here is something <b>really</b> interesting.</p>'; 493 $expectedq->responsetemplate['format'] = FORMAT_HTML; 494 $expectedq->tags = array('tagEssay', 'tagEssay21', 'tagTest'); 495 496 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 497 } 498 499 public function test_import_essay_311() { 500 $xml = ' <question type="essay"> 501 <name> 502 <text>An essay</text> 503 </name> 504 <questiontext format="moodle_auto_format"> 505 <text>Write something.</text> 506 </questiontext> 507 <generalfeedback> 508 <text>I hope you wrote something interesting.</text> 509 </generalfeedback> 510 <defaultgrade>1</defaultgrade> 511 <penalty>0</penalty> 512 <hidden>0</hidden> 513 <responseformat>monospaced</responseformat> 514 <responserequired>0</responserequired> 515 <responsefieldlines>42</responsefieldlines> 516 <minwordlimit>10</minwordlimit> 517 <maxwordlimit>20</maxwordlimit> 518 <attachments>-1</attachments> 519 <attachmentsrequired>1</attachmentsrequired> 520 <maxbytes>52428800</maxbytes> 521 <filetypeslist>.pdf,.zip.,.docx</filetypeslist> 522 <graderinfo format="html"> 523 <text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text> 524 </graderinfo> 525 <responsetemplate format="html"> 526 <text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text> 527 </responsetemplate> 528 <tags> 529 <tag><text>tagEssay</text></tag> 530 <tag><text>tagEssay21</text></tag> 531 <tag><text>tagTest</text></tag> 532 </tags> 533 </question>'; 534 $xmldata = xmlize($xml); 535 536 $importer = new qformat_xml(); 537 $q = $importer->import_essay($xmldata['question']); 538 539 $expectedq = new \stdClass(); 540 $expectedq->qtype = 'essay'; 541 $expectedq->name = 'An essay'; 542 $expectedq->questiontext = 'Write something.'; 543 $expectedq->questiontextformat = FORMAT_MOODLE; 544 $expectedq->generalfeedback = 'I hope you wrote something interesting.'; 545 $expectedq->defaultmark = 1; 546 $expectedq->length = 1; 547 $expectedq->penalty = 0; 548 $expectedq->responseformat = 'monospaced'; 549 $expectedq->responserequired = 0; 550 $expectedq->responsefieldlines = 42; 551 $expectedq->minwordlimit = 10; 552 $expectedq->minwordenabled = true; 553 $expectedq->maxwordlimit = 20; 554 $expectedq->maxwordenabled = true; 555 $expectedq->attachments = -1; 556 $expectedq->attachmentsrequired = 1; 557 $expectedq->maxbytes = 52428800; // 50MB. 558 $expectedq->filetypeslist = '.pdf,.zip.,.docx'; 559 $expectedq->graderinfo['text'] = '<p>Grade <b>generously</b>!</p>'; 560 $expectedq->graderinfo['format'] = FORMAT_HTML; 561 $expectedq->responsetemplate['text'] = '<p>Here is something <b>really</b> interesting.</p>'; 562 $expectedq->responsetemplate['format'] = FORMAT_HTML; 563 $expectedq->tags = array('tagEssay', 'tagEssay21', 'tagTest'); 564 565 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 566 } 567 568 public function test_export_essay() { 569 $qdata = new \stdClass(); 570 $qdata->id = 123; 571 $qdata->contextid = \context_system::instance()->id; 572 $qdata->qtype = 'essay'; 573 $qdata->name = 'An essay'; 574 $qdata->questiontext = 'Write something.'; 575 $qdata->questiontextformat = FORMAT_MOODLE; 576 $qdata->generalfeedback = 'I hope you wrote something interesting.'; 577 $qdata->generalfeedbackformat = FORMAT_MOODLE; 578 $qdata->defaultmark = 1; 579 $qdata->length = 1; 580 $qdata->penalty = 0; 581 $qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; 582 $qdata->idnumber = null; 583 $qdata->options = new \stdClass(); 584 $qdata->options->id = 456; 585 $qdata->options->questionid = 123; 586 $qdata->options->responseformat = 'monospaced'; 587 $qdata->options->responserequired = 0; 588 $qdata->options->responsefieldlines = 42; 589 $qdata->options->minwordlimit = 10; 590 $qdata->options->maxwordlimit = 20; 591 $qdata->options->attachments = -1; 592 $qdata->options->attachmentsrequired = 1; 593 $qdata->options->graderinfo = '<p>Grade <b>generously</b>!</p>'; 594 $qdata->options->graderinfoformat = FORMAT_HTML; 595 $qdata->options->responsetemplate = '<p>Here is something <b>really</b> interesting.</p>'; 596 $qdata->options->responsetemplateformat = FORMAT_HTML; 597 $qdata->options->maxbytes = 52428800; // 50MB. 598 $qdata->options->filetypeslist = '.pdf,.zip.,.docx'; 599 $exporter = new qformat_xml(); 600 $xml = $exporter->writequestion($qdata); 601 602 $expectedxml = '<!-- question: 123 --> 603 <question type="essay"> 604 <name> 605 <text>An essay</text> 606 </name> 607 <questiontext format="moodle_auto_format"> 608 <text>Write something.</text> 609 </questiontext> 610 <generalfeedback format="moodle_auto_format"> 611 <text>I hope you wrote something interesting.</text> 612 </generalfeedback> 613 <defaultgrade>1</defaultgrade> 614 <penalty>0</penalty> 615 <hidden>0</hidden> 616 <idnumber></idnumber> 617 <responseformat>monospaced</responseformat> 618 <responserequired>0</responserequired> 619 <responsefieldlines>42</responsefieldlines> 620 <minwordlimit>10</minwordlimit> 621 <maxwordlimit>20</maxwordlimit> 622 <attachments>-1</attachments> 623 <attachmentsrequired>1</attachmentsrequired> 624 <maxbytes>52428800</maxbytes> 625 <filetypeslist>.pdf,.zip.,.docx</filetypeslist> 626 <graderinfo format="html"> 627 <text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text> 628 </graderinfo> 629 <responsetemplate format="html"> 630 <text><![CDATA[<p>Here is something <b>really</b> interesting.</p>]]></text> 631 </responsetemplate> 632 </question> 633 '; 634 635 $this->assert_same_xml($expectedxml, $xml); 636 } 637 638 public function test_import_match_19() { 639 $xml = ' <question type="matching"> 640 <name> 641 <text>Matching question</text> 642 </name> 643 <questiontext format="html"> 644 <text>Match the upper and lower case letters.</text> 645 </questiontext> 646 <generalfeedback> 647 <text>The answer is A -> a, B -> b and C -> c.</text> 648 </generalfeedback> 649 <defaultgrade>1</defaultgrade> 650 <penalty>0.3333333</penalty> 651 <hidden>0</hidden> 652 <shuffleanswers>false</shuffleanswers> 653 <correctfeedback> 654 <text>Well done.</text> 655 </correctfeedback> 656 <partiallycorrectfeedback> 657 <text>Not entirely.</text> 658 </partiallycorrectfeedback> 659 <incorrectfeedback> 660 <text>Completely wrong!</text> 661 </incorrectfeedback> 662 <subquestion> 663 <text>A</text> 664 <answer> 665 <text>a</text> 666 </answer> 667 </subquestion> 668 <subquestion> 669 <text>B</text> 670 <answer> 671 <text>b</text> 672 </answer> 673 </subquestion> 674 <subquestion> 675 <text>C</text> 676 <answer> 677 <text>c</text> 678 </answer> 679 </subquestion> 680 <subquestion> 681 <text></text> 682 <answer> 683 <text>d</text> 684 </answer> 685 </subquestion> 686 <hint> 687 <text>Hint 1</text> 688 <shownumcorrect /> 689 </hint> 690 <hint> 691 <text></text> 692 <shownumcorrect /> 693 <clearwrong /> 694 </hint> 695 <tags> 696 <tag><text>tagMatching</text></tag> 697 <tag><text>tagTest</text></tag> 698 </tags> 699 </question>'; 700 $xmldata = xmlize($xml); 701 702 $importer = new qformat_xml(); 703 $q = $importer->import_match($xmldata['question']); 704 705 $expectedq = new \stdClass(); 706 $expectedq->qtype = 'match'; 707 $expectedq->name = 'Matching question'; 708 $expectedq->questiontext = 'Match the upper and lower case letters.'; 709 $expectedq->questiontextformat = FORMAT_HTML; 710 $expectedq->correctfeedback = array('text' => 'Well done.', 711 'format' => FORMAT_HTML); 712 $expectedq->partiallycorrectfeedback = array('text' => 'Not entirely.', 713 'format' => FORMAT_HTML); 714 $expectedq->shownumcorrect = false; 715 $expectedq->incorrectfeedback = array('text' => 'Completely wrong!', 716 'format' => FORMAT_HTML); 717 $expectedq->generalfeedback = 'The answer is A -> a, B -> b and C -> c.'; 718 $expectedq->generalfeedbackformat = FORMAT_HTML; 719 $expectedq->defaultmark = 1; 720 $expectedq->length = 1; 721 $expectedq->penalty = 0.3333333; 722 $expectedq->shuffleanswers = 0; 723 $expectedq->subquestions = array( 724 array('text' => 'A', 'format' => FORMAT_HTML), 725 array('text' => 'B', 'format' => FORMAT_HTML), 726 array('text' => 'C', 'format' => FORMAT_HTML), 727 array('text' => '', 'format' => FORMAT_HTML)); 728 $expectedq->subanswers = array('a', 'b', 'c', 'd'); 729 $expectedq->hint = array( 730 array('text' => 'Hint 1', 'format' => FORMAT_HTML), 731 array('text' => '', 'format' => FORMAT_HTML), 732 ); 733 $expectedq->hintshownumcorrect = array(true, true); 734 $expectedq->hintclearwrong = array(false, true); 735 $expectedq->tags = array('tagMatching', 'tagTest'); 736 737 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 738 } 739 740 public function test_export_match() { 741 $qdata = new \stdClass(); 742 $qdata->id = 123; 743 $qdata->contextid = \context_system::instance()->id; 744 $qdata->qtype = 'match'; 745 $qdata->name = 'Matching question'; 746 $qdata->questiontext = 'Match the upper and lower case letters.'; 747 $qdata->questiontextformat = FORMAT_HTML; 748 $qdata->generalfeedback = 'The answer is A -> a, B -> b and C -> c.'; 749 $qdata->generalfeedbackformat = FORMAT_HTML; 750 $qdata->defaultmark = 1; 751 $qdata->length = 1; 752 $qdata->penalty = 0.3333333; 753 $qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; 754 $qdata->idnumber = null; 755 756 $qdata->options = new \stdClass(); 757 $qdata->options->shuffleanswers = 1; 758 $qdata->options->correctfeedback = 'Well done.'; 759 $qdata->options->correctfeedbackformat = FORMAT_HTML; 760 $qdata->options->partiallycorrectfeedback = 'Not entirely.'; 761 $qdata->options->partiallycorrectfeedbackformat = FORMAT_HTML; 762 $qdata->options->shownumcorrect = false; 763 $qdata->options->incorrectfeedback = 'Completely wrong!'; 764 $qdata->options->incorrectfeedbackformat = FORMAT_HTML; 765 766 $subq1 = new \stdClass(); 767 $subq1->id = -4; 768 $subq1->questiontext = 'A'; 769 $subq1->questiontextformat = FORMAT_HTML; 770 $subq1->answertext = 'a'; 771 772 $subq2 = new \stdClass(); 773 $subq2->id = -3; 774 $subq2->questiontext = 'B'; 775 $subq2->questiontextformat = FORMAT_HTML; 776 $subq2->answertext = 'b'; 777 778 $subq3 = new \stdClass(); 779 $subq3->id = -2; 780 $subq3->questiontext = 'C'; 781 $subq3->questiontextformat = FORMAT_HTML; 782 $subq3->answertext = 'c'; 783 784 $subq4 = new \stdClass(); 785 $subq4->id = -1; 786 $subq4->questiontext = ''; 787 $subq4->questiontextformat = FORMAT_HTML; 788 $subq4->answertext = 'd'; 789 790 $qdata->options->subquestions = array( 791 $subq1, $subq2, $subq3, $subq4); 792 793 $qdata->hints = array( 794 new question_hint_with_parts(0, 'Hint 1', FORMAT_HTML, true, false), 795 new question_hint_with_parts(0, '', FORMAT_HTML, true, true), 796 ); 797 798 $exporter = new qformat_xml(); 799 $xml = $exporter->writequestion($qdata); 800 801 $expectedxml = '<!-- question: 123 --> 802 <question type="matching"> 803 <name> 804 <text>Matching question</text> 805 </name> 806 <questiontext format="html"> 807 <text>Match the upper and lower case letters.</text> 808 </questiontext> 809 <generalfeedback format="html"> 810 <text><![CDATA[The answer is A -> a, B -> b and C -> c.]]></text> 811 </generalfeedback> 812 <defaultgrade>1</defaultgrade> 813 <penalty>0.3333333</penalty> 814 <hidden>0</hidden> 815 <idnumber></idnumber> 816 <shuffleanswers>true</shuffleanswers> 817 <correctfeedback format="html"> 818 <text>Well done.</text> 819 </correctfeedback> 820 <partiallycorrectfeedback format="html"> 821 <text>Not entirely.</text> 822 </partiallycorrectfeedback> 823 <incorrectfeedback format="html"> 824 <text>Completely wrong!</text> 825 </incorrectfeedback> 826 <subquestion format="html"> 827 <text>A</text> 828 <answer> 829 <text>a</text> 830 </answer> 831 </subquestion> 832 <subquestion format="html"> 833 <text>B</text> 834 <answer> 835 <text>b</text> 836 </answer> 837 </subquestion> 838 <subquestion format="html"> 839 <text>C</text> 840 <answer> 841 <text>c</text> 842 </answer> 843 </subquestion> 844 <subquestion format="html"> 845 <text></text> 846 <answer> 847 <text>d</text> 848 </answer> 849 </subquestion> 850 <hint format="html"> 851 <text>Hint 1</text> 852 <shownumcorrect/> 853 </hint> 854 <hint format="html"> 855 <text></text> 856 <shownumcorrect/> 857 <clearwrong/> 858 </hint> 859 </question> 860 '; 861 862 $this->assert_same_xml($expectedxml, $xml); 863 } 864 865 public function test_import_multichoice_19() { 866 $xml = ' <question type="multichoice"> 867 <name> 868 <text>Multiple choice question</text> 869 </name> 870 <questiontext format="html"> 871 <text>Which are the even numbers?</text> 872 </questiontext> 873 <generalfeedback> 874 <text>The even numbers are 2 and 4.</text> 875 </generalfeedback> 876 <defaultgrade>2</defaultgrade> 877 <penalty>0.3333333</penalty> 878 <hidden>0</hidden> 879 <single>false</single> 880 <shuffleanswers>false</shuffleanswers> 881 <answernumbering>abc</answernumbering> 882 <correctfeedback> 883 <text><![CDATA[<p>Your answer is correct.</p>]]></text> 884 </correctfeedback> 885 <partiallycorrectfeedback> 886 <text><![CDATA[<p>Your answer is partially correct.</p>]]></text> 887 </partiallycorrectfeedback> 888 <incorrectfeedback> 889 <text><![CDATA[<p>Your answer is incorrect.</p>]]></text> 890 </incorrectfeedback> 891 <shownumcorrect/> 892 <answer fraction="0"> 893 <text>1</text> 894 <feedback> 895 <text></text> 896 </feedback> 897 </answer> 898 <answer fraction="100"> 899 <text>2</text> 900 <feedback> 901 <text></text> 902 </feedback> 903 </answer> 904 <answer fraction="0"> 905 <text>3</text> 906 <feedback> 907 <text></text> 908 </feedback> 909 </answer> 910 <answer fraction="100"> 911 <text>4</text> 912 <feedback> 913 <text></text> 914 </feedback> 915 </answer> 916 <hint> 917 <text>Hint 1.</text> 918 </hint> 919 <hint> 920 <text>Hint 2.</text> 921 </hint> 922 </question>'; 923 $xmldata = xmlize($xml); 924 925 $importer = new qformat_xml(); 926 $q = $importer->import_multichoice($xmldata['question']); 927 928 $expectedq = new \stdClass(); 929 $expectedq->qtype = 'multichoice'; 930 $expectedq->name = 'Multiple choice question'; 931 $expectedq->questiontext = 'Which are the even numbers?'; 932 $expectedq->questiontextformat = FORMAT_HTML; 933 $expectedq->correctfeedback = array( 934 'text' => '<p>Your answer is correct.</p>', 935 'format' => FORMAT_HTML); 936 $expectedq->shownumcorrect = false; 937 $expectedq->partiallycorrectfeedback = array( 938 'text' => '<p>Your answer is partially correct.</p>', 939 'format' => FORMAT_HTML); 940 $expectedq->shownumcorrect = true; 941 $expectedq->incorrectfeedback = array( 942 'text' => '<p>Your answer is incorrect.</p>', 943 'format' => FORMAT_HTML); 944 $expectedq->generalfeedback = 'The even numbers are 2 and 4.'; 945 $expectedq->defaultmark = 2; 946 $expectedq->length = 1; 947 $expectedq->penalty = 0.3333333; 948 $expectedq->shuffleanswers = 0; 949 $expectedq->single = false; 950 951 $expectedq->answer = array( 952 array('text' => '1', 'format' => FORMAT_HTML), 953 array('text' => '2', 'format' => FORMAT_HTML), 954 array('text' => '3', 'format' => FORMAT_HTML), 955 array('text' => '4', 'format' => FORMAT_HTML)); 956 $expectedq->fraction = array(0, 1, 0, 1); 957 $expectedq->feedback = array( 958 array('text' => '', 'format' => FORMAT_HTML), 959 array('text' => '', 'format' => FORMAT_HTML), 960 array('text' => '', 'format' => FORMAT_HTML), 961 array('text' => '', 'format' => FORMAT_HTML)); 962 963 $expectedq->hint = array( 964 array('text' => 'Hint 1.', 'format' => FORMAT_HTML), 965 array('text' => 'Hint 2.', 'format' => FORMAT_HTML), 966 ); 967 $expectedq->hintshownumcorrect = array(false, false); 968 $expectedq->hintclearwrong = array(false, false); 969 970 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 971 } 972 973 public function test_export_multichoice() { 974 $qdata = new \stdClass(); 975 $qdata->id = 123; 976 $qdata->contextid = \context_system::instance()->id; 977 $qdata->qtype = 'multichoice'; 978 $qdata->name = 'Multiple choice question'; 979 $qdata->questiontext = 'Which are the even numbers?'; 980 $qdata->questiontextformat = FORMAT_HTML; 981 $qdata->generalfeedback = 'The even numbers are 2 and 4.'; 982 $qdata->generalfeedbackformat = FORMAT_HTML; 983 $qdata->defaultmark = 2; 984 $qdata->length = 1; 985 $qdata->penalty = 0.3333333; 986 $qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; 987 $qdata->idnumber = null; 988 989 $qdata->options = new \stdClass(); 990 $qdata->options->single = 0; 991 $qdata->options->shuffleanswers = 0; 992 $qdata->options->answernumbering = 'abc'; 993 $qdata->options->showstandardinstruction = 0; 994 $qdata->options->correctfeedback = '<p>Your answer is correct.</p>'; 995 $qdata->options->correctfeedbackformat = FORMAT_HTML; 996 $qdata->options->partiallycorrectfeedback = '<p>Your answer is partially correct.</p>'; 997 $qdata->options->partiallycorrectfeedbackformat = FORMAT_HTML; 998 $qdata->options->shownumcorrect = 1; 999 $qdata->options->incorrectfeedback = '<p>Your answer is incorrect.</p>'; 1000 $qdata->options->incorrectfeedbackformat = FORMAT_HTML; 1001 1002 $qdata->options->answers = array( 1003 13 => new question_answer(13, '1', 0, '', FORMAT_HTML), 1004 14 => new question_answer(14, '2', 1, '', FORMAT_HTML), 1005 15 => new question_answer(15, '3', 0, '', FORMAT_HTML), 1006 16 => new question_answer(16, '4', 1, '', FORMAT_HTML), 1007 ); 1008 1009 $qdata->hints = array( 1010 new question_hint_with_parts(0, 'Hint 1.', FORMAT_HTML, false, false), 1011 new question_hint_with_parts(0, 'Hint 2.', FORMAT_HTML, false, false), 1012 ); 1013 1014 $exporter = new qformat_xml(); 1015 $xml = $exporter->writequestion($qdata); 1016 1017 $expectedxml = '<!-- question: 123 --> 1018 <question type="multichoice"> 1019 <name> 1020 <text>Multiple choice question</text> 1021 </name> 1022 <questiontext format="html"> 1023 <text>Which are the even numbers?</text> 1024 </questiontext> 1025 <generalfeedback format="html"> 1026 <text>The even numbers are 2 and 4.</text> 1027 </generalfeedback> 1028 <defaultgrade>2</defaultgrade> 1029 <penalty>0.3333333</penalty> 1030 <hidden>0</hidden> 1031 <idnumber></idnumber> 1032 <single>false</single> 1033 <shuffleanswers>false</shuffleanswers> 1034 <answernumbering>abc</answernumbering> 1035 <showstandardinstruction>0</showstandardinstruction> 1036 <correctfeedback format="html"> 1037 <text><![CDATA[<p>Your answer is correct.</p>]]></text> 1038 </correctfeedback> 1039 <partiallycorrectfeedback format="html"> 1040 <text><![CDATA[<p>Your answer is partially correct.</p>]]></text> 1041 </partiallycorrectfeedback> 1042 <incorrectfeedback format="html"> 1043 <text><![CDATA[<p>Your answer is incorrect.</p>]]></text> 1044 </incorrectfeedback> 1045 <shownumcorrect/> 1046 <answer fraction="0" format="plain_text"> 1047 <text>1</text> 1048 <feedback format="html"> 1049 <text></text> 1050 </feedback> 1051 </answer> 1052 <answer fraction="100" format="plain_text"> 1053 <text>2</text> 1054 <feedback format="html"> 1055 <text></text> 1056 </feedback> 1057 </answer> 1058 <answer fraction="0" format="plain_text"> 1059 <text>3</text> 1060 <feedback format="html"> 1061 <text></text> 1062 </feedback> 1063 </answer> 1064 <answer fraction="100" format="plain_text"> 1065 <text>4</text> 1066 <feedback format="html"> 1067 <text></text> 1068 </feedback> 1069 </answer> 1070 <hint format="html"> 1071 <text>Hint 1.</text> 1072 </hint> 1073 <hint format="html"> 1074 <text>Hint 2.</text> 1075 </hint> 1076 </question> 1077 '; 1078 1079 $this->assert_same_xml($expectedxml, $xml); 1080 } 1081 1082 public function test_import_numerical_19() { 1083 $xml = ' <question type="numerical"> 1084 <name> 1085 <text>Numerical question</text> 1086 </name> 1087 <questiontext format="html"> 1088 <text>What is the answer?</text> 1089 </questiontext> 1090 <generalfeedback> 1091 <text>General feedback: Think Hitch-hikers guide to the Galaxy.</text> 1092 </generalfeedback> 1093 <defaultgrade>1</defaultgrade> 1094 <penalty>0.1</penalty> 1095 <hidden>0</hidden> 1096 <answer fraction="100"> 1097 <text>42</text> 1098 <feedback> 1099 <text>Well done!</text> 1100 </feedback> 1101 <tolerance>0.001</tolerance> 1102 </answer> 1103 <answer fraction="0"> 1104 <text>13</text> 1105 <feedback> 1106 <text>What were you thinking?!</text> 1107 </feedback> 1108 <tolerance>1</tolerance> 1109 </answer> 1110 <answer fraction="0"> 1111 <text>*</text> 1112 <feedback> 1113 <text>Completely wrong.</text> 1114 </feedback> 1115 <tolerance></tolerance> 1116 </answer> 1117 </question>'; 1118 $xmldata = xmlize($xml); 1119 1120 $importer = new qformat_xml(); 1121 $q = $importer->import_numerical($xmldata['question']); 1122 1123 $expectedq = new \stdClass(); 1124 $expectedq->qtype = 'numerical'; 1125 $expectedq->name = 'Numerical question'; 1126 $expectedq->questiontext = 'What is the answer?'; 1127 $expectedq->questiontextformat = FORMAT_HTML; 1128 $expectedq->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.'; 1129 $expectedq->generalfeedbackformat = FORMAT_HTML; 1130 $expectedq->defaultmark = 1; 1131 $expectedq->length = 1; 1132 $expectedq->penalty = 0.1; 1133 1134 $expectedq->answer = array('42', '13', '*'); 1135 $expectedq->fraction = array(1, 0, 0); 1136 $expectedq->feedback = array( 1137 array('text' => 'Well done!', 1138 'format' => FORMAT_HTML), 1139 array('text' => 'What were you thinking?!', 1140 'format' => FORMAT_HTML), 1141 array('text' => 'Completely wrong.', 1142 'format' => FORMAT_HTML)); 1143 $expectedq->tolerance = array(0.001, 1, ''); 1144 1145 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 1146 } 1147 1148 public function test_export_numerical() { 1149 question_bank::load_question_definition_classes('numerical'); 1150 1151 $qdata = new \stdClass(); 1152 $qdata->id = 123; 1153 $qdata->contextid = \context_system::instance()->id; 1154 $qdata->qtype = 'numerical'; 1155 $qdata->name = 'Numerical question'; 1156 $qdata->questiontext = 'What is the answer?'; 1157 $qdata->questiontextformat = FORMAT_HTML; 1158 $qdata->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.'; 1159 $qdata->generalfeedbackformat = FORMAT_HTML; 1160 $qdata->defaultmark = 1; 1161 $qdata->length = 1; 1162 $qdata->penalty = 0.1; 1163 $qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; 1164 $qdata->idnumber = null; 1165 1166 $qdata->options = new \stdClass(); 1167 $qdata->options->answers = array( 1168 13 => new qtype_numerical_answer(13, '42', 1, 'Well done!', 1169 FORMAT_HTML, 0.001), 1170 14 => new qtype_numerical_answer(14, '13', 0, 'What were you thinking?!', 1171 FORMAT_HTML, 1), 1172 15 => new qtype_numerical_answer(15, '*', 0, 'Completely wrong.', 1173 FORMAT_HTML, ''), 1174 ); 1175 1176 $qdata->options->units = array(); 1177 1178 $exporter = new qformat_xml(); 1179 $xml = $exporter->writequestion($qdata); 1180 1181 $expectedxml = '<!-- question: 123 --> 1182 <question type="numerical"> 1183 <name> 1184 <text>Numerical question</text> 1185 </name> 1186 <questiontext format="html"> 1187 <text>What is the answer?</text> 1188 </questiontext> 1189 <generalfeedback format="html"> 1190 <text>General feedback: Think Hitch-hikers guide to the Galaxy.</text> 1191 </generalfeedback> 1192 <defaultgrade>1</defaultgrade> 1193 <penalty>0.1</penalty> 1194 <hidden>0</hidden> 1195 <idnumber></idnumber> 1196 <answer fraction="100" format="plain_text"> 1197 <text>42</text> 1198 <feedback format="html"> 1199 <text>Well done!</text> 1200 </feedback> 1201 <tolerance>0.001</tolerance> 1202 </answer> 1203 <answer fraction="0" format="plain_text"> 1204 <text>13</text> 1205 <feedback format="html"> 1206 <text>What were you thinking?!</text> 1207 </feedback> 1208 <tolerance>1</tolerance> 1209 </answer> 1210 <answer fraction="0" format="plain_text"> 1211 <text>*</text> 1212 <feedback format="html"> 1213 <text>Completely wrong.</text> 1214 </feedback> 1215 <tolerance>0</tolerance> 1216 </answer> 1217 </question> 1218 '; 1219 1220 $this->assert_same_xml($expectedxml, $xml); 1221 } 1222 1223 public function test_import_shortanswer_19() { 1224 $xml = ' <question type="shortanswer"> 1225 <name> 1226 <text>Short answer question</text> 1227 </name> 1228 <questiontext format="html"> 1229 <text>Fill in the gap in this sequence: Alpha, ________, Gamma.</text> 1230 </questiontext> 1231 <generalfeedback> 1232 <text>The answer is Beta.</text> 1233 </generalfeedback> 1234 <defaultgrade>1</defaultgrade> 1235 <penalty>0.3333333</penalty> 1236 <hidden>0</hidden> 1237 <usecase>0</usecase> 1238 <answer fraction="100" format="plain_text"> 1239 <text>Beta</text> 1240 <feedback> 1241 <text>Well done!</text> 1242 </feedback> 1243 </answer> 1244 <answer fraction="0" format="plain_text"> 1245 <text>*</text> 1246 <feedback> 1247 <text>Doh!</text> 1248 </feedback> 1249 </answer> 1250 <hint> 1251 <text>Hint 1</text> 1252 </hint> 1253 <hint> 1254 <text>Hint 2</text> 1255 </hint> 1256 </question>'; 1257 $xmldata = xmlize($xml); 1258 1259 $importer = new qformat_xml(); 1260 $q = $importer->import_shortanswer($xmldata['question']); 1261 1262 $expectedq = new \stdClass(); 1263 $expectedq->qtype = 'shortanswer'; 1264 $expectedq->name = 'Short answer question'; 1265 $expectedq->questiontext = 'Fill in the gap in this sequence: Alpha, ________, Gamma.'; 1266 $expectedq->questiontextformat = FORMAT_HTML; 1267 $expectedq->generalfeedback = 'The answer is Beta.'; 1268 $expectedq->usecase = false; 1269 $expectedq->defaultmark = 1; 1270 $expectedq->length = 1; 1271 $expectedq->penalty = 0.3333333; 1272 1273 $expectedq->answer = array('Beta', '*'); 1274 $expectedq->fraction = array(1, 0); 1275 $expectedq->feedback = array( 1276 array('text' => 'Well done!', 'format' => FORMAT_HTML), 1277 array('text' => 'Doh!', 'format' => FORMAT_HTML)); 1278 1279 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 1280 } 1281 1282 public function test_export_shortanswer() { 1283 $qdata = new \stdClass(); 1284 $qdata->id = 123; 1285 $qdata->contextid = \context_system::instance()->id; 1286 $qdata->qtype = 'shortanswer'; 1287 $qdata->name = 'Short answer question'; 1288 $qdata->questiontext = 'Fill in the gap in this sequence: Alpha, ________, Gamma.'; 1289 $qdata->questiontextformat = FORMAT_HTML; 1290 $qdata->generalfeedback = 'The answer is Beta.'; 1291 $qdata->generalfeedbackformat = FORMAT_HTML; 1292 $qdata->defaultmark = 1; 1293 $qdata->length = 1; 1294 $qdata->penalty = 0.3333333; 1295 $qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; 1296 $qdata->idnumber = null; 1297 1298 $qdata->options = new \stdClass(); 1299 $qdata->options->usecase = 0; 1300 1301 $qdata->options->answers = array( 1302 13 => new question_answer(13, 'Beta', 1, 'Well done!', FORMAT_HTML), 1303 14 => new question_answer(14, '*', 0, 'Doh!', FORMAT_HTML), 1304 ); 1305 1306 $qdata->hints = array( 1307 new question_hint(0, 'Hint 1', FORMAT_HTML), 1308 new question_hint(0, 'Hint 2', FORMAT_HTML), 1309 ); 1310 1311 $exporter = new qformat_xml(); 1312 $xml = $exporter->writequestion($qdata); 1313 1314 $expectedxml = '<!-- question: 123 --> 1315 <question type="shortanswer"> 1316 <name> 1317 <text>Short answer question</text> 1318 </name> 1319 <questiontext format="html"> 1320 <text>Fill in the gap in this sequence: Alpha, ________, Gamma.</text> 1321 </questiontext> 1322 <generalfeedback format="html"> 1323 <text>The answer is Beta.</text> 1324 </generalfeedback> 1325 <defaultgrade>1</defaultgrade> 1326 <penalty>0.3333333</penalty> 1327 <hidden>0</hidden> 1328 <idnumber></idnumber> 1329 <usecase>0</usecase> 1330 <answer fraction="100" format="plain_text"> 1331 <text>Beta</text> 1332 <feedback format="html"> 1333 <text>Well done!</text> 1334 </feedback> 1335 </answer> 1336 <answer fraction="0" format="plain_text"> 1337 <text>*</text> 1338 <feedback format="html"> 1339 <text>Doh!</text> 1340 </feedback> 1341 </answer> 1342 <hint format="html"> 1343 <text>Hint 1</text> 1344 </hint> 1345 <hint format="html"> 1346 <text>Hint 2</text> 1347 </hint> 1348 </question> 1349 '; 1350 1351 $this->assert_same_xml($expectedxml, $xml); 1352 } 1353 1354 public function test_import_truefalse_19() { 1355 $xml = ' <question type="truefalse"> 1356 <name> 1357 <text>True false question</text> 1358 </name> 1359 <questiontext format="html"> 1360 <text>The answer is true.</text> 1361 </questiontext> 1362 <generalfeedback> 1363 <text>General feedback: You should have chosen true.</text> 1364 </generalfeedback> 1365 <defaultgrade>1</defaultgrade> 1366 <penalty>1</penalty> 1367 <hidden>0</hidden> 1368 <answer fraction="100"> 1369 <text>true</text> 1370 <feedback> 1371 <text>Well done!</text> 1372 </feedback> 1373 </answer> 1374 <answer fraction="0"> 1375 <text>false</text> 1376 <feedback> 1377 <text>Doh!</text> 1378 </feedback> 1379 </answer> 1380 </question>'; 1381 $xmldata = xmlize($xml); 1382 1383 $importer = new qformat_xml(); 1384 $q = $importer->import_truefalse($xmldata['question']); 1385 1386 $expectedq = new \stdClass(); 1387 $expectedq->qtype = 'truefalse'; 1388 $expectedq->name = 'True false question'; 1389 $expectedq->questiontext = 'The answer is true.'; 1390 $expectedq->questiontextformat = FORMAT_HTML; 1391 $expectedq->generalfeedback = 'General feedback: You should have chosen true.'; 1392 $expectedq->defaultmark = 1; 1393 $expectedq->length = 1; 1394 $expectedq->penalty = 1; 1395 1396 $expectedq->feedbacktrue = array('text' => 'Well done!', 1397 'format' => FORMAT_HTML); 1398 $expectedq->feedbackfalse = array('text' => 'Doh!', 1399 'format' => FORMAT_HTML); 1400 $expectedq->correctanswer = true; 1401 1402 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 1403 } 1404 1405 public function test_import_truefalse_with_idnumber() { 1406 $xml = ' <question type="truefalse"> 1407 <name> 1408 <text>True false question</text> 1409 </name> 1410 <questiontext format="html"> 1411 <text>The answer is true.</text> 1412 </questiontext> 1413 <generalfeedback> 1414 <text>General feedback: You should have chosen true.</text> 1415 </generalfeedback> 1416 <defaultgrade>1</defaultgrade> 1417 <penalty>1</penalty> 1418 <hidden>0</hidden> 1419 <idnumber>TestIdNum1</idnumber> 1420 <answer fraction="100"> 1421 <text>true</text> 1422 <feedback> 1423 <text>Well done!</text> 1424 </feedback> 1425 </answer> 1426 <answer fraction="0"> 1427 <text>false</text> 1428 <feedback> 1429 <text>Doh!</text> 1430 </feedback> 1431 </answer> 1432 </question>'; 1433 $xmldata = xmlize($xml); 1434 1435 $importer = new qformat_xml(); 1436 $q = $importer->import_truefalse($xmldata['question']); 1437 1438 $expectedq = new \stdClass(); 1439 $expectedq->qtype = 'truefalse'; 1440 $expectedq->name = 'True false question'; 1441 $expectedq->questiontext = 'The answer is true.'; 1442 $expectedq->questiontextformat = FORMAT_HTML; 1443 $expectedq->generalfeedback = 'General feedback: You should have chosen true.'; 1444 $expectedq->defaultmark = 1; 1445 $expectedq->length = 1; 1446 $expectedq->penalty = 1; 1447 $expectedq->idnumber = 'TestIdNum1'; 1448 1449 $expectedq->feedbacktrue = array('text' => 'Well done!', 1450 'format' => FORMAT_HTML); 1451 $expectedq->feedbackfalse = array('text' => 'Doh!', 1452 'format' => FORMAT_HTML); 1453 $expectedq->correctanswer = true; 1454 1455 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 1456 } 1457 1458 public function test_export_truefalse() { 1459 $qdata = new \stdClass(); 1460 $qdata->id = 12; 1461 $qdata->contextid = \context_system::instance()->id; 1462 $qdata->qtype = 'truefalse'; 1463 $qdata->name = 'True false question'; 1464 $qdata->questiontext = 'The answer is true.'; 1465 $qdata->questiontextformat = FORMAT_HTML; 1466 $qdata->generalfeedback = 'General feedback: You should have chosen true.'; 1467 $qdata->generalfeedbackformat = FORMAT_HTML; 1468 $qdata->defaultmark = 1; 1469 $qdata->length = 1; 1470 $qdata->penalty = 1; 1471 $qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; 1472 $qdata->idnumber = null; 1473 1474 $qdata->options = new \stdClass(); 1475 $qdata->options->answers = array( 1476 1 => new question_answer(1, 'True', 1, 'Well done!', FORMAT_HTML), 1477 2 => new question_answer(2, 'False', 0, 'Doh!', FORMAT_HTML), 1478 ); 1479 $qdata->options->trueanswer = 1; 1480 $qdata->options->falseanswer = 2; 1481 1482 $exporter = new qformat_xml(); 1483 $xml = $exporter->writequestion($qdata); 1484 1485 $expectedxml = '<!-- question: 12 --> 1486 <question type="truefalse"> 1487 <name> 1488 <text>True false question</text> 1489 </name> 1490 <questiontext format="html"> 1491 <text>The answer is true.</text> 1492 </questiontext> 1493 <generalfeedback format="html"> 1494 <text>General feedback: You should have chosen true.</text> 1495 </generalfeedback> 1496 <defaultgrade>1</defaultgrade> 1497 <penalty>1</penalty> 1498 <hidden>0</hidden> 1499 <idnumber></idnumber> 1500 <answer fraction="100" format="plain_text"> 1501 <text>true</text> 1502 <feedback format="html"> 1503 <text>Well done!</text> 1504 </feedback> 1505 </answer> 1506 <answer fraction="0" format="plain_text"> 1507 <text>false</text> 1508 <feedback format="html"> 1509 <text>Doh!</text> 1510 </feedback> 1511 </answer> 1512 </question> 1513 '; 1514 1515 $this->assert_same_xml($expectedxml, $xml); 1516 } 1517 1518 public function test_export_truefalse_with_idnumber() { 1519 $qdata = new \stdClass(); 1520 $qdata->id = 12; 1521 $qdata->contextid = \context_system::instance()->id; 1522 $qdata->qtype = 'truefalse'; 1523 $qdata->name = 'True false question'; 1524 $qdata->questiontext = 'The answer is true.'; 1525 $qdata->questiontextformat = FORMAT_HTML; 1526 $qdata->generalfeedback = 'General feedback: You should have chosen true.'; 1527 $qdata->generalfeedbackformat = FORMAT_HTML; 1528 $qdata->defaultmark = 1; 1529 $qdata->length = 1; 1530 $qdata->penalty = 1; 1531 $qdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; 1532 $qdata->idnumber = 'TestIDNum2'; 1533 1534 $qdata->options = new \stdClass(); 1535 $qdata->options->answers = array( 1536 1 => new question_answer(1, 'True', 1, 'Well done!', FORMAT_HTML), 1537 2 => new question_answer(2, 'False', 0, 'Doh!', FORMAT_HTML), 1538 ); 1539 $qdata->options->trueanswer = 1; 1540 $qdata->options->falseanswer = 2; 1541 1542 $exporter = new qformat_xml(); 1543 $xml = $exporter->writequestion($qdata); 1544 1545 $expectedxml = '<!-- question: 12 --> 1546 <question type="truefalse"> 1547 <name> 1548 <text>True false question</text> 1549 </name> 1550 <questiontext format="html"> 1551 <text>The answer is true.</text> 1552 </questiontext> 1553 <generalfeedback format="html"> 1554 <text>General feedback: You should have chosen true.</text> 1555 </generalfeedback> 1556 <defaultgrade>1</defaultgrade> 1557 <penalty>1</penalty> 1558 <hidden>0</hidden> 1559 <idnumber>TestIDNum2</idnumber> 1560 <answer fraction="100" format="plain_text"> 1561 <text>true</text> 1562 <feedback format="html"> 1563 <text>Well done!</text> 1564 </feedback> 1565 </answer> 1566 <answer fraction="0" format="plain_text"> 1567 <text>false</text> 1568 <feedback format="html"> 1569 <text>Doh!</text> 1570 </feedback> 1571 </answer> 1572 </question> 1573 '; 1574 1575 $this->assert_same_xml($expectedxml, $xml); 1576 } 1577 1578 public function test_import_multianswer() { 1579 $xml = ' <question type="cloze"> 1580 <name> 1581 <text>Simple multianswer</text> 1582 </name> 1583 <questiontext format="html"> 1584 <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> 1585 </questiontext> 1586 <generalfeedback format="html"> 1587 <text><![CDATA[General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea".]]></text> 1588 </generalfeedback> 1589 <penalty>0.5</penalty> 1590 <hidden>0</hidden> 1591 <idnumber>id-101</idnumber> 1592 <hint format="html"> 1593 <text>Hint 1</text> 1594 </hint> 1595 <hint format="html"> 1596 <text>Hint 2</text> 1597 </hint> 1598 <tags> 1599 <tag><text>tagCloze</text></tag> 1600 <tag><text>tagTest</text></tag> 1601 </tags> 1602 </question> 1603 '; 1604 $xmldata = xmlize($xml); 1605 1606 $importer = new qformat_xml(); 1607 $q = $importer->import_multianswer($xmldata['question']); 1608 1609 // Annoyingly, import works in a weird way (it duplicates code, rather 1610 // than just calling save_question) so we cannot use 1611 // \test_question_maker::get_question_form_data('multianswer', 'twosubq'). 1612 $expectedqa = new \stdClass(); 1613 $expectedqa->name = 'Simple multianswer'; 1614 $expectedqa->qtype = 'multianswer'; 1615 $expectedqa->questiontext = 'Complete this opening line of verse: "The {#1} and the {#2} went to sea".'; 1616 $expectedqa->idnumber = 'id-101'; 1617 $expectedqa->generalfeedback = 1618 'General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea".'; 1619 $expectedqa->defaultmark = 2; 1620 $expectedqa->penalty = 0.5; 1621 1622 $expectedqa->hint = array( 1623 array('text' => 'Hint 1', 'format' => FORMAT_HTML), 1624 array('text' => 'Hint 2', 'format' => FORMAT_HTML), 1625 ); 1626 1627 $sa = new \stdClass(); 1628 1629 $sa->questiontext = array('text' => '{1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer}', 1630 'format' => FORMAT_HTML, 'itemid' => null); 1631 $sa->generalfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null); 1632 $sa->defaultmark = 1.0; 1633 $sa->qtype = 'shortanswer'; 1634 $sa->usecase = 0; 1635 1636 $sa->answer = array('Dog', 'Owl', '*'); 1637 $sa->fraction = array(0, 1, 0); 1638 $sa->feedback = array( 1639 array('text' => 'Wrong, silly!', 'format' => FORMAT_HTML, 'itemid' => null), 1640 array('text' => 'Well done!', 'format' => FORMAT_HTML, 'itemid' => null), 1641 array('text' => 'Wrong answer', 'format' => FORMAT_HTML, 'itemid' => null), 1642 ); 1643 1644 $mc = new \stdClass(); 1645 1646 $mc->generalfeedback = ''; 1647 $mc->questiontext = array('text' => '{1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~' . 1648 'Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!}', 1649 'format' => FORMAT_HTML, 'itemid' => null); 1650 $mc->generalfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null); 1651 $mc->defaultmark = 1.0; 1652 $mc->qtype = 'multichoice'; 1653 1654 $mc->layout = 0; 1655 $mc->single = 1; 1656 $mc->shuffleanswers = 0; 1657 $mc->correctfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null); 1658 $mc->partiallycorrectfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null); 1659 $mc->incorrectfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null); 1660 $mc->answernumbering = 0; 1661 1662 $mc->answer = array( 1663 array('text' => 'Bow-wow', 'format' => FORMAT_HTML, 'itemid' => null), 1664 array('text' => 'Wiggly worm', 'format' => FORMAT_HTML, 'itemid' => null), 1665 array('text' => 'Pussy-cat', 'format' => FORMAT_HTML, 'itemid' => null), 1666 ); 1667 $mc->fraction = array(0, 0, 1); 1668 $mc->feedback = array( 1669 array('text' => 'You seem to have a dog obsessions!', 'format' => FORMAT_HTML, 'itemid' => null), 1670 array('text' => 'Now you are just being ridiculous!', 'format' => FORMAT_HTML, 'itemid' => null), 1671 array('text' => 'Well done!', 'format' => FORMAT_HTML, 'itemid' => null), 1672 ); 1673 1674 $expectedqa->options = new \stdClass(); 1675 $expectedqa->options->questions = array( 1676 1 => $sa, 1677 2 => $mc, 1678 ); 1679 $expectedqa->tags = array('tagCloze', 'tagTest'); 1680 1681 $this->assertEquals($expectedqa->hint, $q->hint); 1682 $this->assertEquals($expectedqa->options->questions[1], $q->options->questions[1]); 1683 $this->assertEquals($expectedqa->options->questions[2], $q->options->questions[2]); 1684 $this->assert(new question_check_specified_fields_expectation($expectedqa), $q); 1685 } 1686 1687 public function test_export_multianswer() { 1688 $qdata = \test_question_maker::get_question_data('multianswer', 'twosubq'); 1689 $qdata->contextid = \context_system::instance()->id; 1690 $exporter = new qformat_xml(); 1691 $xml = $exporter->writequestion($qdata); 1692 1693 $expectedxml = '<!-- question: 0 --> 1694 <question type="cloze"> 1695 <name> 1696 <text>Simple multianswer</text> 1697 </name> 1698 <questiontext format="html"> 1699 <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> 1700 </questiontext> 1701 <generalfeedback format="html"> 1702 <text><![CDATA[General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea]]></text> 1703 </generalfeedback> 1704 <penalty>0.3333333</penalty> 1705 <hidden>0</hidden> 1706 <idnumber></idnumber> 1707 <hint format="html"> 1708 <text>Hint 1</text> 1709 </hint> 1710 <hint format="html"> 1711 <text>Hint 2</text> 1712 </hint> 1713 </question> 1714 '; 1715 1716 $this->assert_same_xml($expectedxml, $xml); 1717 } 1718 1719 public function test_export_multianswer_withdollars() { 1720 $qdata = \test_question_maker::get_question_data('multianswer', 'dollarsigns'); 1721 $qdata->contextid = \context_system::instance()->id; 1722 $exporter = new qformat_xml(); 1723 $xml = $exporter->writequestion($qdata); 1724 1725 $expectedxml = '<!-- question: 0 --> 1726 <question type="cloze"> 1727 <name> 1728 <text>Multianswer with $s</text> 1729 </name> 1730 <questiontext format="html"> 1731 <text>Which is the right order? {1:MULTICHOICE:=y,y,$3~$3,y,y}</text> 1732 </questiontext> 1733 <generalfeedback format="html"> 1734 <text></text> 1735 </generalfeedback> 1736 <penalty>0.3333333</penalty> 1737 <hidden>0</hidden> 1738 <idnumber></idnumber> 1739 </question> 1740 '; 1741 1742 $this->assert_same_xml($expectedxml, $xml); 1743 } 1744 1745 public function test_import_files_as_draft() { 1746 $this->resetAfterTest(); 1747 $this->setAdminUser(); 1748 1749 $xml = <<<END 1750 <questiontext format="html"> 1751 <text><![CDATA[<p><a href="@@PLUGINFILE@@/moodle.txt">This text file</a> contains the word 'Moodle'.</p>]]></text> 1752 <file name="moodle.txt" encoding="base64">TW9vZGxl</file> 1753 </questiontext> 1754 END; 1755 1756 $textxml = xmlize($xml); 1757 $qo = new \stdClass(); 1758 1759 $importer = new qformat_xml(); 1760 $draftitemid = $importer->import_files_as_draft($textxml['questiontext']['#']['file']); 1761 $files = file_get_drafarea_files($draftitemid); 1762 1763 $this->assertEquals(1, count($files->list)); 1764 1765 $file = $files->list[0]; 1766 $this->assertEquals('moodle.txt', $file->filename); 1767 $this->assertEquals('/', $file->filepath); 1768 $this->assertEquals(6, $file->size); 1769 } 1770 1771 public function test_import_truefalse_wih_files() { 1772 $this->resetAfterTest(); 1773 $this->setAdminUser(); 1774 1775 $xml = '<question type="truefalse"> 1776 <name> 1777 <text>truefalse</text> 1778 </name> 1779 <questiontext format="html"> 1780 <text><![CDATA[<p><a href="@@PLUGINFILE@@/myfolder/moodle.txt">This text file</a> contains the word Moodle.</p>]]></text> 1781 <file name="moodle.txt" path="/myfolder/" encoding="base64">TW9vZGxl</file> 1782 </questiontext> 1783 <generalfeedback format="html"> 1784 <text><![CDATA[<p>For further information, see the documentation about Moodle.</p>]]></text> 1785 </generalfeedback> 1786 <defaultgrade>1.0000000</defaultgrade> 1787 <penalty>1.0000000</penalty> 1788 <hidden>0</hidden> 1789 <answer fraction="100" format="moodle_auto_format"> 1790 <text>true</text> 1791 <feedback format="html"> 1792 <text></text> 1793 </feedback> 1794 </answer> 1795 <answer fraction="0" format="moodle_auto_format"> 1796 <text>false</text> 1797 <feedback format="html"> 1798 <text></text> 1799 </feedback> 1800 </answer> 1801 </question>'; 1802 $xmldata = xmlize($xml); 1803 1804 $importer = new qformat_xml(); 1805 $q = $importer->import_truefalse($xmldata['question']); 1806 1807 $draftitemid = $q->questiontextitemid; 1808 $files = file_get_drafarea_files($draftitemid, '/myfolder/'); 1809 1810 $this->assertEquals(1, count($files->list)); 1811 1812 $file = $files->list[0]; 1813 $this->assertEquals('moodle.txt', $file->filename); 1814 $this->assertEquals('/myfolder/', $file->filepath); 1815 $this->assertEquals(6, $file->size); 1816 } 1817 1818 public function test_create_dummy_question() { 1819 1820 $testobject = new mock_qformat_xml(); 1821 $categoryname = 'name1'; 1822 $categoryinfo = new \stdClass(); 1823 $categoryinfo->info = 'info1'; 1824 $categoryinfo->infoformat = 'infoformat1'; 1825 $categoryinfo->idnumber = null; 1826 $dummyquestion = $testobject->mock_create_dummy_question_representing_category($categoryname, $categoryinfo); 1827 1828 $this->assertEquals('category', $dummyquestion->qtype); 1829 $this->assertEquals($categoryname, $dummyquestion->category); 1830 $this->assertEquals($categoryinfo->info, $dummyquestion->info); 1831 $this->assertEquals($categoryinfo->infoformat, $dummyquestion->infoformat); 1832 $this->assertEquals('Switch category to ' . $categoryname, $dummyquestion->name); 1833 $this->assertEquals(0, $dummyquestion->id); 1834 $this->assertEquals('', $dummyquestion->questiontextformat); 1835 $this->assertEquals(0, $dummyquestion->contextid); 1836 } 1837 } 1838 1839 /** 1840 * Class mock_qformat_xml exists only to enable testing of the create dummy question category. 1841 * @package qformat_xml 1842 * @copyright 2018 The Open University 1843 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1844 */ 1845 class mock_qformat_xml extends qformat_xml { 1846 /** 1847 * Make public an otherwise protected function. 1848 * @param string $categoryname the name of the category 1849 * @param object $categoryinfo description of the category 1850 */ 1851 public function mock_create_dummy_question_representing_category(string $categoryname, $categoryinfo) { 1852 return $this->create_dummy_question_representing_category($categoryname, $categoryinfo); 1853 } 1854 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body