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 311 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   * This file contains tests for the question_engine class.
  19   *
  20   * @package    moodlecore
  21   * @subpackage questionengine
  22   * @copyright  2009 The Open University
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace core_question;
  27  
  28  use advanced_testcase;
  29  use moodle_exception;
  30  use question_engine;
  31  
  32  /**
  33   * Unit tests for the question_engine class.
  34   *
  35   * @copyright  2009 The Open University
  36   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   * @coversDefaultClass \question_engine
  38   */
  39  class question_engine_test extends advanced_testcase {
  40  
  41      /**
  42       * Load required libraries.
  43       */
  44      public static function setUpBeforeClass(): void {
  45          global $CFG;
  46  
  47          require_once("{$CFG->dirroot}/question/engine/lib.php");
  48      }
  49  
  50      /**
  51       * Tests for load_behaviour_class.
  52       *
  53       * @covers \question_engine::load_behaviour_class
  54       */
  55      public function test_load_behaviour_class(): void {
  56          // Exercise SUT.
  57          question_engine::load_behaviour_class('deferredfeedback');
  58  
  59          // Verify.
  60          $this->assertTrue(class_exists('qbehaviour_deferredfeedback'));
  61      }
  62  
  63      /**
  64       * Tests for load_behaviour_class when a class is missing.
  65       *
  66       * @covers \question_engine::load_behaviour_class
  67       */
  68      public function test_load_behaviour_class_missing(): void {
  69          // Exercise SUT.
  70          $this->expectException(moodle_exception::class);
  71          question_engine::load_behaviour_class('nonexistantbehaviour');
  72      }
  73  
  74      /**
  75       * Test the get_behaviour_unused_display_options with various options.
  76       *
  77       * @covers \question_engine::get_behaviour_unused_display_options
  78       * @dataProvider get_behaviour_unused_display_options_provider
  79       * @param string $behaviour
  80       * @param array $expected
  81       */
  82      public function test_get_behaviour_unused_display_options(string $behaviour, array $expected): void {
  83          $this->assertEquals($expected, question_engine::get_behaviour_unused_display_options($behaviour));
  84      }
  85  
  86      /**
  87       * Data provider for get_behaviour_unused_display_options.
  88       *
  89       * @return array
  90       */
  91      public function get_behaviour_unused_display_options_provider(): array {
  92          return [
  93              'interactive' => [
  94                  'interactive',
  95                  [],
  96              ],
  97              'deferredfeedback' => [
  98                  'deferredfeedback',
  99                  ['correctness', 'marks', 'specificfeedback', 'generalfeedback', 'rightanswer'],
 100              ],
 101              'deferredcbm' => [
 102                  'deferredcbm',
 103                  ['correctness', 'marks', 'specificfeedback', 'generalfeedback', 'rightanswer'],
 104              ],
 105              'manualgraded' => [
 106                  'manualgraded',
 107                  ['correctness', 'marks', 'specificfeedback', 'generalfeedback', 'rightanswer'],
 108              ],
 109          ];
 110      }
 111  
 112      /**
 113       * Tests for can_questions_finish_during_the_attempt.
 114       *
 115       * @covers \question_engine::can_questions_finish_during_the_attempt
 116       * @dataProvider can_questions_finish_during_the_attempt_provider
 117       * @param string $behaviour
 118       * @param bool $expected
 119       */
 120      public function test_can_questions_finish_during_the_attempt(string $behaviour, bool $expected): void {
 121          $this->assertEquals($expected, question_engine::can_questions_finish_during_the_attempt($behaviour));
 122      }
 123  
 124      /**
 125       * Data provider for can_questions_finish_during_the_attempt_provider.
 126       *
 127       * @return array
 128       */
 129      public function can_questions_finish_during_the_attempt_provider(): array {
 130          return [
 131              ['deferredfeedback', false],
 132              ['interactive', true],
 133          ];
 134      }
 135  
 136      /**
 137       * Tests for sort_behaviours
 138       *
 139       * @covers \question_engine::sort_behaviours
 140       * @dataProvider sort_behaviours_provider
 141       * @param array $input The params passed to sort_behaviours
 142       * @param array $expected
 143       */
 144      public function test_sort_behaviours(array $input, array $expected): void {
 145          $this->assertSame($expected, question_engine::sort_behaviours(...$input));
 146      }
 147  
 148      /**
 149       * Data provider for sort_behaviours.
 150       *
 151       * @return array
 152       */
 153      public function sort_behaviours_provider(): array {
 154          $in = [
 155              'b1' => 'Behave 1',
 156              'b2' => 'Behave 2',
 157              'b3' => 'Behave 3',
 158              'b4' => 'Behave 4',
 159              'b5' => 'Behave 5',
 160              'b6' => 'Behave 6',
 161          ];
 162  
 163          return [
 164              [
 165                  [$in, '', '', ''],
 166                  $in,
 167              ],
 168              [
 169                  [$in, '', 'b4', 'b4'],
 170                  $in,
 171              ],
 172              [
 173                  [$in, '', 'b1,b2,b3,b4', 'b4'],
 174                  ['b4' => 'Behave 4', 'b5' => 'Behave 5', 'b6' => 'Behave 6'],
 175              ],
 176              [
 177                  [$in, 'b6,b1,b4', 'b2,b3,b4,b5', 'b4'],
 178                  ['b6' => 'Behave 6', 'b1' => 'Behave 1', 'b4' => 'Behave 4'],
 179              ],
 180              [
 181                  [$in, 'b6,b5,b4', 'b1,b2,b3', 'b4'],
 182                  ['b6' => 'Behave 6', 'b5' => 'Behave 5', 'b4' => 'Behave 4'],
 183              ],
 184              [
 185                  [$in, 'b1,b6,b5', 'b1,b2,b3,b4', 'b4'],
 186                  ['b6' => 'Behave 6', 'b5' => 'Behave 5', 'b4' => 'Behave 4'],
 187              ],
 188              [
 189                  [$in, 'b2,b4,b6', 'b1,b3,b5', 'b2'],
 190                  ['b2' => 'Behave 2', 'b4' => 'Behave 4', 'b6' => 'Behave 6'],
 191              ],
 192              // Ignore unknown input in the order argument.
 193              [
 194                  [$in, 'unknown', '', ''],
 195                  $in,
 196              ],
 197              // Ignore unknown input in the disabled argument.
 198              [
 199                  [$in, '', 'unknown', ''],
 200                  $in,
 201              ],
 202          ];
 203      }
 204  
 205      /**
 206       * Tests for is_manual_grade_in_range.
 207       *
 208       * @dataProvider is_manual_grade_in_range_provider
 209       * @covers \question_engine::is_manual_grade_in_range
 210       * @param array $post The values to add to $_POST
 211       * @param array $params The params to pass to is_manual_grade_in_range
 212       * @param bool $expected
 213       */
 214      public function test_is_manual_grade_in_range(array $post, array $params, bool $expected): void {
 215          $_POST[] = $post;
 216          $this->assertEquals($expected, question_engine::is_manual_grade_in_range(...$params));
 217      }
 218  
 219      /**
 220       * Data provider for is_manual_grade_in_range tests.
 221       *
 222       * @return array
 223       */
 224      public function is_manual_grade_in_range_provider(): array {
 225          return [
 226              'In range' => [
 227                  'post' => [
 228                      'q1:2_-mark' => 0.5,
 229                      'q1:2_-maxmark' => 1.0,
 230                      'q1:2_:minfraction' => 0,
 231                      'q1:2_:maxfraction' => 1,
 232                  ],
 233                  'range' => [1, 2],
 234                  'expected' => true,
 235              ],
 236              'Bottom end' => [
 237                  'post' => [
 238                      'q1:2_-mark' => -1.0,
 239                      'q1:2_-maxmark' => 2.0,
 240                      'q1:2_:minfraction' => -0.5,
 241                      'q1:2_:maxfraction' => 1,
 242                  ],
 243                  'range' => [1, 2],
 244                  'expected' => true,
 245              ],
 246              'Too low' => [
 247                  'post' => [
 248                      'q1:2_-mark' => -1.1,
 249                      'q1:2_-maxmark' => 2.0,
 250                      'q1:2_:minfraction' => -0.5,
 251                      'q1:2_:maxfraction' => 1,
 252                  ],
 253                  'range' => [1, 2],
 254                  'expected' => true,
 255              ],
 256              'Top end' => [
 257                  'post' => [
 258                      'q1:2_-mark' => 3.0,
 259                      'q1:2_-maxmark' => 1.0,
 260                      'q1:2_:minfraction' => -6.0,
 261                      'q1:2_:maxfraction' => 3.0,
 262                  ],
 263                  'range' => [1, 2],
 264                  'expected' => true,
 265              ],
 266              'Too high' => [
 267                  'post' => [
 268                      'q1:2_-mark' => 3.1,
 269                      'q1:2_-maxmark' => 1.0,
 270                      'q1:2_:minfraction' => -6.0,
 271                      'q1:2_:maxfraction' => 3.0,
 272                  ],
 273                  'range' => [1, 2],
 274                  'expected' => true,
 275              ],
 276          ];
 277      }
 278  
 279      /**
 280       * Tests for is_manual_grade_in_range.
 281       *
 282       * @covers \question_engine::is_manual_grade_in_range
 283       */
 284      public function test_is_manual_grade_in_range_ungraded(): void {
 285          $this->assertTrue(question_engine::is_manual_grade_in_range(1, 2));
 286      }
 287  
 288      /**
 289       * Ensure that the number renderer performs as expected.
 290       *
 291       * @covers \core_question_renderer::number
 292       * @dataProvider render_question_number_provider
 293       * @param mixed $value
 294       * @param string $expected
 295       */
 296      public function test_render_question_number($value, string $expected): void {
 297          global $PAGE;
 298  
 299          $renderer = new \core_question_renderer($PAGE, 'core_question');
 300          $rc = new \ReflectionClass($renderer);
 301          $rcm = $rc->getMethod('number');
 302          $rcm->setAccessible(true);
 303  
 304          $this->assertEquals($expected, $rcm->invoke($renderer, $value));
 305      }
 306  
 307      /**
 308       * Data provider for test_render_question_number.
 309       *
 310       * @return array
 311       */
 312      public function render_question_number_provider(): array {
 313          return [
 314              'Test with number is i character' => [
 315                  'i',
 316                  '<h3 class="no">Information</h3>',
 317              ],
 318              'Test with number is empty string' => [
 319                  '',
 320                  '',
 321              ],
 322              'Test with null' => [
 323                  null,
 324                  '',
 325              ],
 326              'Test with number is 0' => [
 327                  0,
 328                  '<h3 class="no">Question <span class="qno">0</span></h3>',
 329              ],
 330              'Test with number is numeric' => [
 331                  1,
 332                  '<h3 class="no">Question <span class="qno">1</span></h3>',
 333              ],
 334              'Test with number is string' => [
 335                  '1 of 2',
 336                  '<h3 class="no">Question <span class="qno">1 of 2</span></h3>',
 337              ],
 338          ];
 339      }
 340  }