Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   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_calculated;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  global $CFG;
  22  require_once($CFG->dirroot . '/question/type/calculated/questiontype.php');
  23  
  24  /**
  25   * Unit tests for formula validation code.
  26   *
  27   * @package    qtype_calculated
  28   * @copyright  2014 The Open University
  29   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   */
  31  class formula_validation_test extends \basic_testcase {
  32      protected function assert_nonempty_string($actual) {
  33          $this->assertIsString($actual);
  34          $this->assertNotEquals('', $actual);
  35      }
  36  
  37      public function test_simple_equations_ok() {
  38          $this->assertFalse(qtype_calculated_find_formula_errors(1));
  39          $this->assertFalse(qtype_calculated_find_formula_errors('1 + 1'));
  40          $this->assertFalse(qtype_calculated_find_formula_errors('{x} + {y}'));
  41          $this->assertFalse(qtype_calculated_find_formula_errors('{x}*{y}'));
  42          $this->assertFalse(qtype_calculated_find_formula_errors('{x}*({y}+1)'));
  43      }
  44  
  45      public function test_simple_equations_errors() {
  46          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('{a{b}}'));
  47          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('{a{b}}'));
  48          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('{a}({b})'));
  49          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('2({b})'));
  50      }
  51  
  52      public function test_safe_functions_ok() {
  53          $this->assertFalse(qtype_calculated_find_formula_errors('abs(-1)'));
  54          $this->assertFalse(qtype_calculated_find_formula_errors('tan(pi())'));
  55          $this->assertFalse(qtype_calculated_find_formula_errors('log(10)'));
  56          $this->assertFalse(qtype_calculated_find_formula_errors('log(64, 2)'));
  57          $this->assertFalse(qtype_calculated_find_formula_errors('atan2(1.0, 1.0)'));
  58          $this->assertFalse(qtype_calculated_find_formula_errors('max(1.0, 1.0)'));
  59          $this->assertFalse(qtype_calculated_find_formula_errors('max(1.0, 1.0, 2.0)'));
  60          $this->assertFalse(qtype_calculated_find_formula_errors('max(1.0, 1.0, 2, 3)'));
  61      }
  62  
  63      public function test_php_comments_blocked() {
  64          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('# No need for this.'));
  65          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('/* Also blocked. */'));
  66          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('1 + 1 /* Blocked too. */'));
  67          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('// As is this.'));
  68          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('1/*2'));
  69          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('/*{a*///{x}}'));
  70      }
  71  
  72      public function test_dangerous_functions_blocked() {
  73          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('eval(1)'));
  74          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('system(1)'));
  75          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('base64_decode(1)'));
  76          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('unserialize(1)'));
  77  
  78          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('cos(tan(1) + abs(cos(eval)) * pi())'));
  79          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('eval (CONSTANTREADASSTRING)'));
  80          $this->assert_nonempty_string(qtype_calculated_find_formula_errors("eval \t ()"));
  81          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('"eval"()'));
  82          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('?><?php()'));
  83          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('?><?php+1'));
  84      }
  85  
  86      public function test_functions_with_wrong_num_args_caught() {
  87          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('abs(-1, 1)'));
  88          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('abs()'));
  89          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('pi(1)'));
  90          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('log()'));
  91          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('log(64, 2, 3)'));
  92          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('atan2(1.0)'));
  93          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('atan2(1.0, 1.0, 2.0)'));
  94          $this->assert_nonempty_string(qtype_calculated_find_formula_errors('max(1.0)'));
  95      }
  96  
  97      public function test_validation_of_formulas_in_text_ok() {
  98          $this->assertFalse(qtype_calculated_find_formula_errors_in_text(
  99                  '<p>Look no equations.</p>'));
 100          $this->assertFalse(qtype_calculated_find_formula_errors_in_text(
 101                  '<p>Simple variable: {x}.</p>'));
 102          $this->assertFalse(qtype_calculated_find_formula_errors_in_text(
 103                  '<p>This is an equation: {=1+1}, as is this: {={x}+{y}}.</p>' .
 104                  '<p>Here is a more complex one: {=sin(2*pi()*{theta})}.</p>'));
 105      }
 106  
 107      public function test_validation_of_formulas_in_text_bad_function() {
 108          $this->assert_nonempty_string(qtype_calculated_find_formula_errors_in_text(
 109                  '<p>This is an equation: {=eval(1)}.</p>'));
 110          $this->assert_nonempty_string(qtype_calculated_find_formula_errors_in_text(
 111                  '<p>Good: {=1+1}, bad: {=eval(1)}, good: {={x}+{y}}.</p>'));
 112          $this->assert_nonempty_string(qtype_calculated_find_formula_errors_in_text(
 113                  '<p>Bad: {=eval(1)}, bad: {=system(1)}.</p>'));
 114      }
 115  }