See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 /** 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 /** 263 * @expectedException coding_exception 264 */ 265 public function test_cannot_remove_first_section() { 266 $quizobj = $this->create_test_quiz(array( 267 'Heading 1', 268 array('TF1', 1, 'truefalse'), 269 )); 270 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 271 272 $sections = $structure->get_sections(); 273 $section = reset($sections); 274 275 $structure->remove_section_heading($section->id); 276 } 277 278 public function test_move_slot_to_the_same_place_does_nothing() { 279 $quizobj = $this->create_test_quiz(array( 280 array('TF1', 1, 'truefalse'), 281 array('TF2', 1, 'truefalse'), 282 array('TF3', 2, 'truefalse'), 283 )); 284 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 285 286 $idtomove = $structure->get_question_in_slot(2)->slotid; 287 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 288 $structure->move_slot($idtomove, $idmoveafter, '1'); 289 290 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 291 $this->assert_quiz_layout(array( 292 array('TF1', 1, 'truefalse'), 293 array('TF2', 1, 'truefalse'), 294 array('TF3', 2, 'truefalse'), 295 ), $structure); 296 } 297 298 public function test_move_slot_end_of_one_page_to_start_of_next() { 299 $quizobj = $this->create_test_quiz(array( 300 array('TF1', 1, 'truefalse'), 301 array('TF2', 1, 'truefalse'), 302 array('TF3', 2, 'truefalse'), 303 )); 304 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 305 306 $idtomove = $structure->get_question_in_slot(2)->slotid; 307 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 308 $structure->move_slot($idtomove, $idmoveafter, '2'); 309 310 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 311 $this->assert_quiz_layout(array( 312 array('TF1', 1, 'truefalse'), 313 array('TF2', 2, 'truefalse'), 314 array('TF3', 2, 'truefalse'), 315 ), $structure); 316 } 317 318 public function test_move_last_slot_to_previous_page_emptying_the_last_page() { 319 $quizobj = $this->create_test_quiz(array( 320 array('TF1', 1, 'truefalse'), 321 array('TF2', 2, 'truefalse'), 322 )); 323 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 324 325 $idtomove = $structure->get_question_in_slot(2)->slotid; 326 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 327 $structure->move_slot($idtomove, $idmoveafter, '1'); 328 329 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 330 $this->assert_quiz_layout(array( 331 array('TF1', 1, 'truefalse'), 332 array('TF2', 1, 'truefalse'), 333 ), $structure); 334 } 335 336 public function test_end_of_one_section_to_start_of_next() { 337 $quizobj = $this->create_test_quiz(array( 338 array('TF1', 1, 'truefalse'), 339 array('TF2', 1, 'truefalse'), 340 'Heading', 341 array('TF3', 2, 'truefalse'), 342 )); 343 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 344 345 $idtomove = $structure->get_question_in_slot(2)->slotid; 346 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 347 $structure->move_slot($idtomove, $idmoveafter, '2'); 348 349 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 350 $this->assert_quiz_layout(array( 351 array('TF1', 1, 'truefalse'), 352 'Heading', 353 array('TF2', 2, 'truefalse'), 354 array('TF3', 2, 'truefalse'), 355 ), $structure); 356 } 357 358 public function test_start_of_one_section_to_end_of_previous() { 359 $quizobj = $this->create_test_quiz(array( 360 array('TF1', 1, 'truefalse'), 361 'Heading', 362 array('TF2', 2, 'truefalse'), 363 array('TF3', 2, 'truefalse'), 364 )); 365 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 366 367 $idtomove = $structure->get_question_in_slot(2)->slotid; 368 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 369 $structure->move_slot($idtomove, $idmoveafter, '1'); 370 371 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 372 $this->assert_quiz_layout(array( 373 array('TF1', 1, 'truefalse'), 374 array('TF2', 1, 'truefalse'), 375 'Heading', 376 array('TF3', 2, 'truefalse'), 377 ), $structure); 378 } 379 public function test_move_slot_on_same_page() { 380 $quizobj = $this->create_test_quiz(array( 381 array('TF1', 1, 'truefalse'), 382 array('TF2', 1, 'truefalse'), 383 array('TF3', 1, 'truefalse'), 384 )); 385 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 386 387 $idtomove = $structure->get_question_in_slot(2)->slotid; 388 $idmoveafter = $structure->get_question_in_slot(3)->slotid; 389 $structure->move_slot($idtomove, $idmoveafter, '1'); 390 391 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 392 $this->assert_quiz_layout(array( 393 array('TF1', 1, 'truefalse'), 394 array('TF3', 1, 'truefalse'), 395 array('TF2', 1, 'truefalse'), 396 ), $structure); 397 } 398 399 public function test_move_slot_up_onto_previous_page() { 400 $quizobj = $this->create_test_quiz(array( 401 array('TF1', 1, 'truefalse'), 402 array('TF2', 2, 'truefalse'), 403 array('TF3', 2, 'truefalse'), 404 )); 405 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 406 407 $idtomove = $structure->get_question_in_slot(3)->slotid; 408 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 409 $structure->move_slot($idtomove, $idmoveafter, '1'); 410 411 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 412 $this->assert_quiz_layout(array( 413 array('TF1', 1, 'truefalse'), 414 array('TF3', 1, 'truefalse'), 415 array('TF2', 2, 'truefalse'), 416 ), $structure); 417 } 418 419 public function test_move_slot_emptying_a_page_renumbers_pages() { 420 $quizobj = $this->create_test_quiz(array( 421 array('TF1', 1, 'truefalse'), 422 array('TF2', 2, 'truefalse'), 423 array('TF3', 3, 'truefalse'), 424 )); 425 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 426 427 $idtomove = $structure->get_question_in_slot(2)->slotid; 428 $idmoveafter = $structure->get_question_in_slot(3)->slotid; 429 $structure->move_slot($idtomove, $idmoveafter, '3'); 430 431 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 432 $this->assert_quiz_layout(array( 433 array('TF1', 1, 'truefalse'), 434 array('TF3', 2, 'truefalse'), 435 array('TF2', 2, 'truefalse'), 436 ), $structure); 437 } 438 439 /** 440 * @expectedException coding_exception 441 */ 442 public function test_move_slot_too_small_page_number_detected() { 443 $quizobj = $this->create_test_quiz(array( 444 array('TF1', 1, 'truefalse'), 445 array('TF2', 2, 'truefalse'), 446 array('TF3', 3, 'truefalse'), 447 )); 448 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 449 450 $idtomove = $structure->get_question_in_slot(3)->slotid; 451 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 452 $structure->move_slot($idtomove, $idmoveafter, '1'); 453 } 454 455 /** 456 * @expectedException coding_exception 457 */ 458 public function test_move_slot_too_large_page_number_detected() { 459 $quizobj = $this->create_test_quiz(array( 460 array('TF1', 1, 'truefalse'), 461 array('TF2', 2, 'truefalse'), 462 array('TF3', 3, 'truefalse'), 463 )); 464 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 465 466 $idtomove = $structure->get_question_in_slot(1)->slotid; 467 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 468 $structure->move_slot($idtomove, $idmoveafter, '4'); 469 } 470 471 public function test_move_slot_within_section() { 472 $quizobj = $this->create_test_quiz(array( 473 'Heading 1', 474 array('TF1', 1, 'truefalse'), 475 array('TF2', 1, 'truefalse'), 476 'Heading 2', 477 array('TF3', 2, 'truefalse'), 478 )); 479 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 480 481 $idtomove = $structure->get_question_in_slot(1)->slotid; 482 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 483 $structure->move_slot($idtomove, $idmoveafter, '1'); 484 485 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 486 $this->assert_quiz_layout(array( 487 'Heading 1', 488 array('TF2', 1, 'truefalse'), 489 array('TF1', 1, 'truefalse'), 490 'Heading 2', 491 array('TF3', 2, 'truefalse'), 492 ), $structure); 493 } 494 495 public function test_move_slot_to_new_section() { 496 $quizobj = $this->create_test_quiz(array( 497 'Heading 1', 498 array('TF1', 1, 'truefalse'), 499 array('TF2', 1, 'truefalse'), 500 'Heading 2', 501 array('TF3', 2, 'truefalse'), 502 )); 503 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 504 505 $idtomove = $structure->get_question_in_slot(2)->slotid; 506 $idmoveafter = $structure->get_question_in_slot(3)->slotid; 507 $structure->move_slot($idtomove, $idmoveafter, '2'); 508 509 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 510 $this->assert_quiz_layout(array( 511 'Heading 1', 512 array('TF1', 1, 'truefalse'), 513 'Heading 2', 514 array('TF3', 2, 'truefalse'), 515 array('TF2', 2, 'truefalse'), 516 ), $structure); 517 } 518 519 public function test_move_slot_to_start() { 520 $quizobj = $this->create_test_quiz(array( 521 'Heading 1', 522 array('TF1', 1, 'truefalse'), 523 'Heading 2', 524 array('TF2', 2, 'truefalse'), 525 array('TF3', 2, 'truefalse'), 526 )); 527 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 528 529 $idtomove = $structure->get_question_in_slot(3)->slotid; 530 $structure->move_slot($idtomove, 0, '1'); 531 532 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 533 $this->assert_quiz_layout(array( 534 'Heading 1', 535 array('TF3', 1, 'truefalse'), 536 array('TF1', 1, 'truefalse'), 537 'Heading 2', 538 array('TF2', 2, 'truefalse'), 539 ), $structure); 540 } 541 542 public function test_move_slot_down_to_start_of_second_section() { 543 $quizobj = $this->create_test_quiz(array( 544 'Heading 1', 545 array('TF1', 1, 'truefalse'), 546 array('TF2', 1, 'truefalse'), 547 'Heading 2', 548 array('TF3', 2, 'truefalse'), 549 )); 550 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 551 552 $idtomove = $structure->get_question_in_slot(2)->slotid; 553 $idmoveafter = $structure->get_question_in_slot(2)->slotid; 554 $structure->move_slot($idtomove, $idmoveafter, '2'); 555 556 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 557 $this->assert_quiz_layout(array( 558 'Heading 1', 559 array('TF1', 1, 'truefalse'), 560 'Heading 2', 561 array('TF2', 2, 'truefalse'), 562 array('TF3', 2, 'truefalse'), 563 ), $structure); 564 } 565 566 public function test_move_first_slot_down_to_start_of_page_2() { 567 $quizobj = $this->create_test_quiz(array( 568 'Heading 1', 569 array('TF1', 1, 'truefalse'), 570 array('TF2', 2, 'truefalse'), 571 )); 572 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 573 574 $idtomove = $structure->get_question_in_slot(1)->slotid; 575 $structure->move_slot($idtomove, 0, '2'); 576 577 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 578 $this->assert_quiz_layout(array( 579 'Heading 1', 580 array('TF1', 1, 'truefalse'), 581 array('TF2', 1, 'truefalse'), 582 ), $structure); 583 } 584 585 public function test_move_first_slot_to_same_place_on_page_1() { 586 $quizobj = $this->create_test_quiz(array( 587 'Heading 1', 588 array('TF1', 1, 'truefalse'), 589 array('TF2', 2, 'truefalse'), 590 )); 591 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 592 593 $idtomove = $structure->get_question_in_slot(1)->slotid; 594 $structure->move_slot($idtomove, 0, '1'); 595 596 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 597 $this->assert_quiz_layout(array( 598 'Heading 1', 599 array('TF1', 1, 'truefalse'), 600 array('TF2', 2, 'truefalse'), 601 ), $structure); 602 } 603 604 public function test_move_first_slot_to_before_page_1() { 605 $quizobj = $this->create_test_quiz(array( 606 'Heading 1', 607 array('TF1', 1, 'truefalse'), 608 array('TF2', 2, 'truefalse'), 609 )); 610 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 611 612 $idtomove = $structure->get_question_in_slot(1)->slotid; 613 $structure->move_slot($idtomove, 0, ''); 614 615 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 616 $this->assert_quiz_layout(array( 617 'Heading 1', 618 array('TF1', 1, 'truefalse'), 619 array('TF2', 2, 'truefalse'), 620 ), $structure); 621 } 622 623 public function test_move_slot_up_to_start_of_second_section() { 624 $quizobj = $this->create_test_quiz(array( 625 'Heading 1', 626 array('TF1', 1, 'truefalse'), 627 'Heading 2', 628 array('TF2', 2, 'truefalse'), 629 'Heading 3', 630 array('TF3', 3, 'truefalse'), 631 array('TF4', 3, 'truefalse'), 632 )); 633 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 634 635 $idtomove = $structure->get_question_in_slot(3)->slotid; 636 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 637 $structure->move_slot($idtomove, $idmoveafter, '2'); 638 639 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 640 $this->assert_quiz_layout(array( 641 'Heading 1', 642 array('TF1', 1, 'truefalse'), 643 'Heading 2', 644 array('TF3', 2, 'truefalse'), 645 array('TF2', 2, 'truefalse'), 646 'Heading 3', 647 array('TF4', 3, 'truefalse'), 648 ), $structure); 649 } 650 651 public function test_move_slot_does_not_violate_heading_unique_key() { 652 $quizobj = $this->create_test_quiz(array( 653 'Heading 1', 654 array('TF1', 1, 'truefalse'), 655 'Heading 2', 656 array('TF2', 2, 'truefalse'), 657 'Heading 3', 658 array('TF3', 3, 'truefalse'), 659 array('TF4', 3, 'truefalse'), 660 )); 661 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 662 663 $idtomove = $structure->get_question_in_slot(4)->slotid; 664 $idmoveafter = $structure->get_question_in_slot(1)->slotid; 665 $structure->move_slot($idtomove, $idmoveafter, 1); 666 667 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 668 $this->assert_quiz_layout(array( 669 'Heading 1', 670 array('TF1', 1, 'truefalse'), 671 array('TF4', 1, 'truefalse'), 672 'Heading 2', 673 array('TF2', 2, 'truefalse'), 674 'Heading 3', 675 array('TF3', 3, 'truefalse'), 676 ), $structure); 677 } 678 679 public function test_quiz_remove_slot() { 680 $quizobj = $this->create_test_quiz(array( 681 array('TF1', 1, 'truefalse'), 682 array('TF2', 1, 'truefalse'), 683 'Heading 2', 684 array('TF3', 2, 'truefalse'), 685 )); 686 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 687 688 $structure->remove_slot(2); 689 690 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 691 $this->assert_quiz_layout(array( 692 array('TF1', 1, 'truefalse'), 693 'Heading 2', 694 array('TF3', 2, 'truefalse'), 695 ), $structure); 696 } 697 698 public function test_quiz_removing_a_random_question_deletes_the_question() { 699 global $DB; 700 701 $this->resetAfterTest(true); 702 $this->setAdminUser(); 703 704 $quizobj = $this->create_test_quiz(array( 705 array('TF1', 1, 'truefalse'), 706 )); 707 708 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 709 $cat = $questiongenerator->create_question_category(); 710 quiz_add_random_questions($quizobj->get_quiz(), 1, $cat->id, 1, false); 711 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 712 $randomq = $DB->get_record('question', array('qtype' => 'random')); 713 714 $structure->remove_slot(2); 715 716 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 717 $this->assert_quiz_layout(array( 718 array('TF1', 1, 'truefalse'), 719 ), $structure); 720 $this->assertFalse($DB->record_exists('question', array('id' => $randomq->id))); 721 } 722 723 /** 724 * Unit test to make sue it is not possible to remove all slots in a section at once. 725 * 726 * @expectedException coding_exception 727 */ 728 public function test_cannot_remove_all_slots_in_a_section() { 729 $quizobj = $this->create_test_quiz(array( 730 array('TF1', 1, 'truefalse'), 731 array('TF2', 1, 'truefalse'), 732 'Heading 2', 733 array('TF3', 2, 'truefalse'), 734 )); 735 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 736 737 $structure->remove_slot(1); 738 $structure->remove_slot(2); 739 } 740 741 /** 742 * @expectedException coding_exception 743 */ 744 public function test_cannot_remove_last_slot_in_a_section() { 745 $quizobj = $this->create_test_quiz(array( 746 array('TF1', 1, 'truefalse'), 747 array('TF2', 1, 'truefalse'), 748 'Heading 2', 749 array('TF3', 2, 'truefalse'), 750 )); 751 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 752 753 $structure->remove_slot(3); 754 } 755 756 public function test_can_remove_last_question_in_a_quiz() { 757 $quizobj = $this->create_test_quiz(array( 758 'Heading 1', 759 array('TF1', 1, 'truefalse'), 760 )); 761 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 762 763 $structure->remove_slot(1); 764 765 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 766 $cat = $questiongenerator->create_question_category(); 767 $q = $questiongenerator->create_question('truefalse', null, 768 array('name' => 'TF2', 'category' => $cat->id)); 769 770 quiz_add_quiz_question($q->id, $quizobj->get_quiz(), 0); 771 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 772 773 $this->assert_quiz_layout(array( 774 'Heading 1', 775 array('TF2', 1, 'truefalse'), 776 ), $structure); 777 } 778 779 public function test_add_question_updates_headings() { 780 $quizobj = $this->create_test_quiz(array( 781 array('TF1', 1, 'truefalse'), 782 'Heading 2', 783 array('TF2', 2, 'truefalse'), 784 )); 785 786 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 787 $cat = $questiongenerator->create_question_category(); 788 $q = $questiongenerator->create_question('truefalse', null, 789 array('name' => 'TF3', 'category' => $cat->id)); 790 791 quiz_add_quiz_question($q->id, $quizobj->get_quiz(), 1); 792 793 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 794 $this->assert_quiz_layout(array( 795 array('TF1', 1, 'truefalse'), 796 array('TF3', 1, 'truefalse'), 797 'Heading 2', 798 array('TF2', 2, 'truefalse'), 799 ), $structure); 800 } 801 802 public function test_add_question_updates_headings_even_with_one_question_sections() { 803 $quizobj = $this->create_test_quiz(array( 804 'Heading 1', 805 array('TF1', 1, 'truefalse'), 806 'Heading 2', 807 array('TF2', 2, 'truefalse'), 808 'Heading 3', 809 array('TF3', 3, 'truefalse'), 810 )); 811 812 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 813 $cat = $questiongenerator->create_question_category(); 814 $q = $questiongenerator->create_question('truefalse', null, 815 array('name' => 'TF4', 'category' => $cat->id)); 816 817 quiz_add_quiz_question($q->id, $quizobj->get_quiz(), 1); 818 819 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 820 $this->assert_quiz_layout(array( 821 'Heading 1', 822 array('TF1', 1, 'truefalse'), 823 array('TF4', 1, 'truefalse'), 824 'Heading 2', 825 array('TF2', 2, 'truefalse'), 826 'Heading 3', 827 array('TF3', 3, 'truefalse'), 828 ), $structure); 829 } 830 831 public function test_add_question_at_end_does_not_update_headings() { 832 $quizobj = $this->create_test_quiz(array( 833 array('TF1', 1, 'truefalse'), 834 'Heading 2', 835 array('TF2', 2, 'truefalse'), 836 )); 837 838 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 839 $cat = $questiongenerator->create_question_category(); 840 $q = $questiongenerator->create_question('truefalse', null, 841 array('name' => 'TF3', 'category' => $cat->id)); 842 843 quiz_add_quiz_question($q->id, $quizobj->get_quiz(), 0); 844 845 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 846 $this->assert_quiz_layout(array( 847 array('TF1', 1, 'truefalse'), 848 'Heading 2', 849 array('TF2', 2, 'truefalse'), 850 array('TF3', 2, 'truefalse'), 851 ), $structure); 852 } 853 854 public function test_remove_page_break() { 855 $quizobj = $this->create_test_quiz(array( 856 array('TF1', 1, 'truefalse'), 857 array('TF2', 2, 'truefalse'), 858 )); 859 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 860 861 $slotid = $structure->get_question_in_slot(2)->slotid; 862 $slots = $structure->update_page_break($slotid, \mod_quiz\repaginate::LINK); 863 864 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 865 $this->assert_quiz_layout(array( 866 array('TF1', 1, 'truefalse'), 867 array('TF2', 1, 'truefalse'), 868 ), $structure); 869 } 870 871 public function test_add_page_break() { 872 $quizobj = $this->create_test_quiz(array( 873 array('TF1', 1, 'truefalse'), 874 array('TF2', 1, 'truefalse'), 875 )); 876 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 877 878 $slotid = $structure->get_question_in_slot(2)->slotid; 879 $slots = $structure->update_page_break($slotid, \mod_quiz\repaginate::UNLINK); 880 881 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 882 $this->assert_quiz_layout(array( 883 array('TF1', 1, 'truefalse'), 884 array('TF2', 2, 'truefalse'), 885 ), $structure); 886 } 887 888 public function test_update_question_dependency() { 889 $quizobj = $this->create_test_quiz(array( 890 array('TF1', 1, 'truefalse'), 891 array('TF2', 1, 'truefalse'), 892 )); 893 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 894 895 // Test adding a dependency. 896 $slotid = $structure->get_slot_id_for_slot(2); 897 $structure->update_question_dependency($slotid, true); 898 899 // Having called update page break, we need to reload $structure. 900 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 901 $this->assertEquals(1, $structure->is_question_dependent_on_previous_slot(2)); 902 903 // Test removing a dependency. 904 $structure->update_question_dependency($slotid, false); 905 906 // Having called update page break, we need to reload $structure. 907 $structure = \mod_quiz\structure::create_for_quiz($quizobj); 908 $this->assertEquals(0, $structure->is_question_dependent_on_previous_slot(2)); 909 } 910 911 /** 912 * Data provider for the get_slot_tags_for_slot test. 913 */ 914 public function get_slot_tags_for_slot_test_cases() { 915 return [ 916 'incorrect slot id' => [ 917 'layout' => [ 918 ['TF1', 1, 'truefalse'], 919 ['TF2', 1, 'truefalse'], 920 ['TF3', 1, 'truefalse'] 921 ], 922 'tagnames' => [ 923 ['foo'], 924 ['bar'], 925 ['baz'] 926 ], 927 'slotnumber' => null, 928 'expected' => [] 929 ], 930 'no tags' => [ 931 'layout' => [ 932 ['TF1', 1, 'truefalse'], 933 ['TF2', 1, 'truefalse'], 934 ['TF3', 1, 'truefalse'] 935 ], 936 'tagnames' => [ 937 ['foo'], 938 [], 939 ['baz'] 940 ], 941 'slotnumber' => 2, 942 'expected' => [] 943 ], 944 'one tag 1' => [ 945 'layout' => [ 946 ['TF1', 1, 'truefalse'], 947 ['TF2', 1, 'truefalse'], 948 ['TF3', 1, 'truefalse'] 949 ], 950 'tagnames' => [ 951 ['foo'], 952 ['bar'], 953 ['baz'] 954 ], 955 'slotnumber' => 1, 956 'expected' => ['foo'] 957 ], 958 'one tag 2' => [ 959 'layout' => [ 960 ['TF1', 1, 'truefalse'], 961 ['TF2', 1, 'truefalse'], 962 ['TF3', 1, 'truefalse'] 963 ], 964 'tagnames' => [ 965 ['foo'], 966 ['bar'], 967 ['baz'] 968 ], 969 'slotnumber' => 2, 970 'expected' => ['bar'] 971 ], 972 'multiple tags 1' => [ 973 'layout' => [ 974 ['TF1', 1, 'truefalse'], 975 ['TF2', 1, 'truefalse'], 976 ['TF3', 1, 'truefalse'] 977 ], 978 'tagnames' => [ 979 ['foo', 'bar'], 980 ['bar'], 981 ['baz'] 982 ], 983 'slotnumber' => 1, 984 'expected' => ['foo', 'bar'] 985 ], 986 'multiple tags 2' => [ 987 'layout' => [ 988 ['TF1', 1, 'truefalse'], 989 ['TF2', 1, 'truefalse'], 990 ['TF3', 1, 'truefalse'] 991 ], 992 'tagnames' => [ 993 ['foo', 'bar'], 994 ['bar', 'baz'], 995 ['baz'] 996 ], 997 'slotnumber' => 2, 998 'expected' => ['bar', 'baz'] 999 ] 1000 ]; 1001 } 1002 1003 /** 1004 * @dataProvider get_slot_tags_for_slot_test_cases() 1005 * @param array $layout Quiz layout for create_test_quiz function 1006 * @param array $tagnames Tags to create for each question slot 1007 * @param int $slotnumber The slot number to select tags from 1008 * @param string[] $expected The tags expected for the given $slotnumber 1009 */ 1010 public function test_get_slot_tags_for_slot($layout, $tagnames, $slotnumber, $expected) { 1011 global $DB; 1012 $this->resetAfterTest(); 1013 1014 $quiz = $this->create_test_quiz($layout); 1015 $structure = \mod_quiz\structure::create_for_quiz($quiz); 1016 $collid = core_tag_area::get_collection('core', 'question'); 1017 $slottagrecords = []; 1018 1019 if (is_null($slotnumber)) { 1020 // Null slot number means to create a non-existent slot id. 1021 $slot = $structure->get_last_slot(); 1022 $slotid = $slot->id + 100; 1023 } else { 1024 $slot = $structure->get_slot_by_number($slotnumber); 1025 $slotid = $slot->id; 1026 } 1027 1028 foreach ($tagnames as $index => $slottagnames) { 1029 $tagslotnumber = $index + 1; 1030 $tagslotid = $structure->get_slot_id_for_slot($tagslotnumber); 1031 $tags = core_tag_tag::create_if_missing($collid, $slottagnames); 1032 $records = array_map(function($tag) use ($tagslotid) { 1033 return (object) [ 1034 'slotid' => $tagslotid, 1035 'tagid' => $tag->id, 1036 'tagname' => $tag->name 1037 ]; 1038 }, array_values($tags)); 1039 $slottagrecords = array_merge($slottagrecords, $records); 1040 } 1041 1042 $DB->insert_records('quiz_slot_tags', $slottagrecords); 1043 1044 $actualslottags = $structure->get_slot_tags_for_slot_id($slotid); 1045 $actual = array_map(function($slottag) { 1046 return $slottag->tagname; 1047 }, $actualslottags); 1048 1049 sort($expected); 1050 sort($actual); 1051 1052 $this->assertEquals($expected, $actual); 1053 } 1054 1055 /** 1056 * Test for can_add_random_questions. 1057 */ 1058 public function test_can_add_random_questions() { 1059 $this->resetAfterTest(); 1060 1061 $quiz = $this->create_test_quiz([]); 1062 $course = $quiz->get_course(); 1063 1064 $generator = $this->getDataGenerator(); 1065 $teacher = $generator->create_and_enrol($course, 'editingteacher'); 1066 $noneditingteacher = $generator->create_and_enrol($course, 'teacher'); 1067 1068 $this->setUser($teacher); 1069 $structure = \mod_quiz\structure::create_for_quiz($quiz); 1070 $this->assertTrue($structure->can_add_random_questions()); 1071 1072 $this->setUser($noneditingteacher); 1073 $structure = \mod_quiz\structure::create_for_quiz($quiz); 1074 $this->assertFalse($structure->can_add_random_questions()); 1075 } 1076 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body