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 39 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  namespace qtype_numerical;
  18  
  19  use qtype_numerical;
  20  use qtype_numerical_answer_processor;
  21  use question_attempt_step;
  22  use question_classified_response;
  23  use question_display_options;
  24  use question_state;
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
  30  
  31  
  32  /**
  33   * Unit tests for the numerical question definition class.
  34   *
  35   * @package qtype_numerical
  36   * @copyright 2008 The Open University
  37   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class question_test extends \advanced_testcase {
  40      public function test_is_complete_response() {
  41          $question = \test_question_maker::make_question('numerical');
  42  
  43          $this->assertFalse($question->is_complete_response(array()));
  44          $this->assertTrue($question->is_complete_response(array('answer' => '0')));
  45          $this->assertTrue($question->is_complete_response(array('answer' => 0)));
  46          $this->assertFalse($question->is_complete_response(array('answer' => 'test')));
  47      }
  48  
  49      public function test_is_gradable_response() {
  50          $question = \test_question_maker::make_question('numerical');
  51  
  52          $this->assertFalse($question->is_gradable_response(array()));
  53          $this->assertTrue($question->is_gradable_response(array('answer' => '0')));
  54          $this->assertTrue($question->is_gradable_response(array('answer' => 0)));
  55          $this->assertTrue($question->is_gradable_response(array('answer' => 'test')));
  56      }
  57  
  58      public function test_grading() {
  59          $question = \test_question_maker::make_question('numerical');
  60  
  61          $this->assertEquals(array(0, question_state::$gradedwrong),
  62                  $question->grade_response(array('answer' => '1.0')));
  63          $this->assertEquals(array(1, question_state::$gradedright),
  64                  $question->grade_response(array('answer' => '3.14')));
  65      }
  66  
  67      public function test_grading_with_units() {
  68          $question = \test_question_maker::make_question('numerical');
  69          $question->unitgradingtype = qtype_numerical::UNITOPTIONAL;
  70          $question->ap = new qtype_numerical_answer_processor(
  71                  array('m' => 1, 'cm' => 100), false, '.', ',');
  72  
  73          $this->assertEquals(array(1, question_state::$gradedright),
  74                  $question->grade_response(array('answer' => '3.14 frogs')));
  75          $this->assertEquals(array(1, question_state::$gradedright),
  76                  $question->grade_response(array('answer' => '3.14')));
  77          $this->assertEquals(array(1, question_state::$gradedright),
  78                  $question->grade_response(array('answer' => '3.14 m')));
  79          $this->assertEquals(array(1, question_state::$gradedright),
  80                  $question->grade_response(array('answer' => '314cm')));
  81          $this->assertEquals(array(1, question_state::$gradedright),
  82                  $question->grade_response(array('answer' => '314000000x10^-8m')));
  83      }
  84  
  85      public function test_grading_with_units_graded() {
  86          $question = \test_question_maker::make_question('numerical');
  87          $question->unitgradingtype = qtype_numerical::UNITGRADED;
  88          $question->ap = new qtype_numerical_answer_processor(
  89                  array('m' => 1, 'cm' => 100), false, '.', ',');
  90  
  91          $this->assertEquals(array(0.8, question_state::$gradedpartial),
  92                  $question->grade_response(array('answer' => '3.14 frogs')));
  93          $this->assertEquals(array(0.8, question_state::$gradedpartial),
  94                  $question->grade_response(array('answer' => '3.14')));
  95          $this->assertEquals(array(1, question_state::$gradedright),
  96                  $question->grade_response(array('answer' => '3.14 m')));
  97          $this->assertEquals(array(1, question_state::$gradedright),
  98                  $question->grade_response(array('answer' => '314cm')));
  99          $this->assertEquals(array(1, question_state::$gradedright),
 100                  $question->grade_response(array('answer' => '314000000x10^-8m')));
 101          $this->assertEquals(array(0.8, question_state::$gradedpartial),
 102                  $question->grade_response(array('answer' => '3.14 cm')));
 103          $this->assertEquals(array(0, question_state::$gradedwrong),
 104                  $question->grade_response(array('answer' => '314 m')));
 105      }
 106  
 107      public function test_grading_unit() {
 108          $question = \test_question_maker::make_question('numerical', 'unit');
 109  
 110          $this->assertEquals(array(0, question_state::$gradedwrong),
 111                  $question->grade_response(array('answer' => '2', 'unit' => 'm')));
 112          $this->assertEquals(array(0, question_state::$gradedwrong),
 113                  $question->grade_response(array('answer' => '2', 'unit' => 'cm')));
 114          $this->assertEquals(array(0, question_state::$gradedwrong),
 115                  $question->grade_response(array('answer' => '2', 'unit' => '')));
 116  
 117          $this->assertEquals(array(1, question_state::$gradedright),
 118                  $question->grade_response(array('answer' => '1.25', 'unit' => 'm')));
 119          $this->assertEquals(array(1, question_state::$gradedright),
 120                  $question->grade_response(array('answer' => '125', 'unit' => 'cm')));
 121          $this->assertEquals(array(0.5, question_state::$gradedpartial),
 122                  $question->grade_response(array('answer' => '1.25', 'unit' => '')));
 123  
 124          $this->assertEquals(array(0.5, question_state::$gradedpartial),
 125                  $question->grade_response(array('answer' => '1.23', 'unit' => 'm')));
 126          $this->assertEquals(array(0.5, question_state::$gradedpartial),
 127                  $question->grade_response(array('answer' => '123', 'unit' => 'cm')));
 128          $this->assertEquals(array(0.25, question_state::$gradedpartial),
 129                  $question->grade_response(array('answer' => '1.23', 'unit' => '')));
 130      }
 131  
 132      public function test_grading_currency() {
 133          $question = \test_question_maker::make_question('numerical', 'currency');
 134  
 135          $this->assertEquals(array(1, question_state::$gradedright),
 136                  $question->grade_response(array('answer' => '$1332')));
 137          $this->assertEquals(array(1, question_state::$gradedright),
 138                  $question->grade_response(array('answer' => '$ 1332')));
 139          $this->assertEquals(array(0.8, question_state::$gradedpartial),
 140                  $question->grade_response(array('answer' => 'frog 1332')));
 141          $this->assertEquals(array(0.8, question_state::$gradedpartial),
 142                  $question->grade_response(array('answer' => '1332')));
 143          $this->assertEquals(array(0.8, question_state::$gradedpartial),
 144                  $question->grade_response(array('answer' => ' 1332')));
 145          $this->assertEquals(array(0, question_state::$gradedwrong),
 146                  $question->grade_response(array('answer' => '1332 $')));
 147          $this->assertEquals(array(0, question_state::$gradedwrong),
 148                  $question->grade_response(array('answer' => '1332 frogs')));
 149          $this->assertEquals(array(0, question_state::$gradedwrong),
 150                  $question->grade_response(array('answer' => '$1')));
 151      }
 152  
 153      public function test_get_correct_response() {
 154          $question = \test_question_maker::make_question('numerical');
 155  
 156          $this->assertEquals(array('answer' => '3.14'),
 157                  $question->get_correct_response());
 158      }
 159  
 160      public function test_get_correct_response_units() {
 161          $question = \test_question_maker::make_question('numerical', 'unit');
 162  
 163          $this->assertEquals(array('answer' => '1.25', 'unit' => 'm'),
 164                  $question->get_correct_response());
 165      }
 166  
 167      public function test_get_correct_response_currency() {
 168          $question = \test_question_maker::make_question('numerical', 'currency');
 169  
 170          $this->assertEquals(array('answer' => '$ 1332'),
 171                  $question->get_correct_response());
 172      }
 173  
 174      public function test_get_question_summary() {
 175          $num = \test_question_maker::make_question('numerical');
 176          $qsummary = $num->get_question_summary();
 177          $this->assertEquals('What is pi to two d.p.?', $qsummary);
 178      }
 179  
 180      public function test_summarise_response() {
 181          $num = \test_question_maker::make_question('numerical');
 182          $this->assertEquals('3.1', $num->summarise_response(array('answer' => '3.1')));
 183      }
 184  
 185      public function test_summarise_response_zero() {
 186          $num = \test_question_maker::make_question('numerical');
 187          $this->assertEquals('0', $num->summarise_response(array('answer' => '0')));
 188      }
 189  
 190      public function test_summarise_response_unit() {
 191          $num = \test_question_maker::make_question('numerical', 'unit');
 192          $this->assertEquals('3.1', $num->summarise_response(array('answer' => '3.1')));
 193          $this->assertEquals('3.1m', $num->summarise_response(array('answer' => '3.1m')));
 194          $this->assertEquals('3.1 cm', $num->summarise_response(array('answer' => '3.1 cm')));
 195      }
 196  
 197      public function test_summarise_response_currency() {
 198          $num = \test_question_maker::make_question('numerical', 'currency');
 199          $this->assertEquals('100', $num->summarise_response(array('answer' => '100')));
 200          $this->assertEquals('$100', $num->summarise_response(array('answer' => '$100')));
 201          $this->assertEquals('$ 100', $num->summarise_response(array('answer' => '$ 100')));
 202          $this->assertEquals('100 frogs', $num->summarise_response(array('answer' => '100 frogs')));
 203      }
 204  
 205      public function test_classify_response() {
 206          $num = \test_question_maker::make_question('numerical');
 207          $num->start_attempt(new question_attempt_step(), 1);
 208  
 209          $this->assertEquals(array(
 210                  new question_classified_response(15, '3.1', 0.0)),
 211                  $num->classify_response(array('answer' => '3.1')));
 212          $this->assertEquals(array(
 213                  new question_classified_response(17, '42', 0.0)),
 214                  $num->classify_response(array('answer' => '42')));
 215          $this->assertEquals(array(
 216                  new question_classified_response(13, '3.14', 1.0)),
 217                  $num->classify_response(array('answer' => '3.14')));
 218          // Invalid response.
 219          $this->assertEquals(array(
 220                  new question_classified_response(null, 'abc', 0.0)),
 221                  $num->classify_response(array('answer' => 'abc')));
 222      }
 223  
 224      public function test_classify_response_no_star() {
 225          $num = \test_question_maker::make_question('numerical');
 226          unset($num->answers[17]);
 227          $num->start_attempt(new question_attempt_step(), 1);
 228  
 229          $this->assertEquals(array(
 230                  new question_classified_response(15, '3.1', 0.0)),
 231                  $num->classify_response(array('answer' => '3.1')));
 232          $this->assertEquals(array(
 233                  new question_classified_response(0, '42', 0.0)),
 234                  $num->classify_response(array('answer' => '42')));
 235          // Invalid response.
 236          $this->assertEquals(array(
 237                  new question_classified_response(null, 'abc', 0.0)),
 238                  $num->classify_response(array('answer' => 'abc')));
 239      }
 240  
 241      public function test_classify_response_unit() {
 242          $num = \test_question_maker::make_question('numerical', 'unit');
 243          $num->start_attempt(new question_attempt_step(), 1);
 244  
 245          $this->assertEquals(array(
 246                  new question_classified_response(13, '1.25', 0.5)),
 247                  $num->classify_response(array('answer' => '1.25', 'unit' => '')));
 248          $this->assertEquals(array(
 249                  new question_classified_response(13, '1.25 m', 1.0)),
 250                  $num->classify_response(array('answer' => '1.25', 'unit' => 'm')));
 251          $this->assertEquals(array(
 252                  new question_classified_response(13, '125 cm', 1.0)),
 253                  $num->classify_response(array('answer' => '125', 'unit' => 'cm')));
 254          $this->assertEquals(array(
 255                  new question_classified_response(14, '123 cm', 0.5)),
 256                  $num->classify_response(array('answer' => '123', 'unit' => 'cm')));
 257          $this->assertEquals(array(
 258                  new question_classified_response(14, '1.27 m', 0.5)),
 259                  $num->classify_response(array('answer' => '1.27', 'unit' => 'm')));
 260          $this->assertEquals(array(
 261                  new question_classified_response(17, '3.0 m', 0)),
 262                  $num->classify_response(array('answer' => '3.0', 'unit' => 'm')));
 263          $this->assertEquals(array(
 264                  question_classified_response::no_response()),
 265                  $num->classify_response(array('answer' => '')));
 266          // Invalid response.
 267          $this->assertEquals(array(
 268                  new question_classified_response(null, 'abc m', 0.0)),
 269                  $num->classify_response(array('answer' => 'abc', 'unit' => 'm')));
 270      }
 271  
 272      public function test_classify_response_unit_no_star() {
 273          $num = \test_question_maker::make_question('numerical', 'unit');
 274          unset($num->answers[17]);
 275          $num->start_attempt(new question_attempt_step(), 1);
 276  
 277          $this->assertEquals(array(
 278                  new question_classified_response(0, '42 cm', 0)),
 279                  $num->classify_response(array('answer' => '42', 'unit' => 'cm')));
 280          $this->assertEquals(array(
 281                  new question_classified_response(0, '3.0', 0)),
 282                  $num->classify_response(array('answer' => '3.0', 'unit' => '')));
 283          $this->assertEquals(array(
 284                  new question_classified_response(0, '3.0 m', 0)),
 285                  $num->classify_response(array('answer' => '3.0', 'unit' => 'm')));
 286          $this->assertEquals(array(
 287                  question_classified_response::no_response()),
 288                  $num->classify_response(array('answer' => '', 'unit' => '')));
 289          // Invalid response.
 290          $this->assertEquals(array(
 291                              new question_classified_response(null, 'abc m', 0.0)),
 292                              $num->classify_response(array('answer' => 'abc', 'unit' => 'm')));
 293      }
 294  
 295      public function test_classify_response_currency() {
 296          $num = \test_question_maker::make_question('numerical', 'currency');
 297          $num->start_attempt(new question_attempt_step(), 1);
 298  
 299          $this->assertEquals(array(
 300                  new question_classified_response(14, '$100', 0)),
 301                  $num->classify_response(array('answer' => '$100')));
 302          $this->assertEquals(array(
 303                  new question_classified_response(13, '1 332', 0.8)),
 304                  $num->classify_response(array('answer' => '1 332')));
 305          // Invalid response.
 306          $this->assertEquals(array(
 307                  new question_classified_response(null, '$abc', 0.0)),
 308                  $num->classify_response(array('answer' => '$abc')));
 309      }
 310  
 311      /**
 312       * test_get_question_definition_for_external_rendering
 313       */
 314      public function test_get_question_definition_for_external_rendering() {
 315          $this->resetAfterTest();
 316  
 317          $question = \test_question_maker::make_question('numerical', 'unit');
 318          $question->start_attempt(new question_attempt_step(), 1);
 319          $qa = \test_question_maker::get_a_qa($question);
 320          $displayoptions = new question_display_options();
 321  
 322          $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
 323          $this->assertNotEmpty($options);
 324          $this->assertEquals(1, $options['unitgradingtype']);
 325          $this->assertEquals(0.5, $options['unitpenalty']);
 326          $this->assertEquals(qtype_numerical::UNITSELECT, $options['unitdisplay']);
 327          $this->assertEmpty($options['unitsleft']);
 328      }
 329  }