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 numerical questions answers processor.
  19   *
  20   * @package    qtype_numerical
  21   * @category   test
  22   * @copyright  2008 The Open University
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace qtype_numerical;
  27  
  28  use qtype_numerical_answer_processor;
  29  
  30  /**
  31   * Unit test for the numerical questions answers processor.
  32   *
  33   * @package    qtype_numerical
  34   * @category   test
  35   * @copyright  2008 The Open University
  36   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   * @covers     \qtype_numerical_answer_processor
  38   */
  39  class answerprocessor_test extends \advanced_testcase {
  40      /**
  41       * Test setup.
  42       */
  43      public function setUp(): void {
  44          global $CFG;
  45  
  46          require_once("{$CFG->dirroot}/question/type/numerical/questiontype.php");
  47      }
  48  
  49      /**
  50       * Test the parse_response function.
  51       *
  52       * @covers ::parse_response
  53       * @dataProvider parse_response_provider
  54       * @param array $expected
  55       * @param mixed $args
  56       */
  57      public function test_parse_response(array $expected, $args): void {
  58          $ap = new qtype_numerical_answer_processor([
  59              'm' => 1,
  60              'cm' => 100,
  61          ], false, '.', ',');
  62  
  63          $rc = new \ReflectionClass($ap);
  64          $rcm = $rc->getMethod('parse_response');
  65          $rcm->setAccessible(true);
  66  
  67          $this->assertEquals($expected, $rcm->invoke($ap, $args));
  68      }
  69  
  70      /**
  71       * Data provider for the parse_response function.
  72       *
  73       * @return array
  74       */
  75      public function parse_response_provider(): array {
  76          return [
  77              [['3', '142', '', ''], '3.142'],
  78              [['', '2', '', ''], '.2'],
  79              [['1', '', '', ''], '1.'],
  80              [['1', '0', '', ''], '1.0'],
  81              [['-1', '', '', ''], '-1.'],
  82              [['+1', '0', '', ''], '+1.0'],
  83  
  84              [['1', '', '4', ''], '1e4'],
  85              [['3', '142', '-4', ''], '3.142E-4'],
  86              [['', '2', '+2', ''], '.2e+2'],
  87              [['1', '', '-1', ''], '1.e-1'],
  88              [['1', '0', '0', ''], '1.0e0'],
  89  
  90              [['3', '', '8', ''], '3x10^8'],
  91              [['3', '', '8', ''], '3×10^8'],
  92              [['3', '0', '8', ''], '3.0*10^8'],
  93              [['3', '00', '-8', ''], '3.00x10**-8'],
  94              [['0', '001', '7', ''], '0.001×10**7'],
  95  
  96              [['1', '', '', 'm'], '1m'],
  97              [['3', '142', '', 'm'], '3.142 m'],
  98              [['', '2', '', 'm'], '.2m'],
  99              [['1', '', '', 'cm'], '1.cm'],
 100              [['1', '0', '', 'cm'], '1.0   cm'],
 101              [['-1', '', '', 'm'], '-1.m'],
 102              [['+1', '0', '', 'cm'], '+1.0cm'],
 103  
 104              [['1', '', '4', 'm'], '1e4 m'],
 105              [['3', '142', '-4', 'cm'], '3.142E-4  cm'],
 106              [['', '2', '+2', 'm'], '.2e+2m'],
 107              [['1', '', '-1', 'm'], '1.e-1 m'],
 108              [['1', '0', '0', 'cm'], '1.0e0cm'],
 109  
 110              [['1000000', '', '', ''], '1,000,000'],
 111              [['1000', '00', '', 'm'], '1,000.00 m'],
 112  
 113              [[null, null, null, null], 'frog'],
 114              [['3', '', '', 'frogs'], '3 frogs'],
 115              [[null, null, null, null], '. m'],
 116              [[null, null, null, null], '.e8 m'],
 117              [[null, null, null, null], ','],
 118          ];
 119      }
 120  
 121      /**
 122       * Call apply_units and verify the value and units returned.
 123       *
 124       * @param int|float $exectedval
 125       * @param null|string $expectedunit
 126       * @param int|float $expectedmultiplier
 127       * @param qtype_numerical_answer_processor $ap
 128       * @param null|int|float $input
 129       * @param null|string $separateunit
 130       */
 131      protected function verify_value_and_unit(
 132          $exectedval,
 133          $expectedunit,
 134          $expectedmultiplier,
 135          qtype_numerical_answer_processor $ap,
 136          $input,
 137          $separateunit = null
 138      ): void {
 139          [$val, $unit, $multiplier] = $ap->apply_units($input, $separateunit);
 140          if (is_null($exectedval)) {
 141              $this->assertNull($val);
 142          } else {
 143              $this->assertEqualsWithDelta($exectedval, $val, 0.0001);
 144          }
 145          $this->assertEquals($expectedunit, $unit);
 146          if (is_null($expectedmultiplier)) {
 147              $this->assertNull($multiplier);
 148          } else {
 149              $this->assertEqualsWithDelta($expectedmultiplier, $multiplier, 0.0001);
 150          }
 151      }
 152  
 153      /**
 154       * Test the apply_units function with various parameters.
 155       *
 156       * @covers \qtype_numerical_answer_processor::apply_units
 157       * @dataProvider apply_units_provider
 158       * @param mixed $expectedvalue
 159       * @param string|null $expectedunit
 160       * @param float|int|null $expectedmultiplier
 161       * @param string|null $input
 162       */
 163      public function test_apply_units(
 164          $expectedvalue,
 165          $expectedunit,
 166          $expectedmultiplier,
 167          $input
 168      ): void {
 169          $ap = new qtype_numerical_answer_processor(
 170              [
 171                  'm/s' => 1,
 172                  'c' => 3.3356409519815E-9,
 173                  'mph' => 2.2369362920544
 174              ],
 175              false,
 176              '.',
 177              ','
 178          );
 179  
 180          $this->verify_value_and_unit(
 181              $expectedvalue,
 182              $expectedunit,
 183              $expectedmultiplier,
 184              $ap,
 185              $input
 186          );
 187      }
 188  
 189      /**
 190       * Data provider for apply_units tests.
 191       *
 192       * @return array
 193       */
 194      public function apply_units_provider(): array {
 195          return [
 196              [3e8, 'm/s', 1, '3x10^8 m/s'],
 197              [3e8, '', null, '3x10^8'],
 198              [1, 'c', 299792458, '1c'],
 199              [1, 'mph', 0.44704, '0001.000 mph'],
 200  
 201              [1, 'frogs', null, '1 frogs'],
 202              [null, null, null, '. m/s'],
 203              [null, null, null, null],
 204              [null, null, null, ''],
 205              [null, null, null, '    '],
 206          ];
 207      }
 208  
 209      /**
 210       * Test the apply_units function with various parameters and different units.
 211       *
 212       * @covers \qtype_numerical_answer_processor::apply_units
 213       * @dataProvider apply_units_provider_with_units
 214       * @param mixed $expectedvalue
 215       * @param string|null $expectedunit
 216       * @param float|int|null $expectedmultiplier
 217       * @param string|null $input
 218       * @param string $units
 219       */
 220      public function test_apply_units_with_unit(
 221          $expectedvalue,
 222          $expectedunit,
 223          $expectedmultiplier,
 224          $input,
 225          $units
 226      ): void {
 227          $ap = new qtype_numerical_answer_processor(
 228              [
 229                  'm/s' => 1,
 230                  'c' => 3.3356409519815E-9,
 231                  'mph' => 2.2369362920544
 232              ],
 233              false,
 234              '.',
 235              ','
 236          );
 237  
 238          $this->verify_value_and_unit(
 239              $expectedvalue,
 240              $expectedunit,
 241              $expectedmultiplier,
 242              $ap,
 243              $input,
 244              $units
 245          );
 246      }
 247  
 248      /**
 249       * Data provider for apply_units with different units.
 250       *
 251       * @return array
 252       */
 253      public function apply_units_provider_with_units(): array {
 254          return [
 255              [3e8, 'm/s', 1, '3x10^8', 'm/s'],
 256              [3e8, '', null, '3x10^8', ''],
 257              [1, 'c', 299792458, '1', 'c'],
 258              [1, 'mph', 0.44704, '0001.000', 'mph'],
 259  
 260              [1, 'frogs', null, '1', 'frogs'],
 261              [null, null, null, '.', 'm/s'],
 262          ];
 263      }
 264  
 265      /**
 266       * Test apply_units with a comma float unit.
 267       *
 268       * @covers \qtype_numerical_answer_processor::apply_units
 269       * @dataProvider euro_provider
 270       * @param array $expected
 271       * @param string $params
 272       */
 273      public function test_euro_style(array $expected, string $params): void {
 274          $ap = new qtype_numerical_answer_processor([], false, ',', ' ');
 275          $this->assertEquals($expected, $ap->apply_units($params));
 276      }
 277  
 278      /**
 279       * Data provider for apply_units with euro float separators.
 280       *
 281       * return array
 282       */
 283      public function euro_provider(): array {
 284          return [
 285              [[-1000, '', null], '-1 000'],
 286              [[3.14159, '', null], '3,14159'],
 287          ];
 288      }
 289  
 290      /**
 291       * Test apply_units with percentage values.
 292       *
 293       * @covers \qtype_numerical_answer_processor::apply_units
 294       * @dataProvider percent_provider
 295       * @param array $expected
 296       * @param string $params
 297       */
 298      public function test_percent(array $expected, string $params): void {
 299          $ap = new qtype_numerical_answer_processor(['%' => 100], false, '.', ',');
 300          $this->assertEquals($expected, $ap->apply_units($params));
 301      }
 302  
 303      /**
 304       * Data provider for apply_units with percentages.
 305       *
 306       * @return array
 307       */
 308      public function percent_provider(): array {
 309          return [
 310              [['3', '%', 0.01], '3%'],
 311              [['1e-6', '%', 0.01], '1e-6 %'],
 312              [['100', '', null], '100'],
 313          ];
 314      }
 315  
 316      /**
 317       * Test apply_units with currency values.
 318       *
 319       * @covers \qtype_numerical_answer_processor::apply_units
 320       * @dataProvider currency_provider
 321       * @param array $expected
 322       * @param string $params
 323       */
 324      public function test_currency(array $expected, string $params): void {
 325          $ap = new qtype_numerical_answer_processor([
 326              '$' => 1,
 327              '£' => 1,
 328          ], true, '.', ',');
 329          $this->assertEquals($expected, $ap->apply_units($params));
 330      }
 331  
 332      /**
 333       * Data provider for apply_units with currency values.
 334       *
 335       * @return array
 336       */
 337      public function currency_provider(): array {
 338          return [
 339              [['1234.56', '£', 1], '£1,234.56'],
 340              [['100', '$', 1], '$100'],
 341              [['100', '$', 1], '$100.'],
 342              [['100.00', '$', 1], '$100.00'],
 343              [['100', '', null], '100'],
 344              [['100', 'frog', null], 'frog 100'],
 345          ];
 346      }
 347  }