Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
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 * Quiz events tests. 19 * 20 * @package mod_quiz 21 * @category test 22 * @copyright 2013 Adrian Greeve 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 global $CFG; 29 require_once($CFG->dirroot . '/mod/quiz/attemptlib.php'); 30 31 /** 32 * Unit tests for quiz events. 33 * 34 * @copyright 2013 Adrian Greeve 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 class mod_quiz_structure_testcase extends advanced_testcase { 38 39 /** 40 * Create a course with an empty quiz. 41 * @return array with three elements quiz, cm and course. 42 */ 43 protected function prepare_quiz_data() { 44 45 $this->resetAfterTest(true); 46 47 // Create a course. 48 $course = $this->getDataGenerator()->create_course(); 49 50 // Make a quiz. 51 $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); 52 53 $quiz = $quizgenerator->create_instance(array('course' => $course->id, 'questionsperpage' => 0, 54 'grade' => 100.0, 'sumgrades' => 2, 'preferredbehaviour' => 'immediatefeedback')); 55 56 $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id); 57 58 return array($quiz, $cm, $course); 59 } 60 61 /** 62 * Creat a test quiz. 63 * 64 * $layout looks like this: 65 * $layout = array( 66 * 'Heading 1' 67 * array('TF1', 1, 'truefalse'), 68 * 'Heading 2*' 69 * array('TF2', 2, 'truefalse'), 70 * ); 71 * That is, either a string, which represents a section heading, 72 * or an array that represents a question. 73 * 74 * If the section heading ends with *, that section is shuffled. 75 * 76 * The elements in the question array are name, page number, and question type. 77 * 78 * @param array $layout as above. 79 * @return quiz the created quiz. 80 */ 81 protected function create_test_quiz($layout) { 82 list($quiz, $cm, $course) = $this->prepare_quiz_data(); 83 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 84 $cat = $questiongenerator->create_question_category(); 85 86 $headings = array(); 87 $slot = 1; 88 $lastpage = 0; 89 foreach ($layout as $item) { 90 if (is_string($item)) { 91 if (isset($headings[$lastpage + 1])) { 92 throw new coding_exception('Sections cannot be empty.'); 93 } 94 $headings[$lastpage + 1] = $item; 95 96 } else { 97 list($name, $page, $qtype) = $item; 98 if ($page < 1 || !($page == $lastpage + 1 || 99 (!isset($headings[$lastpage + 1]) && $page == $lastpage))) { 100 throw new coding_exception('Page numbers wrong.'); 101 } 102 $q = $questiongenerator->create_question($qtype, null, 103 array('name' => $name, 'category' => $cat->id)); 104 105 quiz_add_quiz_question($q->id, $quiz, $page); 106 $lastpage = $page; 107 } 108 } 109 110 $quizobj = new quiz($quiz, $cm, $course); 111 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 112 if (isset($headings[1])) { 113 list($heading, $shuffle) = $this->parse_section_name($headings[1]); 114 $sections = $structure->get_sections(); 115 $firstsection = reset($sections); 116 $structure->set_section_heading($firstsection->id, $heading); 117 $structure->set_section_shuffle($firstsection->id, $shuffle); 118 unset($headings[1]); 119 } 120 121 foreach ($headings as $startpage => $heading) { 122 list($heading, $shuffle) = $this->parse_section_name($heading); 123 $id = $structure->add_section_heading($startpage, $heading); 124 $structure->set_section_shuffle($id, $shuffle); 125 } 126 127 return $quizobj; 128 } 129 130 /** 131 * Verify that the given layout matches that expected. 132 * @param array $expectedlayout as for $layout in {@link create_test_quiz()}. 133 * @param \mod_quiz\structure $structure the structure to test. 134 */ 135 protected function assert_quiz_layout($expectedlayout, \mod_quiz\structure $structure) { 136 $sections = $structure->get_sections(); 137 138 $slot = 1; 139 foreach ($expectedlayout as $item) { 140 if (is_string($item)) { 141 list($heading, $shuffle) = $this->parse_section_name($item); 142 $section = array_shift($sections); 143 144 if ($slot > 1 && $section->heading == '' && $section->firstslot == 1) { 145 // The array $expectedlayout did not contain default first quiz section, so skip over it. 146 $section = array_shift($sections); 147 } 148 149 $this->assertEquals($slot, $section->firstslot); 150 $this->assertEquals($heading, $section->heading); 151 $this->assertEquals($shuffle, $section->shufflequestions); 152 153 } else { 154 list($name, $page, $qtype) = $item; 155 $question = $structure->get_question_in_slot($slot); 156 $this->assertEquals($name, $question->name); 157 $this->assertEquals($slot, $question->slot, 'Slot number wrong for question ' . $name); 158 $this->assertEquals($qtype, $question->qtype, 'Question type wrong for question ' . $name); 159 $this->assertEquals($page, $question->page, 'Page number wrong for question ' . $name); 160 161 $slot += 1; 162 } 163 } 164 165 if ($slot - 1 != count($structure->get_slots())) { 166 $this->fail('The quiz contains more slots than expected.'); 167 } 168 169 if (!empty($sections)) { 170 $section = array_shift($sections); 171 if ($section->heading != '' || $section->firstslot != 1) { 172 $this->fail('Unexpected section (' . $section->heading .') found in the quiz.'); 173 } 174 } 175 } 176 177 /** 178 * Parse the section name, optionally followed by a * to mean shuffle, as 179 * used by create_test_quiz as assert_quiz_layout. 180 * @param string $heading the heading. 181 * @return array with two elements, the heading and the shuffle setting. 182 */ 183 protected function parse_section_name($heading) { 184 if (substr($heading, -1) == '*') { 185 return array(substr($heading, 0, -1), 1); 186 } else { 187 return array($heading, 0); 188 } 189 } 190 191 public function test_get_quiz_slots() { 192 $quizobj = $this->create_test_quiz(array( 193 array('TF1', 1, 'truefalse'), 194 array('TF2', 1, 'truefalse'), 195 )); 196 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 197 198 // Are the correct slots returned? 199 $slots = $structure->get_slots(); 200 $this->assertCount(2, $structure->get_slots()); 201 } 202 203 public function test_quiz_has_one_section_by_default() { 204 $quizobj = $this->create_test_quiz(array( 205 array('TF1', 1, 'truefalse'), 206 )); 207 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 208 209 $sections = $structure->get_sections(); 210 $this->assertCount(1, $sections); 211 212 $section = array_shift($sections); 213 $this->assertEquals(1, $section->firstslot); 214 $this->assertEquals('', $section->heading); 215 $this->assertEquals(0, $section->shufflequestions); 216 } 217 218 public function test_get_sections() { 219 $quizobj = $this->create_test_quiz(array( 220 'Heading 1*', 221 array('TF1', 1, 'truefalse'), 222 'Heading 2*', 223 array('TF2', 2, 'truefalse'), 224 )); 225 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 226 227 $sections = $structure->get_sections(); 228 $this->assertCount(2, $sections); 229 230 $section = array_shift($sections); 231 $this->assertEquals(1, $section->firstslot); 232 $this->assertEquals('Heading 1', $section->heading); 233 $this->assertEquals(1, $section->shufflequestions); 234 235 $section = array_shift($sections); 236 $this->assertEquals(2, $section->firstslot); 237 $this->assertEquals('Heading 2', $section->heading); 238 $this->assertEquals(1, $section->shufflequestions); 239 } 240 241 public function test_remove_section_heading() { 242 $quizobj = $this->create_test_quiz(array( 243 'Heading 1', 244 array('TF1', 1, 'truefalse'), 245 'Heading 2', 246 array('TF2', 2, 'truefalse'), 247 )); 248 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 249 250 $sections = $structure->get_sections(); 251 $section = end($sections); 252 $structure->remove_section_heading($section->id); 253 254 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 255 $this->assert_quiz_layout(array( 256 'Heading 1', 257 array('TF1', 1, 'truefalse'), 258 array('TF2', 2, 'truefalse'), 259 ), $structure); 260 } 261 262 public function test_cannot_remove_first_section() { 263 $quizobj = $this->create_test_quiz(array( 264 'Heading 1', 265 array('TF1', 1, 'truefalse'), 266 )); 267 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 268 269 $sections = $structure->get_sections(); 270 $section = reset($sections); 271 272 $this->expectException(coding_exception::class); 273 $structure->remove_section_heading($section->id); 274 } 275 276 public function test_move_slot_to_the_same_place_does_nothing() { 277 $quizobj = $this->create_test_quiz(array( 278 array('TF1', 1, 'truefalse'), 279 array('TF2', 1, 'truefalse'), 280 array('TF3', 2, 'truefalse'), 281 )); 282 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 283 284 $idtomove = $structure->get_question_in_slot(2)->slotid; 285 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 286 $structure->move_slot($idtomove, $idmoveafter, '1'); 287 288 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 289 $this->assert_quiz_layout(array( 290 array('TF1', 1, 'truefalse'), 291 array('TF2', 1, 'truefalse'), 292 array('TF3', 2, 'truefalse'), 293 ), $structure); 294 } 295 296 public function test_move_slot_end_of_one_page_to_start_of_next() { 297 $quizobj = $this->create_test_quiz(array( 298 array('TF1', 1, 'truefalse'), 299 array('TF2', 1, 'truefalse'), 300 array('TF3', 2, 'truefalse'), 301 )); 302 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 303 304 $idtomove = $structure->get_question_in_slot(2)->slotid; 305 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 306 $structure->move_slot($idtomove, $idmoveafter, '2'); 307 308 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 309 $this->assert_quiz_layout(array( 310 array('TF1', 1, 'truefalse'), 311 array('TF2', 2, 'truefalse'), 312 array('TF3', 2, 'truefalse'), 313 ), $structure); 314 } 315 316 public function test_move_last_slot_to_previous_page_emptying_the_last_page() { 317 $quizobj = $this->create_test_quiz(array( 318 array('TF1', 1, 'truefalse'), 319 array('TF2', 2, 'truefalse'), 320 )); 321 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 322 323 $idtomove = $structure->get_question_in_slot(2)->slotid; 324 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 325 $structure->move_slot($idtomove, $idmoveafter, '1'); 326 327 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 328 $this->assert_quiz_layout(array( 329 array('TF1', 1, 'truefalse'), 330 array('TF2', 1, 'truefalse'), 331 ), $structure); 332 } 333 334 public function test_end_of_one_section_to_start_of_next() { 335 $quizobj = $this->create_test_quiz(array( 336 array('TF1', 1, 'truefalse'), 337 array('TF2', 1, 'truefalse'), 338 'Heading', 339 array('TF3', 2, 'truefalse'), 340 )); 341 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 342 343 $idtomove = $structure->get_question_in_slot(2)->slotid; 344 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 345 $structure->move_slot($idtomove, $idmoveafter, '2'); 346 347 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 348 $this->assert_quiz_layout(array( 349 array('TF1', 1, 'truefalse'), 350 'Heading', 351 array('TF2', 2, 'truefalse'), 352 array('TF3', 2, 'truefalse'), 353 ), $structure); 354 } 355 356 public function test_start_of_one_section_to_end_of_previous() { 357 $quizobj = $this->create_test_quiz(array( 358 array('TF1', 1, 'truefalse'), 359 'Heading', 360 array('TF2', 2, 'truefalse'), 361 array('TF3', 2, 'truefalse'), 362 )); 363 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 364 365 $idtomove = $structure->get_question_in_slot(2)->slotid; 366 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 367 $structure->move_slot($idtomove, $idmoveafter, '1'); 368 369 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 370 $this->assert_quiz_layout(array( 371 array('TF1', 1, 'truefalse'), 372 array('TF2', 1, 'truefalse'), 373 'Heading', 374 array('TF3', 2, 'truefalse'), 375 ), $structure); 376 } 377 public function test_move_slot_on_same_page() { 378 $quizobj = $this->create_test_quiz(array( 379 array('TF1', 1, 'truefalse'), 380 array('TF2', 1, 'truefalse'), 381 array('TF3', 1, 'truefalse'), 382 )); 383 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 384 385 $idtomove = $structure->get_question_in_slot(2)->slotid; 386 $idmoveafter = $structure->get_question_in_slot(3)->slotid; 387 $structure->move_slot($idtomove, $idmoveafter, '1'); 388 389 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 390 $this->assert_quiz_layout(array( 391 array('TF1', 1, 'truefalse'), 392 array('TF3', 1, 'truefalse'), 393 array('TF2', 1, 'truefalse'), 394 ), $structure); 395 } 396 397 public function test_move_slot_up_onto_previous_page() { 398 $quizobj = $this->create_test_quiz(array( 399 array('TF1', 1, 'truefalse'), 400 array('TF2', 2, 'truefalse'), 401 array('TF3', 2, 'truefalse'), 402 )); 403 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 404 405 $idtomove = $structure->get_question_in_slot(3)->slotid; 406 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 407 $structure->move_slot($idtomove, $idmoveafter, '1'); 408 409 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 410 $this->assert_quiz_layout(array( 411 array('TF1', 1, 'truefalse'), 412 array('TF3', 1, 'truefalse'), 413 array('TF2', 2, 'truefalse'), 414 ), $structure); 415 } 416 417 public function test_move_slot_emptying_a_page_renumbers_pages() { 418 $quizobj = $this->create_test_quiz(array( 419 array('TF1', 1, 'truefalse'), 420 array('TF2', 2, 'truefalse'), 421 array('TF3', 3, 'truefalse'), 422 )); 423 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 424 425 $idtomove = $structure->get_question_in_slot(2)->slotid; 426 $idmoveafter = $structure->get_question_in_slot(3)->slotid; 427 $structure->move_slot($idtomove, $idmoveafter, '3'); 428 429 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 430 $this->assert_quiz_layout(array( 431 array('TF1', 1, 'truefalse'), 432 array('TF3', 2, 'truefalse'), 433 array('TF2', 2, 'truefalse'), 434 ), $structure); 435 } 436 437 public function test_move_slot_too_small_page_number_detected() { 438 $quizobj = $this->create_test_quiz(array( 439 array('TF1', 1, 'truefalse'), 440 array('TF2', 2, 'truefalse'), 441 array('TF3', 3, 'truefalse'), 442 )); 443 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 444 445 $idtomove = $structure->get_question_in_slot(3)->slotid; 446 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 447 $this->expectException(coding_exception::class); 448 $structure->move_slot($idtomove, $idmoveafter, '1'); 449 } 450 451 public function test_move_slot_too_large_page_number_detected() { 452 $quizobj = $this->create_test_quiz(array( 453 array('TF1', 1, 'truefalse'), 454 array('TF2', 2, 'truefalse'), 455 array('TF3', 3, 'truefalse'), 456 )); 457 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 458 459 $idtomove = $structure->get_question_in_slot(1)->slotid; 460 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 461 $this->expectException(coding_exception::class); 462 $structure->move_slot($idtomove, $idmoveafter, '4'); 463 } 464 465 public function test_move_slot_within_section() { 466 $quizobj = $this->create_test_quiz(array( 467 'Heading 1', 468 array('TF1', 1, 'truefalse'), 469 array('TF2', 1, 'truefalse'), 470 'Heading 2', 471 array('TF3', 2, 'truefalse'), 472 )); 473 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 474 475 $idtomove = $structure->get_question_in_slot(1)->slotid; 476 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 477 $structure->move_slot($idtomove, $idmoveafter, '1'); 478 479 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 480 $this->assert_quiz_layout(array( 481 'Heading 1', 482 array('TF2', 1, 'truefalse'), 483 array('TF1', 1, 'truefalse'), 484 'Heading 2', 485 array('TF3', 2, 'truefalse'), 486 ), $structure); 487 } 488 489 public function test_move_slot_to_new_section() { 490 $quizobj = $this->create_test_quiz(array( 491 'Heading 1', 492 array('TF1', 1, 'truefalse'), 493 array('TF2', 1, 'truefalse'), 494 'Heading 2', 495 array('TF3', 2, 'truefalse'), 496 )); 497 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 498 499 $idtomove = $structure->get_question_in_slot(2)->slotid; 500 $idmoveafter = $structure->get_question_in_slot(3)->slotid; 501 $structure->move_slot($idtomove, $idmoveafter, '2'); 502 503 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 504 $this->assert_quiz_layout(array( 505 'Heading 1', 506 array('TF1', 1, 'truefalse'), 507 'Heading 2', 508 array('TF3', 2, 'truefalse'), 509 array('TF2', 2, 'truefalse'), 510 ), $structure); 511 } 512 513 public function test_move_slot_to_start() { 514 $quizobj = $this->create_test_quiz(array( 515 'Heading 1', 516 array('TF1', 1, 'truefalse'), 517 'Heading 2', 518 array('TF2', 2, 'truefalse'), 519 array('TF3', 2, 'truefalse'), 520 )); 521 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 522 523 $idtomove = $structure->get_question_in_slot(3)->slotid; 524 $structure->move_slot($idtomove, 0, '1'); 525 526 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 527 $this->assert_quiz_layout(array( 528 'Heading 1', 529 array('TF3', 1, 'truefalse'), 530 array('TF1', 1, 'truefalse'), 531 'Heading 2', 532 array('TF2', 2, 'truefalse'), 533 ), $structure); 534 } 535 536 public function test_move_slot_down_to_start_of_second_section() { 537 $quizobj = $this->create_test_quiz(array( 538 'Heading 1', 539 array('TF1', 1, 'truefalse'), 540 array('TF2', 1, 'truefalse'), 541 'Heading 2', 542 array('TF3', 2, 'truefalse'), 543 )); 544 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 545 546 $idtomove = $structure->get_question_in_slot(2)->slotid; 547 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 548 $structure->move_slot($idtomove, $idmoveafter, '2'); 549 550 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 551 $this->assert_quiz_layout(array( 552 'Heading 1', 553 array('TF1', 1, 'truefalse'), 554 'Heading 2', 555 array('TF2', 2, 'truefalse'), 556 array('TF3', 2, 'truefalse'), 557 ), $structure); 558 } 559 560 public function test_move_first_slot_down_to_start_of_page_2() { 561 $quizobj = $this->create_test_quiz(array( 562 'Heading 1', 563 array('TF1', 1, 'truefalse'), 564 array('TF2', 2, 'truefalse'), 565 )); 566 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 567 568 $idtomove = $structure->get_question_in_slot(1)->slotid; 569 $structure->move_slot($idtomove, 0, '2'); 570 571 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 572 $this->assert_quiz_layout(array( 573 'Heading 1', 574 array('TF1', 1, 'truefalse'), 575 array('TF2', 1, 'truefalse'), 576 ), $structure); 577 } 578 579 public function test_move_first_slot_to_same_place_on_page_1() { 580 $quizobj = $this->create_test_quiz(array( 581 'Heading 1', 582 array('TF1', 1, 'truefalse'), 583 array('TF2', 2, 'truefalse'), 584 )); 585 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 586 587 $idtomove = $structure->get_question_in_slot(1)->slotid; 588 $structure->move_slot($idtomove, 0, '1'); 589 590 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 591 $this->assert_quiz_layout(array( 592 'Heading 1', 593 array('TF1', 1, 'truefalse'), 594 array('TF2', 2, 'truefalse'), 595 ), $structure); 596 } 597 598 public function test_move_first_slot_to_before_page_1() { 599 $quizobj = $this->create_test_quiz(array( 600 'Heading 1', 601 array('TF1', 1, 'truefalse'), 602 array('TF2', 2, 'truefalse'), 603 )); 604 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 605 606 $idtomove = $structure->get_question_in_slot(1)->slotid; 607 $structure->move_slot($idtomove, 0, ''); 608 609 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 610 $this->assert_quiz_layout(array( 611 'Heading 1', 612 array('TF1', 1, 'truefalse'), 613 array('TF2', 2, 'truefalse'), 614 ), $structure); 615 } 616 617 public function test_move_slot_up_to_start_of_second_section() { 618 $quizobj = $this->create_test_quiz(array( 619 'Heading 1', 620 array('TF1', 1, 'truefalse'), 621 'Heading 2', 622 array('TF2', 2, 'truefalse'), 623 'Heading 3', 624 array('TF3', 3, 'truefalse'), 625 array('TF4', 3, 'truefalse'), 626 )); 627 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 628 629 $idtomove = $structure->get_question_in_slot(3)->slotid; 630 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 631 $structure->move_slot($idtomove, $idmoveafter, '2'); 632 633 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 634 $this->assert_quiz_layout(array( 635 'Heading 1', 636 array('TF1', 1, 'truefalse'), 637 'Heading 2', 638 array('TF3', 2, 'truefalse'), 639 array('TF2', 2, 'truefalse'), 640 'Heading 3', 641 array('TF4', 3, 'truefalse'), 642 ), $structure); 643 } 644 645 public function test_move_slot_does_not_violate_heading_unique_key() { 646 $quizobj = $this->create_test_quiz(array( 647 'Heading 1', 648 array('TF1', 1, 'truefalse'), 649 'Heading 2', 650 array('TF2', 2, 'truefalse'), 651 'Heading 3', 652 array('TF3', 3, 'truefalse'), 653 array('TF4', 3, 'truefalse'), 654 )); 655 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 656 657 $idtomove = $structure->get_question_in_slot(4)->slotid; 658 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 659 $structure->move_slot($idtomove, $idmoveafter, 1); 660 661 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 662 $this->assert_quiz_layout(array( 663 'Heading 1', 664 array('TF1', 1, 'truefalse'), 665 array('TF4', 1, 'truefalse'), 666 'Heading 2', 667 array('TF2', 2, 'truefalse'), 668 'Heading 3', 669 array('TF3', 3, 'truefalse'), 670 ), $structure); 671 } 672 673 public function test_quiz_remove_slot() { 674 $quizobj = $this->create_test_quiz(array( 675 array('TF1', 1, 'truefalse'), 676 array('TF2', 1, 'truefalse'), 677 'Heading 2', 678 array('TF3', 2, 'truefalse'), 679 )); 680 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 681 682 $structure->remove_slot(2); 683 684 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 685 $this->assert_quiz_layout(array( 686 array('TF1', 1, 'truefalse'), 687 'Heading 2', 688 array('TF3', 2, 'truefalse'), 689 ), $structure); 690 } 691 692 public function test_quiz_removing_a_random_question_deletes_the_question() { 693 global $DB; 694 695 $this->resetAfterTest(true); 696 $this->setAdminUser(); 697 698 $quizobj = $this->create_test_quiz(array( 699 array('TF1', 1, 'truefalse'), 700 )); 701 702 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 703 $cat = $questiongenerator->create_question_category(); 704 quiz_add_random_questions($quizobj->get_quiz(), 1, $cat->id, 1, false); 705 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 706 $randomq = $DB->get_record('question', array('qtype' => 'random')); 707 708 $structure->remove_slot(2); 709 710 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 711 $this->assert_quiz_layout(array( 712 array('TF1', 1, 'truefalse'), 713 ), $structure); 714 $this->assertFalse($DB->record_exists('question', array('id' => $randomq->id))); 715 } 716 717 /** 718 * Unit test to make sue it is not possible to remove all slots in a section at once. 719 */ 720 public function test_cannot_remove_all_slots_in_a_section() { 721 $quizobj = $this->create_test_quiz(array( 722 array('TF1', 1, 'truefalse'), 723 array('TF2', 1, 'truefalse'), 724 'Heading 2', 725 array('TF3', 2, 'truefalse'), 726 )); 727 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 728 729 $structure->remove_slot(1); 730 $this->expectException(coding_exception::class); 731 $structure->remove_slot(2); 732 } 733 734 public function test_cannot_remove_last_slot_in_a_section() { 735 $quizobj = $this->create_test_quiz(array( 736 array('TF1', 1, 'truefalse'), 737 array('TF2', 1, 'truefalse'), 738 'Heading 2', 739 array('TF3', 2, 'truefalse'), 740 )); 741 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 742 743 $this->expectException(coding_exception::class); 744 $structure->remove_slot(3); 745 } 746 747 public function test_can_remove_last_question_in_a_quiz() { 748 $quizobj = $this->create_test_quiz(array( 749 'Heading 1', 750 array('TF1', 1, 'truefalse'), 751 )); 752 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 753 754 $structure->remove_slot(1); 755 756 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 757 $cat = $questiongenerator->create_question_category(); 758 $q = $questiongenerator->create_question('truefalse', null, 759 array('name' => 'TF2', 'category' => $cat->id)); 760 761 quiz_add_quiz_question($q->id, $quizobj->get_quiz(), 0); 762 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 763 764 $this->assert_quiz_layout(array( 765 'Heading 1', 766 array('TF2', 1, 'truefalse'), 767 ), $structure); 768 } 769 770 public function test_add_question_updates_headings() { 771 $quizobj = $this->create_test_quiz(array( 772 array('TF1', 1, 'truefalse'), 773 'Heading 2', 774 array('TF2', 2, 'truefalse'), 775 )); 776 777 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 778 $cat = $questiongenerator->create_question_category(); 779 $q = $questiongenerator->create_question('truefalse', null, 780 array('name' => 'TF3', 'category' => $cat->id)); 781 782 quiz_add_quiz_question($q->id, $quizobj->get_quiz(), 1); 783 784 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 785 $this->assert_quiz_layout(array( 786 array('TF1', 1, 'truefalse'), 787 array('TF3', 1, 'truefalse'), 788 'Heading 2', 789 array('TF2', 2, 'truefalse'), 790 ), $structure); 791 } 792 793 public function test_add_question_updates_headings_even_with_one_question_sections() { 794 $quizobj = $this->create_test_quiz(array( 795 'Heading 1', 796 array('TF1', 1, 'truefalse'), 797 'Heading 2', 798 array('TF2', 2, 'truefalse'), 799 'Heading 3', 800 array('TF3', 3, 'truefalse'), 801 )); 802 803 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 804 $cat = $questiongenerator->create_question_category(); 805 $q = $questiongenerator->create_question('truefalse', null, 806 array('name' => 'TF4', 'category' => $cat->id)); 807 808 quiz_add_quiz_question($q->id, $quizobj->get_quiz(), 1); 809 810 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 811 $this->assert_quiz_layout(array( 812 'Heading 1', 813 array('TF1', 1, 'truefalse'), 814 array('TF4', 1, 'truefalse'), 815 'Heading 2', 816 array('TF2', 2, 'truefalse'), 817 'Heading 3', 818 array('TF3', 3, 'truefalse'), 819 ), $structure); 820 } 821 822 public function test_add_question_at_end_does_not_update_headings() { 823 $quizobj = $this->create_test_quiz(array( 824 array('TF1', 1, 'truefalse'), 825 'Heading 2', 826 array('TF2', 2, 'truefalse'), 827 )); 828 829 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 830 $cat = $questiongenerator->create_question_category(); 831 $q = $questiongenerator->create_question('truefalse', null, 832 array('name' => 'TF3', 'category' => $cat->id)); 833 834 quiz_add_quiz_question($q->id, $quizobj->get_quiz(), 0); 835 836 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 837 $this->assert_quiz_layout(array( 838 array('TF1', 1, 'truefalse'), 839 'Heading 2', 840 array('TF2', 2, 'truefalse'), 841 array('TF3', 2, 'truefalse'), 842 ), $structure); 843 } 844 845 public function test_remove_page_break() { 846 $quizobj = $this->create_test_quiz(array( 847 array('TF1', 1, 'truefalse'), 848 array('TF2', 2, 'truefalse'), 849 )); 850 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 851 852 $slotid = $structure->get_question_in_slot(2)->slotid; 853 $slots = $structure->update_page_break($slotid, \mod_quiz\repaginate::LINK); 854 855 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 856 $this->assert_quiz_layout(array( 857 array('TF1', 1, 'truefalse'), 858 array('TF2', 1, 'truefalse'), 859 ), $structure); 860 } 861 862 public function test_add_page_break() { 863 $quizobj = $this->create_test_quiz(array( 864 array('TF1', 1, 'truefalse'), 865 array('TF2', 1, 'truefalse'), 866 )); 867 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 868 869 $slotid = $structure->get_question_in_slot(2)->slotid; 870 $slots = $structure->update_page_break($slotid, \mod_quiz\repaginate::UNLINK); 871 872 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 873 $this->assert_quiz_layout(array( 874 array('TF1', 1, 'truefalse'), 875 array('TF2', 2, 'truefalse'), 876 ), $structure); 877 } 878 879 public function test_update_question_dependency() { 880 $quizobj = $this->create_test_quiz(array( 881 array('TF1', 1, 'truefalse'), 882 array('TF2', 1, 'truefalse'), 883 )); 884 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 885 886 // Test adding a dependency. 887 $slotid = $structure->get_slot_id_for_slot(2); 888 $structure->update_question_dependency($slotid, true); 889 890 // Having called update page break, we need to reload $structure. 891 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 892 $this->assertEquals(1, $structure->is_question_dependent_on_previous_slot(2)); 893 894 // Test removing a dependency. 895 $structure->update_question_dependency($slotid, false); 896 897 // Having called update page break, we need to reload $structure. 898 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 899 $this->assertEquals(0, $structure->is_question_dependent_on_previous_slot(2)); 900 } 901 902 /** 903 * Data provider for the get_slot_tags_for_slot test. 904 */ 905 public function get_slot_tags_for_slot_test_cases() { 906 return [ 907 'incorrect slot id' => [ 908 'layout' => [ 909 ['TF1', 1, 'truefalse'], 910 ['TF2', 1, 'truefalse'], 911 ['TF3', 1, 'truefalse'] 912 ], 913 'tagnames' => [ 914 ['foo'], 915 ['bar'], 916 ['baz'] 917 ], 918 'slotnumber' => null, 919 'expected' => [] 920 ], 921 'no tags' => [ 922 'layout' => [ 923 ['TF1', 1, 'truefalse'], 924 ['TF2', 1, 'truefalse'], 925 ['TF3', 1, 'truefalse'] 926 ], 927 'tagnames' => [ 928 ['foo'], 929 [], 930 ['baz'] 931 ], 932 'slotnumber' => 2, 933 'expected' => [] 934 ], 935 'one tag 1' => [ 936 'layout' => [ 937 ['TF1', 1, 'truefalse'], 938 ['TF2', 1, 'truefalse'], 939 ['TF3', 1, 'truefalse'] 940 ], 941 'tagnames' => [ 942 ['foo'], 943 ['bar'], 944 ['baz'] 945 ], 946 'slotnumber' => 1, 947 'expected' => ['foo'] 948 ], 949 'one tag 2' => [ 950 'layout' => [ 951 ['TF1', 1, 'truefalse'], 952 ['TF2', 1, 'truefalse'], 953 ['TF3', 1, 'truefalse'] 954 ], 955 'tagnames' => [ 956 ['foo'], 957 ['bar'], 958 ['baz'] 959 ], 960 'slotnumber' => 2, 961 'expected' => ['bar'] 962 ], 963 'multiple tags 1' => [ 964 'layout' => [ 965 ['TF1', 1, 'truefalse'], 966 ['TF2', 1, 'truefalse'], 967 ['TF3', 1, 'truefalse'] 968 ], 969 'tagnames' => [ 970 ['foo', 'bar'], 971 ['bar'], 972 ['baz'] 973 ], 974 'slotnumber' => 1, 975 'expected' => ['foo', 'bar'] 976 ], 977 'multiple tags 2' => [ 978 'layout' => [ 979 ['TF1', 1, 'truefalse'], 980 ['TF2', 1, 'truefalse'], 981 ['TF3', 1, 'truefalse'] 982 ], 983 'tagnames' => [ 984 ['foo', 'bar'], 985 ['bar', 'baz'], 986 ['baz'] 987 ], 988 'slotnumber' => 2, 989 'expected' => ['bar', 'baz'] 990 ] 991 ]; 992 } 993 994 /** 995 * @dataProvider get_slot_tags_for_slot_test_cases() 996 * @param array $layout Quiz layout for create_test_quiz function 997 * @param array $tagnames Tags to create for each question slot 998 * @param int $slotnumber The slot number to select tags from 999 * @param string[] $expected The tags expected for the given $slotnumber 1000 */ 1001 public function test_get_slot_tags_for_slot($layout, $tagnames, $slotnumber, $expected) { 1002 global $DB; 1003 $this->resetAfterTest(); 1004 1005 $quiz = $this->create_test_quiz($layout); 1006 $structure = \mod_quiz\structure::create_for_quiz($quiz); 1007 $collid = core_tag_area::get_collection('core', 'question'); 1008 $slottagrecords = []; 1009 1010 if (is_null($slotnumber)) { 1011 // Null slot number means to create a non-existent slot id. 1012 $slot = $structure->get_last_slot(); 1013 $slotid = $slot->id + 100; 1014 } else { 1015 $slot = $structure->get_slot_by_number($slotnumber); 1016 $slotid = $slot->id; 1017 } 1018 1019 foreach ($tagnames as $index => $slottagnames) { 1020 $tagslotnumber = $index + 1; 1021 $tagslotid = $structure->get_slot_id_for_slot($tagslotnumber); 1022 $tags = core_tag_tag::create_if_missing($collid, $slottagnames); 1023 $records = array_map(function($tag) use ($tagslotid) { 1024 return (object) [ 1025 'slotid' => $tagslotid, 1026 'tagid' => $tag->id, 1027 'tagname' => $tag->name 1028 ]; 1029 }, array_values($tags)); 1030 $slottagrecords = array_merge($slottagrecords, $records); 1031 } 1032 1033 $DB->insert_records('quiz_slot_tags', $slottagrecords); 1034 1035 $actualslottags = $structure->get_slot_tags_for_slot_id($slotid); 1036 $actual = array_map(function($slottag) { 1037 return $slottag->tagname; 1038 }, $actualslottags); 1039 1040 sort($expected); 1041 sort($actual); 1042 1043 $this->assertEquals($expected, $actual); 1044 } 1045 1046 /** 1047 * Test for can_add_random_questions. 1048 */ 1049 public function test_can_add_random_questions() { 1050 $this->resetAfterTest(); 1051 1052 $quiz = $this->create_test_quiz([]); 1053 $course = $quiz->get_course(); 1054 1055 $generator = $this->getDataGenerator(); 1056 $teacher = $generator->create_and_enrol($course, 'editingteacher'); 1057 $noneditingteacher = $generator->create_and_enrol($course, 'teacher'); 1058 1059 $this->setUser($teacher); 1060 $structure = \mod_quiz\structure::create_for_quiz($quiz); 1061 $this->assertTrue($structure->can_add_random_questions()); 1062 1063 $this->setUser($noneditingteacher); 1064 $structure = \mod_quiz\structure::create_for_quiz($quiz); 1065 $this->assertFalse($structure->can_add_random_questions()); 1066 } 1067 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body