MDL-37157: Normalize short answers before comparison
[moodle.git] / question / type / shortanswer / tests / question_test.php
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/>.
17 /**
18  * Unit tests for the short answer question definition class.
19  *
20  * @package    qtype
21  * @subpackage shortanswer
22  * @copyright  2008 The Open University
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
30 require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
31 require_once($CFG->dirroot . '/question/type/shortanswer/question.php');
34 /**
35  * Unit tests for the short answer question definition class.
36  *
37  * @copyright  2008 The Open University
38  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39  */
40 class qtype_shortanswer_question_test extends advanced_testcase {
41     public function test_compare_string_with_wildcard() {
42         // Test case sensitive literal matches.
43         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
44                 'Frog', 'Frog', false));
45         $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
46                 'Frog', 'frog', false));
47         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
48                 '   Frog   ', 'Frog', false));
49         $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
50                 'Frogs', 'Frog', false));
52         // Test case insensitive literal matches.
53         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
54                 'Frog', 'frog', true));
55         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
56                 '   FROG   ', 'Frog', true));
57         $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
58                 'Frogs', 'Frog', true));
60         // Test case sensitive wildcard matches.
61         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
62                 'Frog', 'F*og', false));
63         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
64                 'Fog', 'F*og', false));
65         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
66                 '   Fat dog   ', 'F*og', false));
67         $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
68                 'Frogs', 'F*og', false));
69         $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
70                 'Fg', 'F*og', false));
71         $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
72                 'frog', 'F*og', false));
73         $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
74                 '   fat dog   ', 'F*og', false));
76         // Test case insensitive wildcard matches.
77         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
78                 'Frog', 'F*og', true));
79         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
80                 'Fog', 'F*og', true));
81         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
82                 '   Fat dog   ', 'F*og', true));
83         $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
84                 'Frogs', 'F*og', true));
85         $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
86                 'Fg', 'F*og', true));
87         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
88                 'frog', 'F*og', true));
89         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
90                 '   fat dog   ', 'F*og', true));
92         // Test match using regexp special chars.
93         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
94                 '   *   ', '\*', false));
95         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
96                 '*', '\*', false));
97         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
98                 'Frog*toad', 'Frog\*toad', false));
99         $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
100                 'a', '[a-z]', false));
101         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
102                 '[a-z]', '[a-z]', false));
103         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
104                 '\{}/', '\{}/', true));
106         // See http://moodle.org/mod/forum/discuss.php?d=120557
107         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
108                 'ITÁLIE', 'Itálie', true));
110         if (function_exists('normalizer_normalize')) {
111             // Test ambiguous unicode representations
112             $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
113                     'départ', 'DÉPART', true));
114             $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
115                     'départ', 'DÉPART', false));
116             $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
117                     'd'."\xC3\xA9".'part', 'd'."\x65\xCC\x81".'part', false));
118             $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
119                     'd'."\xC3\xA9".'part', 'D'."\x45\xCC\x81".'PART', true));
120         }
121     }
123     public function test_is_complete_response() {
124         $question = test_question_maker::make_question('shortanswer');
126         $this->assertFalse($question->is_complete_response(array()));
127         $this->assertFalse($question->is_complete_response(array('answer' => '')));
128         $this->assertTrue($question->is_complete_response(array('answer' => '0')));
129         $this->assertTrue($question->is_complete_response(array('answer' => '0.0')));
130         $this->assertTrue($question->is_complete_response(array('answer' => 'x')));
131     }
133     public function test_is_gradable_response() {
134         $question = test_question_maker::make_question('shortanswer');
136         $this->assertFalse($question->is_gradable_response(array()));
137         $this->assertFalse($question->is_gradable_response(array('answer' => '')));
138         $this->assertTrue($question->is_gradable_response(array('answer' => '0')));
139         $this->assertTrue($question->is_gradable_response(array('answer' => '0.0')));
140         $this->assertTrue($question->is_gradable_response(array('answer' => 'x')));
141     }
143     public function test_grading() {
144         $question = test_question_maker::make_question('shortanswer');
146         $this->assertEquals(array(0, question_state::$gradedwrong),
147                 $question->grade_response(array('answer' => 'x')));
148         $this->assertEquals(array(1, question_state::$gradedright),
149                 $question->grade_response(array('answer' => 'frog')));
150         $this->assertEquals(array(0.8, question_state::$gradedpartial),
151                 $question->grade_response(array('answer' => 'toad')));
152     }
154     public function test_get_correct_response() {
155         $question = test_question_maker::make_question('shortanswer');
157         $this->assertEquals(array('answer' => 'frog'),
158                 $question->get_correct_response());
159     }
161     public function test_get_correct_response_escapedwildcards() {
162         $question = test_question_maker::make_question('shortanswer', 'escapedwildcards');
164         $this->assertEquals(array('answer' => 'x*y'), $question->get_correct_response());
165     }
167     public function test_get_question_summary() {
168         $sa = test_question_maker::make_question('shortanswer');
169         $qsummary = $sa->get_question_summary();
170         $this->assertEquals('Name an amphibian: __________', $qsummary);
171     }
173     public function test_summarise_response() {
174         $sa = test_question_maker::make_question('shortanswer');
175         $summary = $sa->summarise_response(array('answer' => 'dog'));
176         $this->assertEquals('dog', $summary);
177     }
179     public function test_classify_response() {
180         $sa = test_question_maker::make_question('shortanswer');
181         $sa->start_attempt(new question_attempt_step(), 1);
183         $this->assertEquals(array(
184                 new question_classified_response(13, 'frog', 1.0)),
185                 $sa->classify_response(array('answer' => 'frog')));
186         $this->assertEquals(array(
187                 new question_classified_response(14, 'toad', 0.8)),
188                 $sa->classify_response(array('answer' => 'toad')));
189         $this->assertEquals(array(
190                 new question_classified_response(15, 'cat', 0.0)),
191                 $sa->classify_response(array('answer' => 'cat')));
192         $this->assertEquals(array(
193                 question_classified_response::no_response()),
194                 $sa->classify_response(array('answer' => '')));
195     }
197     public function test_classify_response_no_star() {
198         $sa = test_question_maker::make_question('shortanswer', 'frogonly');
199         $sa->start_attempt(new question_attempt_step(), 1);
201         $this->assertEquals(array(
202                 new question_classified_response(13, 'frog', 1.0)),
203                 $sa->classify_response(array('answer' => 'frog')));
204         $this->assertEquals(array(
205                 new question_classified_response(0, 'toad', 0.0)),
206                 $sa->classify_response(array('answer' => 'toad')));
207         $this->assertEquals(array(
208                 question_classified_response::no_response()),
209                 $sa->classify_response(array('answer' => '')));
210     }