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