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