MDL-64420 qtype: gapselect unit tests unsafe
[moodle.git] / question / behaviour / adaptive / tests / walkthrough_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/>.
18 /**
19  * This file contains tests that walks a question through the adaptive
20  * behaviour.
21  *
22  * @package    qbehaviour
23  * @subpackage adaptive
24  * @copyright  2009 The Open University
25  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26  */
29 defined('MOODLE_INTERNAL') || die();
31 global $CFG;
32 require_once(__DIR__ . '/../../../engine/lib.php');
33 require_once(__DIR__ . '/../../../engine/tests/helpers.php');
36 /**
37  * Unit tests for the adaptive behaviour.
38  *
39  * @copyright  2009 The Open University
40  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41  */
42 class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_base {
43     protected function get_contains_penalty_info_expectation($penalty) {
44         $penaltyinfo = get_string('gradingdetailspenalty', 'qbehaviour_adaptive',
45                                   format_float($penalty, $this->displayoptions->markdp));
46         return new question_pattern_expectation('/'.preg_quote($penaltyinfo, '/').'/');
47     }
49     protected function get_does_not_contain_penalty_info_expectation() {
50         $penaltyinfo = get_string('gradingdetailspenalty', 'qbehaviour_adaptive', 'XXXXX');
51         $penaltypattern = '/'.str_replace('XXXXX', '\\w*', preg_quote($penaltyinfo, '/')).'/';
52         return new question_no_pattern_expectation($penaltypattern);
53     }
55     protected function get_contains_total_penalty_expectation($penalty) {
56         $penaltyinfo = get_string('gradingdetailspenaltytotal', 'qbehaviour_adaptive',
57                                   format_float($penalty, $this->displayoptions->markdp));
58         return new question_pattern_expectation('/'.preg_quote($penaltyinfo, '/').'/');
59     }
61     protected function get_does_not_contain_total_penalty_expectation() {
62         $penaltyinfo = get_string('gradingdetailspenaltytotal', 'qbehaviour_adaptive', 'XXXXX');
63         $penaltypattern = '/'.str_replace('XXXXX', '\\w*', preg_quote($penaltyinfo, '/')).'/';
64         return new question_no_pattern_expectation($penaltypattern);
65     }
67     protected function get_contains_disregarded_info_expectation() {
68         $penaltyinfo = get_string('disregardedwithoutpenalty', 'qbehaviour_adaptive');
69         return new question_pattern_expectation('/'.preg_quote($penaltyinfo, '/').'/');
70     }
72     protected function get_does_not_contain_disregarded_info_expectation() {
73         $penaltyinfo = get_string('disregardedwithoutpenalty', 'qbehaviour_adaptive');
74         return new question_no_pattern_expectation('/'.preg_quote($penaltyinfo, '/').'/');
75     }
77     public function test_adaptive_multichoice() {
79         // Create a multiple choice, single response question.
80         $mc = test_question_maker::make_a_multichoice_single_question();
81         $mc->penalty = 0.3333333;
82         $this->start_attempt_at_question($mc, 'adaptive', 3);
84         $rightindex = $this->get_mc_right_answer_index($mc);
85         $wrongindex = ($rightindex + 1) % 3;
87         // Check the initial state.
88         $this->check_current_state(question_state::$todo);
89         $this->check_current_mark(null);
90         $this->check_current_output(
91                 $this->get_contains_marked_out_of_summary(),
92                 $this->get_contains_question_text_expectation($mc),
93                 $this->get_contains_mc_radio_expectation(0, true, false),
94                 $this->get_contains_mc_radio_expectation(1, true, false),
95                 $this->get_contains_mc_radio_expectation(2, true, false),
96                 $this->get_contains_submit_button_expectation(true),
97                 $this->get_does_not_contain_feedback_expectation());
99         // Process a submit.
100         $this->process_submission(array('answer' => $wrongindex, '-submit' => 1));
102         // Verify.
103         $this->check_current_state(question_state::$todo);
104         $this->check_current_mark(0);
105         $this->check_current_output(
106                 $this->get_contains_mark_summary(0),
107                 $this->get_contains_mc_radio_expectation($wrongindex, true, true),
108                 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false),
109                 $this->get_contains_mc_radio_expectation(($wrongindex + 2) % 3, true, false),
110                 $this->get_contains_incorrect_expectation(),
111                 $this->get_contains_penalty_info_expectation(1.00),
112                 $this->get_does_not_contain_total_penalty_expectation());
113         $this->assertRegExp('/B|C/',
114                 $this->quba->get_response_summary($this->slot));
116         // Process a change of answer to the right one, but not sumbitted.
117         $this->process_submission(array('answer' => $rightindex));
119         // Verify.
120         $this->check_current_state(question_state::$todo);
121         $this->check_current_mark(0);
122         $this->check_current_output(
123                 $this->get_contains_mark_summary(0),
124                 $this->get_contains_mc_radio_expectation($rightindex, true, true),
125                 $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, true, false),
126                 $this->get_contains_mc_radio_expectation(($rightindex + 2) % 3, true, false));
127         $this->assertRegExp('/B|C/',
128                 $this->quba->get_response_summary($this->slot));
130         // Now submit the right answer.
131         $this->process_submission(array('answer' => $rightindex, '-submit' => 1));
133         // Verify.
134         $this->check_current_state(question_state::$complete);
135         $this->check_current_mark(3 * (1 - $mc->penalty));
136         $this->check_current_output(
137                 $this->get_contains_mark_summary(3 * (1 - $mc->penalty)),
138                 $this->get_contains_mc_radio_expectation($rightindex, true, true),
139                 $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, true, false),
140                 $this->get_contains_mc_radio_expectation(($rightindex + 2) % 3, true, false),
141                 $this->get_contains_correct_expectation(),
142                 $this->get_does_not_contain_penalty_info_expectation(),
143                 $this->get_does_not_contain_total_penalty_expectation());
144         $this->assertEquals('A',
145                 $this->quba->get_response_summary($this->slot));
147         // Finish the attempt.
148         $this->quba->finish_all_questions();
150         // Verify.
151         $this->check_current_state(question_state::$gradedright);
152         $this->check_current_mark(3 * (1 - $mc->penalty));
153         $this->check_current_output(
154                 $this->get_contains_mark_summary(3 * (1 - $mc->penalty)),
155                 $this->get_contains_mc_radio_expectation($rightindex, false, true),
156                 $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false),
157                 $this->get_contains_mc_radio_expectation(($rightindex + 2) % 3, false, false),
158                 $this->get_contains_correct_expectation());
160         // Process a manual comment.
161         $this->manual_grade('Not good enough!', 1, FORMAT_HTML);
163         // Verify.
164         $this->check_current_state(question_state::$mangrpartial);
165         $this->check_current_mark(1);
166         $this->check_current_output(
167                 $this->get_contains_mark_summary(1),
168                 new question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/'));
170         // Now change the correct answer to the question, and regrade.
171         $mc->answers[13]->fraction = -0.33333333;
172         $mc->answers[14]->fraction = 1; // We don't know which "wrong" index we chose above!
173         $mc->answers[15]->fraction = 1; // Therefore, treat answers B and C with the same score.
174         $this->quba->regrade_all_questions();
176         // Verify.
177         $this->check_current_state(question_state::$mangrpartial);
178         $this->check_current_mark(1);
179         $this->check_current_output(
180                 $this->get_contains_mark_summary(1),
181                 $this->get_contains_partcorrect_expectation());
183         $autogradedstep = $this->get_step($this->get_step_count() - 2);
184         $this->assertEquals($autogradedstep->get_fraction(), 1, '', 0.0000001);
185     }
187     public function test_adaptive_multichoice2() {
189         // Create a multiple choice, multiple response question.
190         $mc = test_question_maker::make_a_multichoice_multi_question();
191         $mc->penalty = 0.3333333;
192         $mc->shuffleanswers = 0;
193         $this->start_attempt_at_question($mc, 'adaptive', 2);
195         // Check the initial state.
196         $this->check_current_state(question_state::$todo);
197         $this->check_current_mark(null);
198         $this->check_current_output(
199                 $this->get_contains_marked_out_of_summary(),
200                 $this->get_contains_question_text_expectation($mc),
201                 $this->get_contains_submit_button_expectation(true),
202                 $this->get_does_not_contain_feedback_expectation());
204         // Process a submit.
205         $this->process_submission(array('choice0' => 1, 'choice2' => 1, '-submit' => 1));
207         // Verify.
208         $this->check_current_state(question_state::$complete);
209         $this->check_current_mark(2);
210         $this->check_current_output(
211                 $this->get_contains_mark_summary(2),
212                 $this->get_contains_submit_button_expectation(true),
213                 $this->get_contains_correct_expectation(),
214                 $this->get_does_not_contain_penalty_info_expectation(),
215                 $this->get_does_not_contain_total_penalty_expectation());
217         // Save the same correct answer again. Should not do anything.
218         $numsteps = $this->get_step_count();
219         $this->process_submission(array('choice0' => 1, 'choice2' => 1));
221         // Verify.
222         $this->check_step_count($numsteps);
223         $this->check_current_mark(2);
224         $this->check_current_state(question_state::$complete);
226         // Finish the attempt.
227         $this->quba->finish_all_questions();
229         // Verify.
230         $this->check_step_count($numsteps + 1);
231         $this->check_current_state(question_state::$gradedright);
232         $this->check_current_mark(2);
233         $this->check_current_output(
234                 $this->get_contains_mark_summary(2),
235                 $this->get_does_not_contain_submit_button_expectation(),
236                 $this->get_contains_correct_expectation());
237     }
239     public function test_adaptive_shortanswer_partially_right() {
241         // Create a short answer question.
242         $sa = test_question_maker::make_question('shortanswer');
243         $this->start_attempt_at_question($sa, 'adaptive');
245         // Check the initial state.
246         $this->check_current_state(question_state::$todo);
247         $this->check_current_mark(null);
248         $this->render();
249         $this->check_output_does_not_contain_text_input_with_class('answer', 'correct');
250         $this->check_output_does_not_contain_text_input_with_class('answer', 'partiallycorrect');
251         $this->check_output_does_not_contain_text_input_with_class('answer', 'incorrect');
252         $this->check_current_output(
253                 $this->get_contains_marked_out_of_summary(),
254                 $this->get_contains_submit_button_expectation(true),
255                 $this->get_does_not_contain_feedback_expectation());
257         // Submit a partially correct answer.
258         $this->process_submission(array('-submit' => 1, 'answer' => 'toad'));
260         // Verify.
261         $this->check_current_state(question_state::$todo);
262         $this->check_current_mark(0.8);
263         $this->render();
264         $this->check_output_contains_text_input_with_class('answer', 'partiallycorrect');
265         $this->check_current_output(
266                 $this->get_contains_mark_summary(0.8),
267                 $this->get_contains_submit_button_expectation(true),
268                 $this->get_contains_partcorrect_expectation(),
269                 $this->get_contains_penalty_info_expectation(0.33),
270                 $this->get_does_not_contain_total_penalty_expectation(),
271                 $this->get_does_not_contain_validation_error_expectation());
273         // Submit an incorrect answer.
274         $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee'));
276         // Verify.
277         $this->check_current_state(question_state::$todo);
278         $this->check_current_mark(0.8);
279         $this->render();
280         $this->check_output_contains_text_input_with_class('answer', 'incorrect');
281         $this->check_current_output(
282                 $this->get_contains_mark_summary(0.8),
283                 $this->get_contains_submit_button_expectation(true),
284                 $this->get_contains_incorrect_expectation(),
285                 $this->get_contains_penalty_info_expectation(0.33),
286                 $this->get_contains_total_penalty_expectation(0.67),
287                 $this->get_does_not_contain_validation_error_expectation());
289         // Submit a correct answer.
290         $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
292         // Verify.
293         $this->check_current_state(question_state::$complete);
294         $this->check_current_mark(0.8);
295         $this->render();
296         $this->check_output_contains_text_input_with_class('answer', 'correct');
297         $this->check_current_output(
298                 $this->get_contains_mark_summary(0.8),
299                 $this->get_contains_submit_button_expectation(true),
300                 $this->get_contains_correct_expectation(),
301                 $this->get_does_not_contain_penalty_info_expectation(),
302                 $this->get_does_not_contain_total_penalty_expectation(),
303                 $this->get_does_not_contain_validation_error_expectation());
305         // Finish the attempt.
306         $this->quba->finish_all_questions();
308         // Verify.
309         $this->check_current_state(question_state::$gradedright);
310         $this->check_current_mark(0.8);
311         $this->check_current_output(
312                 $this->get_contains_mark_summary(0.8),
313                 $this->get_does_not_contain_submit_button_expectation(),
314                 $this->get_contains_correct_expectation(),
315                 $this->get_does_not_contain_validation_error_expectation());
316     }
318     public function test_adaptive_shortanswer_wrong_right_wrong() {
320         // Create a short answer question.
321         $sa = test_question_maker::make_question('shortanswer');
322         $this->start_attempt_at_question($sa, 'adaptive', 6);
324         // Check the initial state.
325         $this->check_current_state(question_state::$todo);
326         $this->check_current_mark(null);
327         $this->check_current_output(
328                 $this->get_contains_marked_out_of_summary(),
329                 $this->get_contains_submit_button_expectation(true),
330                 $this->get_does_not_contain_feedback_expectation());
332         // Submit a wrong answer.
333         $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
335         // Verify.
336         $this->check_current_state(question_state::$todo);
337         $this->check_current_mark(0);
338         $this->check_current_output(
339                 $this->get_contains_mark_summary(0),
340                 $this->get_contains_submit_button_expectation(true),
341                 $this->get_contains_incorrect_expectation(),
342                 $this->get_contains_penalty_info_expectation(2.00),
343                 $this->get_does_not_contain_total_penalty_expectation(),
344                 $this->get_does_not_contain_validation_error_expectation());
346         // Submit the same wrong answer again. Nothing should change.
347         $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
349         // Verify.
350         $this->check_current_state(question_state::$todo);
351         $this->check_current_mark(0);
352         $this->check_current_output(
353                 $this->get_contains_mark_summary(0),
354                 $this->get_contains_submit_button_expectation(true),
355                 $this->get_contains_incorrect_expectation(),
356                 $this->get_contains_penalty_info_expectation(2.00),
357                 $this->get_does_not_contain_total_penalty_expectation(),
358                 $this->get_does_not_contain_validation_error_expectation());
360         // Submit a correct answer.
361         $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
363         // Verify.
364         $this->check_current_state(question_state::$complete);
365         $this->check_current_mark(4.00);
366         $this->check_current_output(
367                 $this->get_contains_mark_summary(4.00),
368                 $this->get_contains_submit_button_expectation(true),
369                 $this->get_contains_correct_expectation(),
370                 $this->get_does_not_contain_penalty_info_expectation(),
371                 $this->get_does_not_contain_total_penalty_expectation(),
372                 $this->get_does_not_contain_validation_error_expectation());
374         // Submit another incorrect answer.
375         $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee'));
377         // Verify.
378         $this->check_current_state(question_state::$complete);
379         $this->check_current_mark(4.00);
380         $this->check_current_output(
381                 $this->get_contains_mark_summary(4.00),
382                 $this->get_contains_submit_button_expectation(true),
383                 $this->get_contains_incorrect_expectation(),
384                 $this->get_does_not_contain_penalty_info_expectation(),
385                 $this->get_does_not_contain_total_penalty_expectation(),
386                 $this->get_does_not_contain_validation_error_expectation());
388         // Finish the attempt.
389         $this->quba->finish_all_questions();
391         // Verify.
392         $this->check_current_state(question_state::$gradedwrong);
393         $this->check_current_mark(4.00);
394         $this->check_current_output(
395                 $this->get_contains_mark_summary(4.00),
396                 $this->get_does_not_contain_submit_button_expectation(),
397                 $this->get_contains_incorrect_expectation(),
398                 $this->get_does_not_contain_validation_error_expectation());
399     }
401     public function test_adaptive_shortanswer_invalid_after_complete() {
403         // Create a short answer question.
404         $sa = test_question_maker::make_question('shortanswer');
405         $this->start_attempt_at_question($sa, 'adaptive');
407         // Check the initial state.
408         $this->check_current_state(question_state::$todo);
409         $this->check_current_mark(null);
410         $this->check_current_output(
411                 $this->get_contains_marked_out_of_summary(),
412                 $this->get_contains_submit_button_expectation(true),
413                 $this->get_does_not_contain_feedback_expectation());
415         // Submit a wrong answer.
416         $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
418         // Verify.
419         $this->check_current_state(question_state::$todo);
420         $this->check_current_mark(0);
421         $this->check_current_output(
422                 $this->get_contains_mark_summary(0),
423                 $this->get_contains_submit_button_expectation(true),
424                 $this->get_contains_incorrect_expectation(),
425                 $this->get_contains_penalty_info_expectation(0.33),
426                 $this->get_does_not_contain_total_penalty_expectation(),
427                 $this->get_does_not_contain_validation_error_expectation());
429         // Submit a correct answer.
430         $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
432         // Verify.
433         $this->check_current_state(question_state::$complete);
434         $this->check_current_mark(0.66666667);
435         $this->check_current_output(
436                 $this->get_contains_mark_summary(0.67),
437                 $this->get_contains_submit_button_expectation(true),
438                 $this->get_contains_correct_expectation(),
439                 $this->get_does_not_contain_penalty_info_expectation(),
440                 $this->get_does_not_contain_total_penalty_expectation(),
441                 $this->get_does_not_contain_validation_error_expectation());
443         // Submit an empty answer.
444         $this->process_submission(array('-submit' => 1, 'answer' => ''));
446         // Verify.
447         $this->check_current_state(question_state::$invalid);
448         $this->check_current_mark(0.66666667);
449         $this->check_current_output(
450                 $this->get_contains_mark_summary(0.67),
451                 $this->get_contains_submit_button_expectation(true),
452                 $this->get_does_not_contain_penalty_info_expectation(),
453                 $this->get_does_not_contain_total_penalty_expectation(),
454                 $this->get_contains_validation_error_expectation());
456         // Submit another wrong answer.
457         $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee'));
459         // Verify.
460         $this->check_current_state(question_state::$complete);
461         $this->check_current_mark(0.66666667);
462         $this->check_current_output(
463                 $this->get_contains_mark_summary(0.67),
464                 $this->get_contains_submit_button_expectation(true),
465                 $this->get_contains_incorrect_expectation(),
466                 $this->get_does_not_contain_penalty_info_expectation(),
467                 $this->get_does_not_contain_total_penalty_expectation(),
468                 $this->get_does_not_contain_validation_error_expectation());
470         // Finish the attempt.
471         $this->quba->finish_all_questions();
473         // Verify.
474         $this->check_current_state(question_state::$gradedwrong);
475         $this->check_current_mark(0.66666667);
476         $this->check_current_output(
477                 $this->get_contains_mark_summary(0.67),
478                 $this->get_does_not_contain_submit_button_expectation(),
479                 $this->get_contains_incorrect_expectation(),
480                 $this->get_does_not_contain_validation_error_expectation());
481     }
483     public function test_adaptive_shortanswer_zero_penalty() {
485         // Create a short answer question.
486         $sa = test_question_maker::make_question('shortanswer');
487         // Disable penalties for this question.
488         $sa->penalty = 0;
489         $this->start_attempt_at_question($sa, 'adaptive');
491         // Check the initial state.
492         $this->check_current_state(question_state::$todo);
493         $this->check_current_mark(null);
494         $this->check_current_output(
495                 $this->get_contains_marked_out_of_summary(),
496                 $this->get_contains_submit_button_expectation(true),
497                 $this->get_does_not_contain_feedback_expectation());
499         // Submit a wrong answer.
500         $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
502         // Verify.
503         $this->check_current_state(question_state::$todo);
504         $this->check_current_mark(0);
505         $this->check_current_output(
506                 $this->get_contains_mark_summary(0),
507                 $this->get_contains_submit_button_expectation(true),
508                 $this->get_contains_incorrect_expectation(),
509                 $this->get_does_not_contain_penalty_info_expectation(),
510                 $this->get_does_not_contain_total_penalty_expectation(),
511                 $this->get_does_not_contain_validation_error_expectation());
513         // Submit a correct answer.
514         $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
516         // Verify.
517         $this->check_current_state(question_state::$complete);
518         $this->check_current_mark(1.0);
519         $this->check_current_output(
520                 $this->get_contains_mark_summary(1.0),
521                 $this->get_contains_submit_button_expectation(true),
522                 $this->get_contains_correct_expectation(),
523                 $this->get_does_not_contain_penalty_info_expectation(),
524                 $this->get_does_not_contain_total_penalty_expectation(),
525                 $this->get_does_not_contain_validation_error_expectation());
527         // Finish the attempt.
528         $this->quba->finish_all_questions();
530         // Verify.
531         $this->check_current_state(question_state::$gradedright);
532         $this->check_current_mark(1.0);
533         $this->check_current_output(
534                 $this->get_contains_mark_summary(1.0),
535                 $this->get_does_not_contain_submit_button_expectation(),
536                 $this->get_contains_correct_expectation(),
537                 $this->get_does_not_contain_validation_error_expectation());
538     }
540     public function test_adaptive_shortanswer_try_to_submit_blank() {
542         // Create a short answer question with correct answer true.
543         $sa = test_question_maker::make_question('shortanswer');
544         $this->start_attempt_at_question($sa, 'adaptive');
546         // Check the initial state.
547         $this->check_current_state(question_state::$todo);
548         $this->check_current_mark(null);
549         $this->check_current_output(
550                 $this->get_contains_marked_out_of_summary(),
551                 $this->get_contains_submit_button_expectation(true),
552                 $this->get_does_not_contain_feedback_expectation());
554         // Submit with blank answer.
555         $this->process_submission(array('-submit' => 1, 'answer' => ''));
557         // Verify.
558         $this->check_current_state(question_state::$invalid);
559         $this->check_current_mark(null);
560         $this->check_current_output(
561                 $this->get_contains_marked_out_of_summary(),
562                 $this->get_contains_submit_button_expectation(true),
563                 $this->get_does_not_contain_correctness_expectation(),
564                 $this->get_does_not_contain_penalty_info_expectation(),
565                 $this->get_does_not_contain_total_penalty_expectation(),
566                 $this->get_contains_validation_error_expectation(),
567                 $this->get_contains_disregarded_info_expectation());
568         $this->assertNull($this->quba->get_response_summary($this->slot));
570         // Now get it wrong.
571         $this->process_submission(array('-submit' => 1, 'answer' => 'toad'));
573         // Verify.
574         $this->check_current_state(question_state::$todo);
575         $this->check_current_mark(0.8);
576         $this->check_current_output(
577                 $this->get_contains_mark_summary(0.8),
578                 $this->get_contains_submit_button_expectation(true),
579                 $this->get_contains_partcorrect_expectation(),
580                 $this->get_contains_penalty_info_expectation(0.33),
581                 $this->get_does_not_contain_total_penalty_expectation(),
582                 $this->get_does_not_contain_validation_error_expectation());
584         // Now submit blank again.
585         $this->process_submission(array('-submit' => 1, 'answer' => ''));
587         // Verify.
588         $this->check_current_state(question_state::$invalid);
589         $this->check_current_mark(0.8);
590         $this->check_current_output(
591                 $this->get_contains_mark_summary(0.8),
592                 $this->get_contains_submit_button_expectation(true),
593                 $this->get_does_not_contain_correctness_expectation(),
594                 $this->get_does_not_contain_penalty_info_expectation(),
595                 $this->get_does_not_contain_total_penalty_expectation(),
596                 $this->get_contains_validation_error_expectation());
597     }
599     public function test_adaptive_numerical() {
601         // Create a numerical question.
602         $sa = test_question_maker::make_question('numerical', 'pi');
603         $this->start_attempt_at_question($sa, 'adaptive');
605         // Check the initial state.
606         $this->check_current_state(question_state::$todo);
607         $this->check_current_mark(null);
608         $this->check_current_output(
609                 $this->get_contains_marked_out_of_summary(),
610                 $this->get_contains_submit_button_expectation(true),
611                 $this->get_does_not_contain_feedback_expectation());
613         // Submit the correct answer.
614         $this->process_submission(array('-submit' => 1, 'answer' => '3.14'));
616         // Verify.
617         $this->check_current_state(question_state::$complete);
618         $this->check_current_mark(1);
619         $this->check_current_output(
620                 $this->get_contains_mark_summary(1),
621                 $this->get_contains_submit_button_expectation(true),
622                 $this->get_contains_correct_expectation(),
623                 $this->get_does_not_contain_penalty_info_expectation(),
624                 $this->get_does_not_contain_total_penalty_expectation(),
625                 $this->get_does_not_contain_validation_error_expectation());
627         // Submit an incorrect answer.
628         $this->process_submission(array('-submit' => 1, 'answer' => '-5'));
630         // Verify.
631         $this->check_current_state(question_state::$complete);
632         $this->check_current_mark(1);
633         $this->check_current_output(
634                 $this->get_contains_mark_summary(1),
635                 $this->get_contains_submit_button_expectation(true),
636                 $this->get_contains_incorrect_expectation(),
637                 $this->get_does_not_contain_penalty_info_expectation(),
638                 $this->get_does_not_contain_total_penalty_expectation(),
639                 $this->get_does_not_contain_validation_error_expectation());
641         // Finish the attempt.
642         $this->quba->finish_all_questions();
644         // Verify.
645         $this->check_current_state(question_state::$gradedwrong);
646         $this->check_current_mark(1);
647         $this->check_current_output(
648                 $this->get_contains_mark_summary(1),
649                 $this->get_does_not_contain_submit_button_expectation(),
650                 $this->get_contains_incorrect_expectation(),
651                 $this->get_does_not_contain_validation_error_expectation());
652     }
654     public function test_adaptive_numerical_invalid() {
656         // Create a numerical question.
657         $numq = test_question_maker::make_question('numerical', 'pi');
658         $numq->penalty = 0.1;
659         $this->start_attempt_at_question($numq, 'adaptive');
661         // Check the initial state.
662         $this->check_current_state(question_state::$todo);
663         $this->check_current_mark(null);
664         $this->check_current_output(
665                 $this->get_contains_marked_out_of_summary(),
666                 $this->get_contains_submit_button_expectation(true),
667                 $this->get_does_not_contain_feedback_expectation());
669         // Submit a non-numerical answer.
670         $this->process_submission(array('-submit' => 1, 'answer' => 'Pi'));
672         // Verify.
673         $this->check_current_state(question_state::$invalid);
674         $this->check_current_mark(null);
675         $this->check_current_output(
676                 $this->get_contains_marked_out_of_summary(1),
677                 $this->get_contains_submit_button_expectation(true),
678                 $this->get_does_not_contain_correctness_expectation(),
679                 $this->get_does_not_contain_penalty_info_expectation(),
680                 $this->get_does_not_contain_total_penalty_expectation(),
681                 $this->get_contains_validation_error_expectation(),
682                 $this->get_contains_disregarded_info_expectation());
684         // Submit an incorrect answer.
685         $this->process_submission(array('-submit' => 1, 'answer' => '-5'));
687         // Verify.
688         $this->check_current_state(question_state::$todo);
689         $this->check_current_mark(0);
690         $this->check_current_output(
691                 $this->get_contains_mark_summary(0),
692                 $this->get_contains_submit_button_expectation(true),
693                 $this->get_contains_incorrect_expectation(),
694                 $this->get_contains_penalty_info_expectation(0.1),
695                 $this->get_does_not_contain_total_penalty_expectation(),
696                 $this->get_does_not_contain_validation_error_expectation(),
697                 $this->get_does_not_contain_disregarded_info_expectation());
699         // Submit another non-numerical answer.
700         $this->process_submission(array('-submit' => 1, 'answer' => 'Pi*2'));
702         // Verify.
703         $this->check_current_state(question_state::$invalid);
704         $this->check_current_mark(0);
705         $this->check_current_output(
706                 $this->get_contains_mark_summary(0),
707                 $this->get_contains_submit_button_expectation(true),
708                 $this->get_does_not_contain_correctness_expectation(),
709                 $this->get_does_not_contain_penalty_info_expectation(),
710                 $this->get_does_not_contain_total_penalty_expectation(),
711                 $this->get_contains_validation_error_expectation(),
712                 $this->get_contains_disregarded_info_expectation());
714         // Submit the correct answer.
715         $this->process_submission(array('-submit' => 1, 'answer' => '3.14'));
717         // Verify.
718         $this->check_current_state(question_state::$complete);
719         $this->check_current_mark(0.9);
720         $this->check_current_output(
721                 $this->get_contains_mark_summary(0.9),
722                 $this->get_contains_submit_button_expectation(true),
723                 $this->get_contains_correct_expectation(),
724                 $this->get_does_not_contain_penalty_info_expectation(),
725                 $this->get_does_not_contain_total_penalty_expectation(),
726                 $this->get_does_not_contain_validation_error_expectation(),
727                 $this->get_does_not_contain_disregarded_info_expectation());
729         // Submit another non-numerical answer.
730         $this->process_submission(array('-submit' => 1, 'answer' => 'Pi/3'));
732         // Verify.
733         $this->check_current_state(question_state::$invalid);
734         $this->check_current_mark(0.9);
735         $this->check_current_output(
736                 $this->get_contains_mark_summary(0.9),
737                 $this->get_contains_submit_button_expectation(true),
738                 $this->get_does_not_contain_correctness_expectation(),
739                 $this->get_does_not_contain_penalty_info_expectation(),
740                 $this->get_does_not_contain_total_penalty_expectation(),
741                 $this->get_contains_validation_error_expectation(),
742                 $this->get_contains_disregarded_info_expectation());
744         // Finish the attempt.
745         $this->quba->finish_all_questions();
747         // Verify.
748         $this->check_current_state(question_state::$gradedwrong);
749         $this->check_current_mark(0.9);
750         $this->check_current_output(
751                 $this->get_contains_mark_summary(0.9),
752                 $this->get_does_not_contain_submit_button_expectation(),
753                 $this->get_contains_incorrect_expectation(),
754                 $this->get_does_not_contain_validation_error_expectation(),
755                 $this->get_does_not_contain_disregarded_info_expectation());
756     }
758     public function test_adaptive_multianswer() {
760         // Create a multianswer question.
761         $q = test_question_maker::make_question('multianswer', 'twosubq');
762         // To simplify testing, multichoice subquestion's answers are not shuffled.
763         $q->subquestions[2]->shuffleanswers = 0;
764         $choices = array('0' => 'Bow-wow', '1' => 'Wiggly worm', '2' => 'Pussy-cat');
766         $this->start_attempt_at_question($q, 'adaptive', 12);
768         // Check the initial state.
769         $this->check_current_state(question_state::$todo);
770         $this->check_current_mark(null);
771         $this->assertEquals('adaptive',
772                 $this->quba->get_question_attempt($this->slot)->get_behaviour_name());
773         $this->render();
774         $this->check_output_contains_text_input('sub1_answer', '', true);
775         $this->check_output_does_not_contain_text_input_with_class('sub1_answer', 'correct');
776         $this->check_output_does_not_contain_text_input_with_class('sub1_answer', 'partiallycorrect');
777         $this->check_output_does_not_contain_text_input_with_class('sub1_answer', 'incorrect');
778         $this->check_current_output(
779                 $this->get_contains_marked_out_of_summary(),
780                 $this->get_contains_submit_button_expectation(true),
781                 $this->get_does_not_contain_validation_error_expectation(),
782                 $this->get_does_not_contain_feedback_expectation());
783         $this->check_output_contains_selectoptions(
784                 $this->get_contains_select_expectation('sub2_answer', $choices, null, true));
786         // Submit an invalid response.
787         $this->process_submission(array('sub1_answer' => '', 'sub2_answer' => 1, '-submit' => 1));
789         // Verify.
790         $this->check_current_state(question_state::$invalid);
791         $this->check_current_mark(null);
792         $this->check_output_contains_text_input('sub1_answer', '', true);
793         $this->check_current_output(
794                 $this->get_contains_submit_button_expectation(true),
795                 $this->get_does_not_contain_penalty_info_expectation(),
796                 $this->get_does_not_contain_total_penalty_expectation(),
797                 $this->get_contains_disregarded_info_expectation());
798         $this->check_output_contains_selectoptions(
799                 $this->get_contains_select_expectation('sub2_answer', $choices, 1, true));
801         // Check that extract responses will return the reset data.
802         $prefix = $this->quba->get_field_prefix($this->slot);
803         $this->assertEquals(array('sub2_answer' => 1),
804                 $this->quba->extract_responses($this->slot, array($prefix . 'sub2_answer' => 1)));
806         // Submit an incorrect response.
807         $this->process_submission(array('sub1_answer' => 'Dog',
808                 'sub2_answer' => 1, '-submit' => 1));
810         // Verify.
811         $this->check_current_state(question_state::$todo);
812         $this->check_current_mark(0);
813         $this->render();
814         $this->check_output_contains_text_input('sub1_answer', 'Dog', true);
815         $this->check_output_contains_text_input_with_class('sub1_answer', 'incorrect');
816         $this->check_current_output(
817                 $this->get_contains_mark_summary(0),
818                 $this->get_contains_submit_button_expectation(true),
819                 $this->get_contains_incorrect_expectation(),
820                 $this->get_contains_penalty_info_expectation(4.00),
821                 $this->get_does_not_contain_validation_error_expectation());
822         $this->check_output_contains_selectoptions(
823                 $this->get_contains_select_expectation('sub2_answer', $choices, 1, true));
825         // Submit the right answer.
826         $this->process_submission(array('sub1_answer' => 'Owl', 'sub2_answer' => 2, '-submit' => 1));
828         // Verify.
829         $this->check_current_state(question_state::$complete);
830         $this->check_current_mark(8.00);
831         $this->render();
832         $this->check_output_contains_text_input('sub1_answer', 'Owl', true);
833         $this->check_output_contains_text_input_with_class('sub1_answer', 'correct');
834         $this->check_current_output(
835                 $this->get_contains_mark_summary(8.00),
836                 $this->get_contains_submit_button_expectation(true),
837                 $this->get_contains_correct_expectation(),
838                 $this->get_does_not_contain_penalty_info_expectation(),
839                 $this->get_does_not_contain_total_penalty_expectation(),
840                 $this->get_does_not_contain_validation_error_expectation());
841         $this->check_output_contains_selectoptions(
842                 $this->get_contains_select_expectation('sub2_answer', $choices, '2', true));
844         // Finish the attempt.
845         $this->quba->finish_all_questions();
847         // Verify.
848         $this->check_current_state(question_state::$gradedright);
849         $this->check_current_mark(8.00);
850         $this->render();
851         $this->check_output_contains_text_input('sub1_answer', 'Owl', false);
852         $this->check_output_contains_text_input_with_class('sub1_answer', 'correct');
853         $this->check_current_output(
854                 $this->get_contains_mark_summary(8.00),
855                 $this->get_does_not_contain_submit_button_expectation(),
856                 $this->get_contains_correct_expectation(),
857                 $this->get_does_not_contain_validation_error_expectation());
858     }