Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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   * Test helpers for the multianswer question type.
  19   *
  20   * @package    qtype_multianswer
  21   * @copyright  2013 The Open University
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  require_once($CFG->dirroot . '/question/type/multianswer/question.php');
  30  
  31  
  32  /**
  33   * Test helper class for the multianswer question type.
  34   *
  35   * @copyright  2013 The Open University
  36   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class qtype_multianswer_test_helper extends question_test_helper {
  39      public function get_test_questions() {
  40          return array('twosubq', 'fourmc', 'numericalzero', 'dollarsigns', 'multiple');
  41      }
  42  
  43      /**
  44       * Makes a multianswer question about completing two blanks in some text.
  45       * @return qtype_multianswer_question
  46       */
  47      public function make_multianswer_question_twosubq() {
  48          question_bank::load_question_definition_classes('multianswer');
  49          $q = new qtype_multianswer_question();
  50          test_question_maker::initialise_a_question($q);
  51          $q->name = 'Simple multianswer';
  52          $q->questiontext =
  53                  'Complete this opening line of verse: "The {#1} and the {#2} went to sea".';
  54          $q->generalfeedback = 'General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: ' .
  55                  '"The owl and the pussycat went to sea';
  56          $q->qtype = question_bank::get_qtype('multianswer');
  57  
  58          $q->textfragments = array(
  59              'Complete this opening line of verse: "The ',
  60              ' and the ',
  61              ' went to sea".',
  62          );
  63          $q->places = array('1' => '1', '2' => '2');
  64  
  65          // Shortanswer subquestion.
  66          question_bank::load_question_definition_classes('shortanswer');
  67          $sa = new qtype_shortanswer_question();
  68          test_question_maker::initialise_a_question($sa);
  69          $sa->name = 'Simple multianswer';
  70          $sa->questiontext = '{1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer}';
  71          $sa->questiontextformat = FORMAT_HTML;
  72          $sa->generalfeedback = '';
  73          $sa->generalfeedbackformat = FORMAT_HTML;
  74          $sa->usecase = true;
  75          $sa->answers = array(
  76              13 => new question_answer(13, 'Dog', 0.0, 'Wrong, silly!', FORMAT_HTML),
  77              14 => new question_answer(14, 'Owl', 1.0, 'Well done!', FORMAT_HTML),
  78              15 => new question_answer(15, '*', 0.0, 'Wrong answer', FORMAT_HTML),
  79          );
  80          $sa->qtype = question_bank::get_qtype('shortanswer');
  81          $sa->maxmark = 1;
  82  
  83          // Multiple-choice subquestion.
  84          question_bank::load_question_definition_classes('multichoice');
  85          $mc = new qtype_multichoice_single_question();
  86          test_question_maker::initialise_a_question($mc);
  87          $mc->name = 'Simple multianswer';
  88          $mc->questiontext = '{1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!' .
  89                  '~Wiggly worm#Now you are just being rediculous!~=Pussy-cat#Well done!}';
  90          $mc->questiontextformat = FORMAT_HTML;
  91          $mc->generalfeedback = '';
  92          $mc->generalfeedbackformat = FORMAT_HTML;
  93  
  94          $mc->shuffleanswers = 0;
  95          $mc->answernumbering = 'none';
  96          $mc->layout = qtype_multichoice_base::LAYOUT_DROPDOWN;
  97  
  98          $mc->answers = array(
  99              13 => new question_answer(13, 'Bow-wow', 0,
 100                      'You seem to have a dog obsessions!', FORMAT_HTML),
 101              14 => new question_answer(14, 'Wiggly worm', 0,
 102                      'Now you are just being rediculous!', FORMAT_HTML),
 103              15 => new question_answer(15, 'Pussy-cat', 1,
 104                      'Well done!', FORMAT_HTML),
 105          );
 106          $mc->qtype = question_bank::get_qtype('multichoice');
 107          $mc->maxmark = 1;
 108  
 109          $q->subquestions = array(
 110              1 => $sa,
 111              2 => $mc,
 112          );
 113  
 114          return $q;
 115      }
 116  
 117      /**
 118       * Makes a multianswer question about completing two blanks in some text.
 119       * @return object the question definition data, as it might be returned from
 120       * get_question_options.
 121       */
 122      public function get_multianswer_question_data_twosubq() {
 123          $qdata = new stdClass();
 124          test_question_maker::initialise_question_data($qdata);
 125  
 126          $qdata->name = 'Simple multianswer';
 127          $qdata->questiontext =
 128                          'Complete this opening line of verse: "The {#1} and the {#2} went to sea".';
 129          $qdata->generalfeedback = 'General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: ' .
 130                          '"The owl and the pussycat went to sea';
 131  
 132          $qdata->defaultmark = 2.0;
 133          $qdata->qtype = 'multianswer';
 134  
 135          $sa = new stdClass();
 136          test_question_maker::initialise_question_data($sa);
 137  
 138          $sa->name = 'Simple multianswer';
 139          $sa->questiontext = '{1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer}';
 140          $sa->generalfeedback = '';
 141          $sa->penalty = 0.0;
 142          $sa->qtype = 'shortanswer';
 143  
 144          $sa->options = new stdClass();
 145          $sa->options->usecase = 0;
 146  
 147          $sa->options->answers = array(
 148              13 => new question_answer(13, 'Dog', 0, 'Wrong, silly!', FORMAT_HTML),
 149              14 => new question_answer(14, 'Owl', 1, 'Well done!',    FORMAT_HTML),
 150              15 => new question_answer(15, '*',   0, 'Wrong answer',  FORMAT_HTML),
 151          );
 152  
 153          $mc = new stdClass();
 154          test_question_maker::initialise_question_data($mc);
 155  
 156          $mc->name = 'Simple multianswer';
 157          $mc->questiontext = '{1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~' .
 158                  'Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!}';
 159          $mc->generalfeedback = '';
 160          $mc->penalty = 0.0;
 161          $mc->qtype = 'multichoice';
 162  
 163          $mc->options = new stdClass();
 164          $mc->options->layout = 0;
 165          $mc->options->single = 1;
 166          $mc->options->shuffleanswers = 0;
 167          $mc->options->correctfeedback = '';
 168          $mc->options->correctfeedbackformat = 1;
 169          $mc->options->partiallycorrectfeedback = '';
 170          $mc->options->partiallycorrectfeedbackformat = 1;
 171          $mc->options->incorrectfeedback = '';
 172          $mc->options->incorrectfeedbackformat = 1;
 173          $mc->options->answernumbering = 0;
 174          $mc->options->shownumcorrect = 0;
 175  
 176          $mc->options->answers = array(
 177              23 => new question_answer(23, 'Bow-wow',     0,
 178                      'You seem to have a dog obsessions!', FORMAT_HTML),
 179              24 => new question_answer(24, 'Wiggly worm', 0,
 180                      'Now you are just being ridiculous!', FORMAT_HTML),
 181              25 => new question_answer(25, 'Pussy-cat',   1,
 182                      'Well done!',                         FORMAT_HTML),
 183          );
 184  
 185          $qdata->options = new stdClass();
 186          $qdata->options->questions = array(
 187              1 => $sa,
 188              2 => $mc,
 189          );
 190  
 191          $qdata->hints = array(
 192              new question_hint_with_parts(0, 'Hint 1', FORMAT_HTML, 0, 0),
 193              new question_hint_with_parts(0, 'Hint 2', FORMAT_HTML, 0, 0),
 194          );
 195  
 196          return $qdata;
 197      }
 198  
 199      /**
 200       * Makes a multianswer question onetaining one blank in some text.
 201       * This question has no hints.
 202       *
 203       * @return object the question definition data, as it might be returned from
 204       * get_question_options.
 205       */
 206      public function get_multianswer_question_data_dollarsigns() {
 207          $qdata = new stdClass();
 208          test_question_maker::initialise_question_data($qdata);
 209  
 210          $qdata->name = 'Multianswer with $s';
 211          $qdata->questiontext =
 212                          'Which is the right order? {#1}';
 213          $qdata->generalfeedback = '';
 214  
 215          $qdata->defaultmark = 1.0;
 216          $qdata->qtype = 'multianswer';
 217  
 218          $mc = new stdClass();
 219          test_question_maker::initialise_question_data($mc);
 220  
 221          $mc->name = 'Multianswer with $s';
 222          $mc->questiontext = '{1:MULTICHOICE:=y,y,$3~$3,y,y}';
 223          $mc->generalfeedback = '';
 224          $mc->penalty = 0.0;
 225          $mc->qtype = 'multichoice';
 226  
 227          $mc->options = new stdClass();
 228          $mc->options->layout = 0;
 229          $mc->options->single = 1;
 230          $mc->options->shuffleanswers = 0;
 231          $mc->options->correctfeedback = '';
 232          $mc->options->correctfeedbackformat = 1;
 233          $mc->options->partiallycorrectfeedback = '';
 234          $mc->options->partiallycorrectfeedbackformat = 1;
 235          $mc->options->incorrectfeedback = '';
 236          $mc->options->incorrectfeedbackformat = 1;
 237          $mc->options->answernumbering = 0;
 238          $mc->options->shownumcorrect = 0;
 239  
 240          $mc->options->answers = array(
 241              23 => new question_answer(23, 'y,y,$3', 0, '', FORMAT_HTML),
 242              24 => new question_answer(24, '$3,y,y', 0, '', FORMAT_HTML),
 243          );
 244  
 245          $qdata->options = new stdClass();
 246          $qdata->options->questions = array(
 247              1 => $mc,
 248          );
 249  
 250          $qdata->hints = array(
 251          );
 252  
 253          return $qdata;
 254      }
 255  
 256      /**
 257       * Makes a multianswer question about completing two blanks in some text.
 258       * @return object the question definition data, as it might be returned from
 259       *      the question editing form.
 260       */
 261      public function get_multianswer_question_form_data_twosubq() {
 262          $formdata = new stdClass();
 263          $formdata->name = 'Simple multianswer';
 264          $formdata->questiontext = array('text' => 'Complete this opening line of verse: "The ' .
 265                  '{1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer} ' .
 266                  'and the {1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!' .
 267                  '~Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!}' .
 268                  ' went to sea".', 'format' => FORMAT_HTML);
 269          $formdata->generalfeedback = array('text' => 'General feedback: It\'s from "The Owl and the Pussy-cat" ' .
 270                  'by Lear: "The owl and the pussycat went to sea', 'format' => FORMAT_HTML);
 271  
 272          $formdata->hint = array(
 273              0 => array('text' => 'Hint 1', 'format' => FORMAT_HTML, 'itemid' => 0),
 274              1 => array('text' => 'Hint 2', 'format' => FORMAT_HTML, 'itemid' => 0),
 275          );
 276  
 277          return $formdata;
 278      }
 279  
 280      /**
 281       * Makes a multianswer question about completing two blanks in some text.
 282       * @return qtype_multianswer_question
 283       */
 284      public function make_multianswer_question_fourmc() {
 285          question_bank::load_question_definition_classes('multianswer');
 286          $q = new qtype_multianswer_question();
 287          test_question_maker::initialise_a_question($q);
 288          $q->name = 'Multianswer four multi-choice';
 289          $q->questiontext = '<p>Match the following cities with the correct state:</p>
 290                  <ul>
 291                  <li>San Francisco: {#1}</li>
 292                  <li>Tucson: {#2}</li>
 293                  <li>Los Angeles: {#3}</li>
 294                  <li>Phoenix: {#4}</li>
 295                  </ul>';
 296          $q->questiontextformat = FORMAT_HTML;
 297          $q->generalfeedback = '';
 298          $q->qtype = question_bank::get_qtype('multianswer');
 299  
 300          $q->textfragments = array('<p>Match the following cities with the correct state:</p>
 301                  <ul>
 302                  <li>San Francisco: ', '</li>
 303                  <li>Tucson: ', '</li>
 304                  <li>Los Angeles: ', '</li>
 305                  <li>Phoenix: ', '</li>
 306                  </ul>');
 307          $q->places = array('1' => '1', '2' => '2', '3' => '3', '4' => '4');
 308  
 309          $subqdata = array(
 310              1 => array('qt' => '{1:MULTICHOICE:=California#OK~Arizona#Wrong}', 'California' => 'OK', 'Arizona' => 'Wrong'),
 311              2 => array('qt' => '{1:MULTICHOICE:%0%California#Wrong~=Arizona#OK}', 'California' => 'Wrong', 'Arizona' => 'OK'),
 312              3 => array('qt' => '{1:MULTICHOICE:=California#OK~Arizona#Wrong}', 'California' => 'OK', 'Arizona' => 'Wrong'),
 313              4 => array('qt' => '{1:MULTICHOICE:%0%California#Wrong~=Arizona#OK}', 'California' => 'Wrong', 'Arizona' => 'OK'),
 314          );
 315          foreach ($subqdata as $i => $data) {
 316                  // Multiple-choice subquestion.
 317              question_bank::load_question_definition_classes('multichoice');
 318              $mc = new qtype_multichoice_single_question();
 319              test_question_maker::initialise_a_question($mc);
 320              $mc->name = 'Multianswer four multi-choice';
 321              $mc->questiontext = $data['qt'];
 322              $mc->questiontextformat = FORMAT_HTML;
 323              $mc->generalfeedback = '';
 324              $mc->generalfeedbackformat = FORMAT_HTML;
 325  
 326              $mc->shuffleanswers = 0; // TODO this is a cheat to make the unit tests easier to write.
 327              // In reality, multianswer questions always shuffle.
 328              $mc->answernumbering = 'none';
 329              $mc->layout = qtype_multichoice_base::LAYOUT_DROPDOWN;
 330  
 331              $mc->answers = array(
 332                  10 * $i     => new question_answer(13, 'California', (float) ($data['California'] == 'OK'),
 333                          $data['California'], FORMAT_HTML),
 334                  10 * $i + 1 => new question_answer(14, 'Arizona', (float) ($data['Arizona'] == 'OK'),
 335                           $data['Arizona'], FORMAT_HTML),
 336              );
 337              $mc->qtype = question_bank::get_qtype('multichoice');
 338              $mc->maxmark = 1;
 339  
 340              $q->subquestions[$i] = $mc;
 341          }
 342  
 343          return $q;
 344      }
 345  
 346      /**
 347       * Makes a multianswer question with one numerical subquestion, right answer 0.
 348       * This is used for testing the MDL-35370 bug.
 349       * @return qtype_multianswer_question
 350       */
 351      public function make_multianswer_question_numericalzero() {
 352          question_bank::load_question_definition_classes('multianswer');
 353          $q = new qtype_multianswer_question();
 354          test_question_maker::initialise_a_question($q);
 355          $q->name = 'Numerical zero';
 356          $q->questiontext =
 357                  'Enter zero: {#1}.';
 358          $q->generalfeedback = '';
 359          $q->qtype = question_bank::get_qtype('multianswer');
 360  
 361          $q->textfragments = array(
 362              'Enter zero: ',
 363              '.',
 364          );
 365          $q->places = array('1' => '1');
 366  
 367          // Numerical subquestion.
 368          question_bank::load_question_definition_classes('numerical');
 369          $sub = new qtype_numerical_question();
 370          test_question_maker::initialise_a_question($sub);
 371          $sub->name = 'Numerical zero';
 372          $sub->questiontext = '{1:NUMERICAL:=0:0}';
 373          $sub->questiontextformat = FORMAT_HTML;
 374          $sub->generalfeedback = '';
 375          $sub->generalfeedbackformat = FORMAT_HTML;
 376          $sub->answers = array(
 377              13 => new qtype_numerical_answer(13, '0', 1.0, '', FORMAT_HTML, 0),
 378          );
 379          $sub->qtype = question_bank::get_qtype('numerical');
 380          $sub->ap = new qtype_numerical_answer_processor(array());
 381          $sub->maxmark = 1;
 382  
 383          $q->subquestions = array(
 384              1 => $sub,
 385          );
 386  
 387          return $q;
 388      }
 389  
 390      /**
 391       * Makes a multianswer question with multichoice_multiple questions in it.
 392       * @return qtype_multianswer_question
 393       */
 394      public function make_multianswer_question_multiple() {
 395          question_bank::load_question_definition_classes('multianswer');
 396          $q = new qtype_multianswer_question();
 397          test_question_maker::initialise_a_question($q);
 398          $q->name = 'Multichoice multiple';
 399          $q->questiontext = 'Please select the fruits {#1} and vegetables {#2}';
 400          $q->generalfeedback = 'You should know which foods are fruits or vegetables.';
 401          $q->qtype = question_bank::get_qtype('multianswer');
 402  
 403          $q->textfragments = array(
 404              'Please select the fruits ',
 405              ' and vegetables ',
 406              ''
 407          );
 408          $q->places = array('1' => '1', '2' => '2');
 409  
 410          // Multiple-choice subquestion.
 411          question_bank::load_question_definition_classes('multichoice');
 412          $mc = new qtype_multichoice_multi_question();
 413          test_question_maker::initialise_a_question($mc);
 414          $mc->name = 'Multianswer 1';
 415          $mc->questiontext = '{1:MULTIRESPONSE:=Apple#Good~%-50%Burger~%-50%Hot dog#Not a fruit~%-50%Pizza' .
 416              '~=Orange#Correct~=Banana}';
 417          $mc->questiontextformat = FORMAT_HTML;
 418          $mc->generalfeedback = '';
 419          $mc->generalfeedbackformat = FORMAT_HTML;
 420  
 421          $mc->shuffleanswers = 0;
 422          $mc->answernumbering = 'none';
 423          $mc->layout = qtype_multichoice_base::LAYOUT_VERTICAL;
 424          $mc->single = 0;
 425  
 426          $mc->answers = array(
 427              16 => new question_answer(16, 'Apple', 0.3333333,
 428                                        'Good', FORMAT_HTML),
 429              17 => new question_answer(17, 'Burger', -0.5,
 430                                        '', FORMAT_HTML),
 431              18 => new question_answer(18, 'Hot dog', -0.5,
 432                                        'Not a fruit', FORMAT_HTML),
 433              19 => new question_answer(19, 'Pizza', -0.5,
 434                                        '', FORMAT_HTML),
 435              20 => new question_answer(20, 'Orange', 0.3333333,
 436                                        'Correct', FORMAT_HTML),
 437              21 => new question_answer(21, 'Banana', 0.3333333,
 438                                        '', FORMAT_HTML),
 439          );
 440          $mc->qtype = question_bank::get_qtype('multichoice');
 441          $mc->maxmark = 1;
 442  
 443          // Multiple-choice subquestion.
 444          question_bank::load_question_definition_classes('multichoice');
 445          $mc2 = new qtype_multichoice_multi_question();
 446          test_question_maker::initialise_a_question($mc2);
 447          $mc2->name = 'Multichoice 2';
 448          $mc2->questiontext = '{1:MULTIRESPONSE:=Raddish#Good~%-50%Chocolate~%-50%Biscuit#Not a vegetable~%-50%Cheese' .
 449              '~=Carrot#Correct}';
 450          $mc2->questiontextformat = FORMAT_HTML;
 451          $mc2->generalfeedback = '';
 452          $mc2->generalfeedbackformat = FORMAT_HTML;
 453  
 454          $mc2->shuffleanswers = 0;
 455          $mc2->answernumbering = 'none';
 456          $mc2->layout = qtype_multichoice_base::LAYOUT_VERTICAL;
 457          $mc2->single = 0;
 458  
 459          $mc2->answers = array(
 460              22 => new question_answer(22, 'Raddish', 0.5,
 461                                        'Good', FORMAT_HTML),
 462              23 => new question_answer(23, 'Chocolate', -0.5,
 463                                        '', FORMAT_HTML),
 464              24 => new question_answer(24, 'Biscuit', -0.5,
 465                                        'Not a vegetable', FORMAT_HTML),
 466              25 => new question_answer(25, 'Cheese', -0.5,
 467                                        '', FORMAT_HTML),
 468              26 => new question_answer(26, 'Carrot', 0.5,
 469                                        'Correct', FORMAT_HTML),
 470          );
 471          $mc2->qtype = question_bank::get_qtype('multichoice');
 472          $mc2->maxmark = 1;
 473  
 474          $q->subquestions = array(
 475              1 => $mc,
 476              2 => $mc2,
 477          );
 478  
 479          return $q;
 480      }
 481  
 482  }