Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [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_match; 18 19 use question_hint_with_parts; 20 use question_state; 21 22 defined('MOODLE_INTERNAL') || die(); 23 24 global $CFG; 25 require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); 26 27 28 /** 29 * Unit tests for the matching question type. 30 * 31 * @package qtype_match 32 * @copyright 2010 The Open University 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class walkthrough_test extends \qbehaviour_walkthrough_test_base { 36 37 public function test_deferred_feedback_unanswered() { 38 39 // Create a matching question. 40 $m = \test_question_maker::make_question('match'); 41 $m->shufflestems = false; 42 $this->start_attempt_at_question($m, 'deferredfeedback', 4); 43 44 $choiceorder = $m->get_choice_order(); 45 $orderforchoice = array_combine(array_values($choiceorder), array_keys($choiceorder)); 46 $choices = array(0 => get_string('choose') . '...'); 47 foreach ($choiceorder as $key => $choice) { 48 $choices[$key] = $m->choices[$choice]; 49 } 50 51 // Check the initial state. 52 $this->check_current_state(question_state::$todo); 53 $this->check_current_mark(null); 54 $this->check_current_output( 55 $this->get_contains_question_text_expectation($m), 56 $this->get_does_not_contain_feedback_expectation()); 57 $this->check_output_contains_selectoptions( 58 $this->get_contains_select_expectation('sub0', $choices, null, true), 59 $this->get_contains_select_expectation('sub1', $choices, null, true), 60 $this->get_contains_select_expectation('sub2', $choices, null, true), 61 $this->get_contains_select_expectation('sub3', $choices, null, true)); 62 $this->check_step_count(1); 63 64 // Save a blank response. 65 $this->process_submission(array('sub0' => '0', 'sub1' => '0', 66 'sub2' => '0', 'sub3' => '0')); 67 68 // Verify. 69 $this->check_current_state(question_state::$todo); 70 $this->check_current_mark(null); 71 $this->check_current_output( 72 $this->get_contains_question_text_expectation($m), 73 $this->get_does_not_contain_feedback_expectation()); 74 $this->check_output_contains_selectoptions( 75 $this->get_contains_select_expectation('sub0', $choices, null, true), 76 $this->get_contains_select_expectation('sub1', $choices, null, true), 77 $this->get_contains_select_expectation('sub2', $choices, null, true), 78 $this->get_contains_select_expectation('sub3', $choices, null, true)); 79 $this->check_step_count(1); 80 81 // Finish the attempt. 82 $this->quba->finish_all_questions(); 83 84 // Verify. 85 $this->check_current_state(question_state::$gaveup); 86 $this->check_current_mark(null); 87 $this->check_output_contains_selectoptions( 88 $this->get_contains_select_expectation('sub0', $choices, null, false), 89 $this->get_contains_select_expectation('sub1', $choices, null, false), 90 $this->get_contains_select_expectation('sub2', $choices, null, false), 91 $this->get_contains_select_expectation('sub3', $choices, null, false)); 92 } 93 94 public function test_deferred_feedback_partial_answer() { 95 96 // Create a matching question. 97 $m = \test_question_maker::make_question('match'); 98 $m->shufflestems = false; 99 $this->start_attempt_at_question($m, 'deferredfeedback', 4); 100 101 $choiceorder = $m->get_choice_order(); 102 $orderforchoice = array_combine(array_values($choiceorder), array_keys($choiceorder)); 103 $choices = array(0 => get_string('choose') . '...'); 104 foreach ($choiceorder as $key => $choice) { 105 $choices[$key] = $m->choices[$choice]; 106 } 107 108 // Check the initial state. 109 $this->check_current_state(question_state::$todo); 110 $this->check_current_mark(null); 111 $this->check_current_output( 112 $this->get_contains_question_text_expectation($m), 113 $this->get_does_not_contain_feedback_expectation()); 114 $this->check_output_contains_selectoptions( 115 $this->get_contains_select_expectation('sub0', $choices, null, true), 116 $this->get_contains_select_expectation('sub1', $choices, null, true), 117 $this->get_contains_select_expectation('sub2', $choices, null, true), 118 $this->get_contains_select_expectation('sub3', $choices, null, true)); 119 120 // Save a partial response. 121 $this->process_submission(array('sub0' => $orderforchoice[1], 122 'sub1' => $orderforchoice[2], 'sub2' => '0', 'sub3' => '0')); 123 124 // Verify. 125 $this->check_current_state(question_state::$invalid); 126 $this->check_current_mark(null); 127 $this->check_current_output( 128 $this->get_contains_question_text_expectation($m), 129 $this->get_does_not_contain_feedback_expectation()); 130 $this->check_output_contains_selectoptions( 131 $this->get_contains_select_expectation('sub0', $choices, $orderforchoice[1], true), 132 $this->get_contains_select_expectation('sub1', $choices, $orderforchoice[2], true), 133 $this->get_contains_select_expectation('sub2', $choices, null, true), 134 $this->get_contains_select_expectation('sub3', $choices, null, true)); 135 136 // Finish the attempt. 137 $this->quba->finish_all_questions(); 138 139 // Verify. 140 $this->check_current_state(question_state::$gradedpartial); 141 $this->check_current_mark(2); 142 $this->check_current_output( 143 $this->get_contains_partcorrect_expectation()); 144 $this->check_output_contains_selectoptions( 145 $this->get_contains_select_expectation('sub0', $choices, $orderforchoice[1], false), 146 $this->get_contains_select_expectation('sub1', $choices, $orderforchoice[2], false), 147 $this->get_contains_select_expectation('sub2', $choices, null, false), 148 $this->get_contains_select_expectation('sub3', $choices, null, false)); 149 } 150 151 public function test_interactive_correct_no_submit() { 152 153 // Create a matching question. 154 $m = \test_question_maker::make_question('match'); 155 $m->hints = array( 156 new question_hint_with_parts(11, 'This is the first hint.', FORMAT_HTML, false, false), 157 new question_hint_with_parts(12, 'This is the second hint.', FORMAT_HTML, true, true), 158 ); 159 $m->shufflestems = false; 160 $this->start_attempt_at_question($m, 'interactive', 4); 161 162 $choiceorder = $m->get_choice_order(); 163 $orderforchoice = array_combine(array_values($choiceorder), array_keys($choiceorder)); 164 $choices = array(0 => get_string('choose') . '...'); 165 foreach ($choiceorder as $key => $choice) { 166 $choices[$key] = $m->choices[$choice]; 167 } 168 169 // Check the initial state. 170 $this->check_current_state(question_state::$todo); 171 $this->check_current_mark(null); 172 $this->check_current_output( 173 $this->get_contains_submit_button_expectation(true), 174 $this->get_does_not_contain_feedback_expectation(), 175 $this->get_tries_remaining_expectation(3), 176 $this->get_no_hint_visible_expectation()); 177 $this->check_output_contains_selectoptions( 178 $this->get_contains_select_expectation('sub0', $choices, null, true), 179 $this->get_contains_select_expectation('sub1', $choices, null, true), 180 $this->get_contains_select_expectation('sub2', $choices, null, true), 181 $this->get_contains_select_expectation('sub3', $choices, null, true)); 182 183 // Save the right answer. 184 $this->process_submission(array('sub0' => $orderforchoice[1], 185 'sub1' => $orderforchoice[2], 'sub2' => $orderforchoice[2], 186 'sub3' => $orderforchoice[1])); 187 188 // Finish the attempt without clicking check. 189 $this->quba->finish_all_questions(); 190 191 // Verify. 192 $this->check_current_state(question_state::$gradedright); 193 $this->check_current_mark(4); 194 $this->check_current_output( 195 $this->get_does_not_contain_submit_button_expectation(), 196 $this->get_contains_correct_expectation(), 197 $this->get_no_hint_visible_expectation()); 198 $this->check_output_contains_selectoptions( 199 $this->get_contains_select_expectation('sub0', $choices, $orderforchoice[1], false), 200 $this->get_contains_select_expectation('sub1', $choices, $orderforchoice[2], false), 201 $this->get_contains_select_expectation('sub2', $choices, $orderforchoice[2], false), 202 $this->get_contains_select_expectation('sub3', $choices, $orderforchoice[1], false)); 203 } 204 205 public function test_interactive_partial_no_submit() { 206 207 // Create a matching question. 208 $m = \test_question_maker::make_question('match'); 209 $m->hints = array( 210 new question_hint_with_parts(11, 'This is the first hint.', FORMAT_HTML, false, false), 211 new question_hint_with_parts(12, 'This is the second hint.', FORMAT_HTML, true, true), 212 ); 213 $m->shufflestems = false; 214 $this->start_attempt_at_question($m, 'interactive', 4); 215 216 $choiceorder = $m->get_choice_order(); 217 $orderforchoice = array_combine(array_values($choiceorder), array_keys($choiceorder)); 218 $choices = array(0 => get_string('choose') . '...'); 219 foreach ($choiceorder as $key => $choice) { 220 $choices[$key] = $m->choices[$choice]; 221 } 222 223 // Check the initial state. 224 $this->check_current_state(question_state::$todo); 225 $this->check_current_mark(null); 226 $this->check_current_output( 227 $this->get_contains_submit_button_expectation(true), 228 $this->get_does_not_contain_feedback_expectation(), 229 $this->get_tries_remaining_expectation(3), 230 $this->get_no_hint_visible_expectation()); 231 $this->check_output_contains_selectoptions( 232 $this->get_contains_select_expectation('sub0', $choices, null, true), 233 $this->get_contains_select_expectation('sub1', $choices, null, true), 234 $this->get_contains_select_expectation('sub2', $choices, null, true), 235 $this->get_contains_select_expectation('sub3', $choices, null, true)); 236 237 // Save the right answer. 238 $this->process_submission(array('sub0' => $orderforchoice[1], 239 'sub1' => $orderforchoice[2], 'sub2' => $orderforchoice[1], 240 'sub3' => '0')); 241 242 // Finish the attempt without clicking check. 243 $this->quba->finish_all_questions(); 244 245 // Verify. 246 $this->check_current_state(question_state::$gradedpartial); 247 $this->check_current_mark(2); 248 $this->check_current_output( 249 $this->get_does_not_contain_submit_button_expectation(), 250 $this->get_contains_partcorrect_expectation(), 251 $this->get_no_hint_visible_expectation()); 252 $this->check_output_contains_selectoptions( 253 $this->get_contains_select_expectation('sub0', $choices, $orderforchoice[1], false), 254 $this->get_contains_select_expectation('sub1', $choices, $orderforchoice[2], false), 255 $this->get_contains_select_expectation('sub2', $choices, $orderforchoice[1], false), 256 $this->get_contains_select_expectation('sub3', $choices, null, false)); 257 } 258 259 public function test_interactive_with_invalid() { 260 261 // Create a matching question. 262 $m = \test_question_maker::make_question('match'); 263 $m->hints = array( 264 new question_hint_with_parts(11, 'This is the first hint.', FORMAT_HTML, false, false), 265 new question_hint_with_parts(12, 'This is the second hint.', FORMAT_HTML, true, true), 266 ); 267 $m->shufflestems = false; 268 $this->start_attempt_at_question($m, 'interactive', 4); 269 270 $choiceorder = $m->get_choice_order(); 271 $orderforchoice = array_combine(array_values($choiceorder), array_keys($choiceorder)); 272 $choices = array(0 => get_string('choose') . '...'); 273 foreach ($choiceorder as $key => $choice) { 274 $choices[$key] = $m->choices[$choice]; 275 } 276 277 // Check the initial state. 278 $this->check_current_state(question_state::$todo); 279 $this->check_current_mark(null); 280 $this->check_current_output( 281 $this->get_contains_submit_button_expectation(true), 282 $this->get_does_not_contain_feedback_expectation(), 283 $this->get_tries_remaining_expectation(3), 284 $this->get_no_hint_visible_expectation()); 285 $this->check_output_contains_selectoptions( 286 $this->get_contains_select_expectation('sub0', $choices, null, true), 287 $this->get_contains_select_expectation('sub1', $choices, null, true), 288 $this->get_contains_select_expectation('sub2', $choices, null, true), 289 $this->get_contains_select_expectation('sub3', $choices, null, true)); 290 291 // Try to submit an invalid answer. 292 $this->process_submission(array('sub0' => '0', 293 'sub1' => '0', 'sub2' => '0', 294 'sub3' => '0', '-submit' => '1')); 295 296 // Verify. 297 $this->check_current_state(question_state::$invalid); 298 $this->check_current_mark(null); 299 $this->check_current_output( 300 $this->get_contains_submit_button_expectation(true), 301 $this->get_does_not_contain_feedback_expectation(), 302 $this->get_invalid_answer_expectation(), 303 $this->get_no_hint_visible_expectation()); 304 $this->check_output_contains_selectoptions( 305 $this->get_contains_select_expectation('sub0', $choices, null, true), 306 $this->get_contains_select_expectation('sub1', $choices, null, true), 307 $this->get_contains_select_expectation('sub2', $choices, null, true), 308 $this->get_contains_select_expectation('sub3', $choices, null, true)); 309 310 // Now submit the right answer. 311 $this->process_submission(array('sub0' => $orderforchoice[1], 312 'sub1' => $orderforchoice[2], 'sub2' => $orderforchoice[2], 313 'sub3' => $orderforchoice[1], '-submit' => '1')); 314 315 // Verify. 316 $this->check_current_state(question_state::$gradedright); 317 $this->check_current_mark(4); 318 $this->check_current_output( 319 $this->get_does_not_contain_submit_button_expectation(), 320 $this->get_contains_correct_expectation(), 321 $this->get_no_hint_visible_expectation()); 322 $this->check_output_contains_selectoptions( 323 $this->get_contains_select_expectation('sub0', $choices, $orderforchoice[1], false), 324 $this->get_contains_select_expectation('sub1', $choices, $orderforchoice[2], false), 325 $this->get_contains_select_expectation('sub2', $choices, $orderforchoice[2], false), 326 $this->get_contains_select_expectation('sub3', $choices, $orderforchoice[1], false)); 327 } 328 329 public function test_match_with_tricky_html_choices() { 330 331 // Create a matching question. 332 $m = \test_question_maker::make_question('match'); 333 $m->stems = array( 334 1 => '(1, 2]', 335 2 => '[1, 2]', 336 3 => '[1, 2)', 337 ); 338 $m->choices = array( 339 1 => '1 < x ≤ 2', 340 2 => '1 ≤ x ≤ 2', 341 3 => '1 ≤ x < 2', 342 ); 343 $m->right = array(1 => 1, 2 => 2, 3 => 3); 344 $m->shufflestems = false; 345 $this->start_attempt_at_question($m, 'deferredfeedback', 3); 346 347 $choiceorder = $m->get_choice_order(); 348 $orderforchoice = array_combine(array_values($choiceorder), array_keys($choiceorder)); 349 $choices = array(0 => get_string('choose') . '...'); 350 foreach ($choiceorder as $key => $choice) { 351 $choices[$key] = $m->choices[$choice]; 352 } 353 354 // Check the initial state. 355 $this->check_current_state(question_state::$todo); 356 $this->check_current_mark(null); 357 // Do not use check_output_contains_selectoptions as there are multibyte characters ('1 ≤ x ≤ 2') that 358 // seem to be read as ascii ('1 ≤ x ≤ 2') in this test. 359 $this->check_current_output( 360 $this->get_contains_select_expectation('sub0', $choices, null, true), 361 $this->get_contains_select_expectation('sub1', $choices, null, true), 362 $this->get_contains_select_expectation('sub2', $choices, null, true), 363 $this->get_contains_question_text_expectation($m), 364 $this->get_does_not_contain_feedback_expectation()); 365 $this->check_step_count(1); 366 367 $rightresponse = array('sub0' => $orderforchoice[1], 368 'sub1' => $orderforchoice[2], 'sub2' => $orderforchoice[3]); 369 $rightresponsesummary = 370 '(1, 2] -> 1 < x ≤ 2; [1, 2] -> 1 ≤ x ≤ 2; [1, 2) -> 1 ≤ x < 2'; 371 372 $this->process_submission($rightresponse); 373 $this->finish(); 374 375 $this->assertEquals($rightresponsesummary, $m->summarise_response($rightresponse)); 376 377 $this->displayoptions->history = 1; 378 $this->check_current_output( 379 new \question_pattern_expectation('/' . 380 preg_quote(htmlspecialchars($rightresponsesummary), '/') . '/')); 381 } 382 383 public function test_match_clear_wrong() { 384 385 // Create a matching question. 386 $m = \test_question_maker::make_question('match'); 387 $m->hints = array( 388 new question_hint_with_parts(11, 'This is the first hint.', FORMAT_HTML, false, true), 389 new question_hint_with_parts(12, 'This is the second hint.', FORMAT_HTML, true, true), 390 ); 391 $m->shufflestems = false; 392 $this->start_attempt_at_question($m, 'interactive', 4); 393 394 $choiceorder = $m->get_choice_order(); 395 $orderforchoice = array_combine(array_values($choiceorder), array_keys($choiceorder)); 396 $choices = array(0 => get_string('choose') . '...'); 397 foreach ($choiceorder as $key => $choice) { 398 $choices[$key] = $m->choices[$choice]; 399 } 400 401 // Check the initial state. 402 $this->check_current_state(question_state::$todo); 403 $this->check_current_mark(null); 404 $this->check_current_output( 405 $this->get_contains_submit_button_expectation(true), 406 $this->get_does_not_contain_feedback_expectation(), 407 $this->get_tries_remaining_expectation(3), 408 $this->get_no_hint_visible_expectation()); 409 $this->check_output_contains_selectoptions( 410 $this->get_contains_select_expectation('sub0', $choices, null, true), 411 $this->get_contains_select_expectation('sub1', $choices, null, true), 412 $this->get_contains_select_expectation('sub2', $choices, null, true), 413 $this->get_contains_select_expectation('sub3', $choices, null, true)); 414 415 // Submit a completely wrong response. 416 $this->process_submission(array('sub0' => $orderforchoice[3], 417 'sub1' => $orderforchoice[3], 'sub2' => $orderforchoice[3], 418 'sub3' => $orderforchoice[3], '-submit' => 1)); 419 420 // Verify. 421 $this->check_current_state(question_state::$todo); 422 $this->check_current_mark(null); 423 $this->check_current_output( 424 $this->get_contains_hidden_expectation( 425 $this->quba->get_field_prefix($this->slot) . 'sub0', '0'), 426 $this->get_contains_hidden_expectation( 427 $this->quba->get_field_prefix($this->slot) . 'sub1', '0'), 428 $this->get_contains_hidden_expectation( 429 $this->quba->get_field_prefix($this->slot) . 'sub2', '0'), 430 $this->get_contains_hidden_expectation( 431 $this->quba->get_field_prefix($this->slot) . 'sub3', '0'), 432 $this->get_does_not_contain_submit_button_expectation(), 433 $this->get_contains_hint_expectation('This is the first hint.')); 434 $this->check_output_contains_selectoptions( 435 $this->get_contains_select_expectation('sub0', $choices, $orderforchoice[3], false), 436 $this->get_contains_select_expectation('sub1', $choices, $orderforchoice[3], false), 437 $this->get_contains_select_expectation('sub2', $choices, $orderforchoice[3], false), 438 $this->get_contains_select_expectation('sub3', $choices, $orderforchoice[3], false)); 439 440 // Try again. 441 $this->process_submission(array('sub0' => 0, 442 'sub1' => 0, 'sub2' => 0, 443 'sub3' => 0, '-tryagain' => 1)); 444 445 // Verify. 446 $this->check_current_state(question_state::$todo); 447 $this->check_current_mark(null); 448 $this->check_current_output( 449 $this->get_contains_submit_button_expectation(true), 450 $this->get_does_not_contain_feedback_expectation(), 451 $this->get_tries_remaining_expectation(2), 452 $this->get_no_hint_visible_expectation()); 453 $this->check_output_contains_selectoptions( 454 $this->get_contains_select_expectation('sub0', $choices, null, true), 455 $this->get_contains_select_expectation('sub1', $choices, null, true), 456 $this->get_contains_select_expectation('sub2', $choices, null, true), 457 $this->get_contains_select_expectation('sub3', $choices, null, true)); 458 459 // Submit a partially wrong response. 460 $this->process_submission(array('sub0' => $orderforchoice[3], 461 'sub1' => $orderforchoice[3], 'sub2' => $orderforchoice[2], 462 'sub3' => $orderforchoice[1], '-submit' => 1)); 463 464 // Verify. 465 $this->check_current_state(question_state::$todo); 466 $this->check_current_mark(null); 467 $this->check_current_output( 468 $this->get_contains_hidden_expectation( 469 $this->quba->get_field_prefix($this->slot) . 'sub0', '0'), 470 $this->get_contains_hidden_expectation( 471 $this->quba->get_field_prefix($this->slot) . 'sub1', '0'), 472 $this->get_contains_hidden_expectation( 473 $this->quba->get_field_prefix($this->slot) . 'sub2', $orderforchoice[2]), 474 $this->get_contains_hidden_expectation( 475 $this->quba->get_field_prefix($this->slot) . 'sub3', $orderforchoice[1]), 476 $this->get_does_not_contain_submit_button_expectation(), 477 $this->get_contains_hint_expectation('This is the second hint.')); 478 $this->check_output_contains_selectoptions( 479 $this->get_contains_select_expectation('sub0', $choices, $orderforchoice[3], false), 480 $this->get_contains_select_expectation('sub1', $choices, $orderforchoice[3], false), 481 $this->get_contains_select_expectation('sub2', $choices, $orderforchoice[2], false), 482 $this->get_contains_select_expectation('sub3', $choices, $orderforchoice[1], false)); 483 484 // Try again. 485 $this->process_submission(array('sub0' => 0, 486 'sub1' => 0, 'sub2' => $orderforchoice[2], 487 'sub3' => $orderforchoice[1], '-tryagain' => 1)); 488 489 // Verify. 490 $this->check_current_state(question_state::$todo); 491 $this->check_current_mark(null); 492 $this->check_current_output( 493 $this->get_contains_submit_button_expectation(true), 494 $this->get_does_not_contain_feedback_expectation(), 495 $this->get_tries_remaining_expectation(1), 496 $this->get_no_hint_visible_expectation()); 497 $this->check_output_contains_selectoptions( 498 $this->get_contains_select_expectation('sub0', $choices, null, true), 499 $this->get_contains_select_expectation('sub1', $choices, null, true), 500 $this->get_contains_select_expectation('sub2', $choices, $orderforchoice[2], true), 501 $this->get_contains_select_expectation('sub3', $choices, $orderforchoice[1], true)); 502 503 } 504 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body