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/**
19 * Unit tests for the numerical question definition class.
20 *
21 * @package    qtype
22 * @subpackage numerical
23 * @copyright  2008 The Open University
24 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
27
28global $CFG;
29require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
30
31
32/**
33 * Unit tests for the numerical question definition class.
34 *
35 * @copyright 2008 The Open University
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 */
38class qtype_numerical_question_test extends advanced_testcase {
39    public function test_is_complete_response() {
40        $question = test_question_maker::make_question('numerical');
41
42        $this->assertFalse($question->is_complete_response(array()));
43        $this->assertTrue($question->is_complete_response(array('answer' => '0')));
44        $this->assertTrue($question->is_complete_response(array('answer' => 0)));
45        $this->assertFalse($question->is_complete_response(array('answer' => 'test')));
46    }
47
48    public function test_is_gradable_response() {
49        $question = test_question_maker::make_question('numerical');
50
51        $this->assertFalse($question->is_gradable_response(array()));
52        $this->assertTrue($question->is_gradable_response(array('answer' => '0')));
53        $this->assertTrue($question->is_gradable_response(array('answer' => 0)));
54        $this->assertTrue($question->is_gradable_response(array('answer' => 'test')));
55    }
56
57    public function test_grading() {
58        $question = test_question_maker::make_question('numerical');
59
60        $this->assertEquals(array(0, question_state::$gradedwrong),
61                $question->grade_response(array('answer' => '1.0')));
62        $this->assertEquals(array(1, question_state::$gradedright),
63                $question->grade_response(array('answer' => '3.14')));
64    }
65
66    public function test_grading_with_units() {
67        $question = test_question_maker::make_question('numerical');
68        $question->unitgradingtype = qtype_numerical::UNITOPTIONAL;
69        $question->ap = new qtype_numerical_answer_processor(
70                array('m' => 1, 'cm' => 100), false, '.', ',');
71
72        $this->assertEquals(array(1, question_state::$gradedright),
73                $question->grade_response(array('answer' => '3.14 frogs')));
74        $this->assertEquals(array(1, question_state::$gradedright),
75                $question->grade_response(array('answer' => '3.14')));
76        $this->assertEquals(array(1, question_state::$gradedright),
77                $question->grade_response(array('answer' => '3.14 m')));
78        $this->assertEquals(array(1, question_state::$gradedright),
79                $question->grade_response(array('answer' => '314cm')));
80        $this->assertEquals(array(1, question_state::$gradedright),
81                $question->grade_response(array('answer' => '314000000x10^-8m')));
82    }
83
84    public function test_grading_with_units_graded() {
85        $question = test_question_maker::make_question('numerical');
86        $question->unitgradingtype = qtype_numerical::UNITGRADED;
87        $question->ap = new qtype_numerical_answer_processor(
88                array('m' => 1, 'cm' => 100), false, '.', ',');
89
90        $this->assertEquals(array(0.8, question_state::$gradedpartial),
91                $question->grade_response(array('answer' => '3.14 frogs')));
92        $this->assertEquals(array(0.8, question_state::$gradedpartial),
93                $question->grade_response(array('answer' => '3.14')));
94        $this->assertEquals(array(1, question_state::$gradedright),
95                $question->grade_response(array('answer' => '3.14 m')));
96        $this->assertEquals(array(1, question_state::$gradedright),
97                $question->grade_response(array('answer' => '314cm')));
98        $this->assertEquals(array(1, question_state::$gradedright),
99                $question->grade_response(array('answer' => '314000000x10^-8m')));
100        $this->assertEquals(array(0.8, question_state::$gradedpartial),
101                $question->grade_response(array('answer' => '3.14 cm')));
102        $this->assertEquals(array(0, question_state::$gradedwrong),
103                $question->grade_response(array('answer' => '314 m')));
104    }
105
106    public function test_grading_unit() {
107        $question = test_question_maker::make_question('numerical', 'unit');
108
109        $this->assertEquals(array(0, question_state::$gradedwrong),
110                $question->grade_response(array('answer' => '2', 'unit' => 'm')));
111        $this->assertEquals(array(0, question_state::$gradedwrong),
112                $question->grade_response(array('answer' => '2', 'unit' => 'cm')));
113        $this->assertEquals(array(0, question_state::$gradedwrong),
114                $question->grade_response(array('answer' => '2', 'unit' => '')));
115
116        $this->assertEquals(array(1, question_state::$gradedright),
117                $question->grade_response(array('answer' => '1.25', 'unit' => 'm')));
118        $this->assertEquals(array(1, question_state::$gradedright),
119                $question->grade_response(array('answer' => '125', 'unit' => 'cm')));
120        $this->assertEquals(array(0.5, question_state::$gradedpartial),
121                $question->grade_response(array('answer' => '1.25', 'unit' => '')));
122
123        $this->assertEquals(array(0.5, question_state::$gradedpartial),
124                $question->grade_response(array('answer' => '1.23', 'unit' => 'm')));
125        $this->assertEquals(array(0.5, question_state::$gradedpartial),
126                $question->grade_response(array('answer' => '123', 'unit' => 'cm')));
127        $this->assertEquals(array(0.25, question_state::$gradedpartial),
128                $question->grade_response(array('answer' => '1.23', 'unit' => '')));
129    }
130
131    public function test_grading_currency() {
132        $question = test_question_maker::make_question('numerical', 'currency');
133
134        $this->assertEquals(array(1, question_state::$gradedright),
135                $question->grade_response(array('answer' => '$1332')));
136        $this->assertEquals(array(1, question_state::$gradedright),
137                $question->grade_response(array('answer' => '$ 1332')));
138        $this->assertEquals(array(0.8, question_state::$gradedpartial),
139                $question->grade_response(array('answer' => 'frog 1332')));
140        $this->assertEquals(array(0.8, question_state::$gradedpartial),
141                $question->grade_response(array('answer' => '1332')));
142        $this->assertEquals(array(0.8, question_state::$gradedpartial),
143                $question->grade_response(array('answer' => ' 1332')));
144        $this->assertEquals(array(0, question_state::$gradedwrong),
145                $question->grade_response(array('answer' => '1332 $')));
146        $this->assertEquals(array(0, question_state::$gradedwrong),
147                $question->grade_response(array('answer' => '1332 frogs')));
148        $this->assertEquals(array(0, question_state::$gradedwrong),
149                $question->grade_response(array('answer' => '$1')));
150    }
151
152    public function test_get_correct_response() {
153        $question = test_question_maker::make_question('numerical');
154
155        $this->assertEquals(array('answer' => '3.14'),
156                $question->get_correct_response());
157    }
158
159    public function test_get_correct_response_units() {
160        $question = test_question_maker::make_question('numerical', 'unit');
161
162        $this->assertEquals(array('answer' => '1.25', 'unit' => 'm'),
163                $question->get_correct_response());
164    }
165
166    public function test_get_correct_response_currency() {
167        $question = test_question_maker::make_question('numerical', 'currency');
168
169        $this->assertEquals(array('answer' => '$ 1332'),
170                $question->get_correct_response());
171    }
172
173    public function test_get_question_summary() {
174        $num = test_question_maker::make_question('numerical');
175        $qsummary = $num->get_question_summary();
176        $this->assertEquals('What is pi to two d.p.?', $qsummary);
177    }
178
179    public function test_summarise_response() {
180        $num = test_question_maker::make_question('numerical');
181        $this->assertEquals('3.1', $num->summarise_response(array('answer' => '3.1')));
182    }
183
184    public function test_summarise_response_zero() {
185        $num = test_question_maker::make_question('numerical');
186        $this->assertEquals('0', $num->summarise_response(array('answer' => '0')));
187    }
188
189    public function test_summarise_response_unit() {
190        $num = test_question_maker::make_question('numerical', 'unit');
191        $this->assertEquals('3.1', $num->summarise_response(array('answer' => '3.1')));
192        $this->assertEquals('3.1m', $num->summarise_response(array('answer' => '3.1m')));
193        $this->assertEquals('3.1 cm', $num->summarise_response(array('answer' => '3.1 cm')));
194    }
195
196    public function test_summarise_response_currency() {
197        $num = test_question_maker::make_question('numerical', 'currency');
198        $this->assertEquals('100', $num->summarise_response(array('answer' => '100')));
199        $this->assertEquals('$100', $num->summarise_response(array('answer' => '$100')));
200        $this->assertEquals('$ 100', $num->summarise_response(array('answer' => '$ 100')));
201        $this->assertEquals('100 frogs', $num->summarise_response(array('answer' => '100 frogs')));
202    }
203
204    public function test_classify_response() {
205        $num = test_question_maker::make_question('numerical');
206        $num->start_attempt(new question_attempt_step(), 1);
207
208        $this->assertEquals(array(
209                new question_classified_response(15, '3.1', 0.0)),
210                $num->classify_response(array('answer' => '3.1')));
211        $this->assertEquals(array(
212                new question_classified_response(17, '42', 0.0)),
213                $num->classify_response(array('answer' => '42')));
214        $this->assertEquals(array(
215                new question_classified_response(13, '3.14', 1.0)),
216                $num->classify_response(array('answer' => '3.14')));
217        // Invalid response.
218        $this->assertEquals(array(
219                new question_classified_response(null, 'abc', 0.0)),
220                $num->classify_response(array('answer' => 'abc')));
221    }
222
223    public function test_classify_response_no_star() {
224        $num = test_question_maker::make_question('numerical');
225        unset($num->answers[17]);
226        $num->start_attempt(new question_attempt_step(), 1);
227
228        $this->assertEquals(array(
229                new question_classified_response(15, '3.1', 0.0)),
230                $num->classify_response(array('answer' => '3.1')));
231        $this->assertEquals(array(
232                new question_classified_response(0, '42', 0.0)),
233                $num->classify_response(array('answer' => '42')));
234        // Invalid response.
235        $this->assertEquals(array(
236                new question_classified_response(null, 'abc', 0.0)),
237                $num->classify_response(array('answer' => 'abc')));
238    }
239
240    public function test_classify_response_unit() {
241        $num = test_question_maker::make_question('numerical', 'unit');
242        $num->start_attempt(new question_attempt_step(), 1);
243
244        $this->assertEquals(array(
245                new question_classified_response(13, '1.25', 0.5)),
246                $num->classify_response(array('answer' => '1.25', 'unit' => '')));
247        $this->assertEquals(array(
248                new question_classified_response(13, '1.25 m', 1.0)),
249                $num->classify_response(array('answer' => '1.25', 'unit' => 'm')));
250        $this->assertEquals(array(
251                new question_classified_response(13, '125 cm', 1.0)),
252                $num->classify_response(array('answer' => '125', 'unit' => 'cm')));
253        $this->assertEquals(array(
254                new question_classified_response(14, '123 cm', 0.5)),
255                $num->classify_response(array('answer' => '123', 'unit' => 'cm')));
256        $this->assertEquals(array(
257                new question_classified_response(14, '1.27 m', 0.5)),
258                $num->classify_response(array('answer' => '1.27', 'unit' => 'm')));
259        $this->assertEquals(array(
260                new question_classified_response(17, '3.0 m', 0)),
261                $num->classify_response(array('answer' => '3.0', 'unit' => 'm')));
262        $this->assertEquals(array(
263                question_classified_response::no_response()),
264                $num->classify_response(array('answer' => '')));
265        // Invalid response.
266        $this->assertEquals(array(
267                new question_classified_response(null, 'abc m', 0.0)),
268                $num->classify_response(array('answer' => 'abc', 'unit' => 'm')));
269    }
270
271    public function test_classify_response_unit_no_star() {
272        $num = test_question_maker::make_question('numerical', 'unit');
273        unset($num->answers[17]);
274        $num->start_attempt(new question_attempt_step(), 1);
275
276        $this->assertEquals(array(
277                new question_classified_response(0, '42 cm', 0)),
278                $num->classify_response(array('answer' => '42', 'unit' => 'cm')));
279        $this->assertEquals(array(
280                new question_classified_response(0, '3.0', 0)),
281                $num->classify_response(array('answer' => '3.0', 'unit' => '')));
282        $this->assertEquals(array(
283                new question_classified_response(0, '3.0 m', 0)),
284                $num->classify_response(array('answer' => '3.0', 'unit' => 'm')));
285        $this->assertEquals(array(
286                question_classified_response::no_response()),
287                $num->classify_response(array('answer' => '', 'unit' => '')));
288        // Invalid response.
289        $this->assertEquals(array(
290                            new question_classified_response(null, 'abc m', 0.0)),
291                            $num->classify_response(array('answer' => 'abc', 'unit' => 'm')));
292    }
293
294    public function test_classify_response_currency() {
295        $num = test_question_maker::make_question('numerical', 'currency');
296        $num->start_attempt(new question_attempt_step(), 1);
297
298        $this->assertEquals(array(
299                new question_classified_response(14, '$100', 0)),
300                $num->classify_response(array('answer' => '$100')));
301        $this->assertEquals(array(
302                new question_classified_response(13, '1 332', 0.8)),
303                $num->classify_response(array('answer' => '1 332')));
304        // Invalid response.
305        $this->assertEquals(array(
306                new question_classified_response(null, '$abc', 0.0)),
307                $num->classify_response(array('answer' => '$abc')));
308    }
309
310    /**
311     * test_get_question_definition_for_external_rendering
312     */
313    public function test_get_question_definition_for_external_rendering() {
314        $this->resetAfterTest();
315
316        $question = test_question_maker::make_question('numerical', 'unit');
317        $question->start_attempt(new question_attempt_step(), 1);
318        $qa = test_question_maker::get_a_qa($question);
319        $displayoptions = new question_display_options();
320
321        $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
322        $this->assertNotEmpty($options);
323        $this->assertEquals(1, $options['unitgradingtype']);
324        $this->assertEquals(0.5, $options['unitpenalty']);
325        $this->assertEquals(qtype_numerical::UNITSELECT, $options['unitdisplay']);
326        $this->assertEmpty($options['unitsleft']);
327    }
328}
329