Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]

   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  }