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_shortanswer;
  18  
  19  use qtype_shortanswer_question;
  20  use question_attempt_step;
  21  use question_classified_response;
  22  use question_display_options;
  23  use question_state;
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  global $CFG;
  28  require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
  29  require_once($CFG->dirroot . '/question/type/shortanswer/question.php');
  30  
  31  
  32  /**
  33   * Unit tests for the short answer question definition class.
  34   *
  35   * @package    qtype_shortanswer
  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_compare_string_with_wildcard() {
  41          // Test case sensitive literal matches.
  42          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  43                  'Frog', 'Frog', false));
  44          $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  45                  'Frog', 'frog', false));
  46          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  47                  '   Frog   ', 'Frog', false));
  48          $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  49                  'Frogs', 'Frog', false));
  50  
  51          // Test case insensitive literal matches.
  52          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  53                  'Frog', 'frog', true));
  54          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  55                  '   FROG   ', 'Frog', true));
  56          $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  57                  'Frogs', 'Frog', true));
  58  
  59          // Test case sensitive wildcard matches.
  60          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  61                  'Frog', 'F*og', false));
  62          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  63                  'Fog', 'F*og', false));
  64          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  65                  '   Fat dog   ', 'F*og', false));
  66          $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  67                  'Frogs', 'F*og', false));
  68          $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  69                  'Fg', 'F*og', false));
  70          $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  71                  'frog', 'F*og', false));
  72          $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  73                  '   fat dog   ', 'F*og', false));
  74  
  75          // Test case insensitive wildcard matches.
  76          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  77                  'Frog', 'F*og', true));
  78          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  79                  'Fog', 'F*og', true));
  80          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  81                  '   Fat dog   ', 'F*og', true));
  82          $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  83                  'Frogs', 'F*og', true));
  84          $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  85                  'Fg', 'F*og', true));
  86          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  87                  'frog', 'F*og', true));
  88          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  89                  '   fat dog   ', 'F*og', true));
  90  
  91          // Test match using regexp special chars.
  92          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  93                  '   *   ', '\*', false));
  94          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  95                  '*', '\*', false));
  96          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  97                  'Frog*toad', 'Frog\*toad', false));
  98          $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
  99                  'a', '[a-z]', false));
 100          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 101                  '[a-z]', '[a-z]', false));
 102          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 103                  '\{}/', '\{}/', true));
 104  
 105          // See http://moodle.org/mod/forum/discuss.php?d=120557.
 106          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 107                  'ITÁLIE', 'Itálie', true));
 108  
 109          if (function_exists('normalizer_normalize')) {
 110              // Test ambiguous unicode representations.
 111              $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 112                      'départ', 'DÉPART', true));
 113              $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 114                      'départ', 'DÉPART', false));
 115              $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 116                      'd'."\xC3\xA9".'part', 'd'."\x65\xCC\x81".'part', false));
 117              $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 118                      'd'."\xC3\xA9".'part', 'D'."\x45\xCC\x81".'PART', true));
 119          }
 120      }
 121  
 122      public function test_compare_0_with_wildcard() {
 123          // Test the classic PHP problem case with '0'.
 124          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 125                  '0', '0', false));
 126          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 127                  '0', '0*', false));
 128          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 129                  '0', '*0', false));
 130          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 131                  '0', '*0*', false));
 132      }
 133  
 134      public function test_compare_string_with_wildcard_many_stars() {
 135          // Test the classic PHP problem case with '0'.
 136          $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
 137                  '<em></em>', '***********************************<em>***********************************</em>', false));
 138      }
 139  
 140      public function test_is_complete_response() {
 141          $question = \test_question_maker::make_question('shortanswer');
 142  
 143          $this->assertFalse($question->is_complete_response(array()));
 144          $this->assertFalse($question->is_complete_response(array('answer' => '')));
 145          $this->assertTrue($question->is_complete_response(array('answer' => '0')));
 146          $this->assertTrue($question->is_complete_response(array('answer' => '0.0')));
 147          $this->assertTrue($question->is_complete_response(array('answer' => 'x')));
 148      }
 149  
 150      public function test_is_gradable_response() {
 151          $question = \test_question_maker::make_question('shortanswer');
 152  
 153          $this->assertFalse($question->is_gradable_response(array()));
 154          $this->assertFalse($question->is_gradable_response(array('answer' => '')));
 155          $this->assertTrue($question->is_gradable_response(array('answer' => '0')));
 156          $this->assertTrue($question->is_gradable_response(array('answer' => '0.0')));
 157          $this->assertTrue($question->is_gradable_response(array('answer' => 'x')));
 158      }
 159  
 160      public function test_grading() {
 161          $question = \test_question_maker::make_question('shortanswer');
 162  
 163          $this->assertEquals(array(0, question_state::$gradedwrong),
 164                  $question->grade_response(array('answer' => 'x')));
 165          $this->assertEquals(array(1, question_state::$gradedright),
 166                  $question->grade_response(array('answer' => 'frog')));
 167          $this->assertEquals(array(0.8, question_state::$gradedpartial),
 168                  $question->grade_response(array('answer' => 'toad')));
 169      }
 170  
 171      public function test_get_correct_response() {
 172          $question = \test_question_maker::make_question('shortanswer');
 173  
 174          $this->assertEquals(array('answer' => 'frog'),
 175                  $question->get_correct_response());
 176      }
 177  
 178      public function test_get_correct_response_escapedwildcards() {
 179          $question = \test_question_maker::make_question('shortanswer', 'escapedwildcards');
 180  
 181          $this->assertEquals(array('answer' => 'x*y'), $question->get_correct_response());
 182      }
 183  
 184      public function test_get_question_summary() {
 185          $sa = \test_question_maker::make_question('shortanswer');
 186          $qsummary = $sa->get_question_summary();
 187          $this->assertEquals('Name an amphibian: __________', $qsummary);
 188      }
 189  
 190      public function test_summarise_response() {
 191          $sa = \test_question_maker::make_question('shortanswer');
 192          $summary = $sa->summarise_response(array('answer' => 'dog'));
 193          $this->assertEquals('dog', $summary);
 194      }
 195  
 196      public function test_classify_response() {
 197          $sa = \test_question_maker::make_question('shortanswer');
 198          $sa->start_attempt(new question_attempt_step(), 1);
 199  
 200          $this->assertEquals(array(
 201                  new question_classified_response(13, 'frog', 1.0)),
 202                  $sa->classify_response(array('answer' => 'frog')));
 203          $this->assertEquals(array(
 204                  new question_classified_response(14, 'toad', 0.8)),
 205                  $sa->classify_response(array('answer' => 'toad')));
 206          $this->assertEquals(array(
 207                  new question_classified_response(15, 'cat', 0.0)),
 208                  $sa->classify_response(array('answer' => 'cat')));
 209          $this->assertEquals(array(
 210                  question_classified_response::no_response()),
 211                  $sa->classify_response(array('answer' => '')));
 212      }
 213  
 214      public function test_classify_response_no_star() {
 215          $sa = \test_question_maker::make_question('shortanswer', 'frogonly');
 216          $sa->start_attempt(new question_attempt_step(), 1);
 217  
 218          $this->assertEquals(array(
 219                  new question_classified_response(13, 'frog', 1.0)),
 220                  $sa->classify_response(array('answer' => 'frog')));
 221          $this->assertEquals(array(
 222                  new question_classified_response(0, 'toad', 0.0)),
 223                  $sa->classify_response(array('answer' => 'toad')));
 224          $this->assertEquals(array(
 225                  question_classified_response::no_response()),
 226                  $sa->classify_response(array('answer' => '')));
 227      }
 228  
 229      /**
 230       * test_get_question_definition_for_external_rendering
 231       */
 232      public function test_get_question_definition_for_external_rendering() {
 233          $this->resetAfterTest();
 234  
 235          $question = \test_question_maker::make_question('shortanswer');
 236          $question->start_attempt(new question_attempt_step(), 1);
 237          $qa = \test_question_maker::get_a_qa($question);
 238          $displayoptions = new question_display_options();
 239  
 240          $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
 241          $this->assertNull($options);
 242      }
 243  }