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 /** 18 * Unit tests for (some of) mod/quiz/locallib.php. 19 * 20 * @package mod_quiz 21 * @category test 22 * @copyright 2008 Tim Hunt 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 namespace mod_quiz; 26 27 use mod_quiz\output\renderer; 28 use mod_quiz\question\display_options; 29 30 defined('MOODLE_INTERNAL') || die(); 31 32 global $CFG; 33 require_once($CFG->dirroot . '/mod/quiz/locallib.php'); 34 35 36 /** 37 * Unit tests for (some of) mod/quiz/locallib.php. 38 * 39 * @copyright 2008 Tim Hunt 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 */ 42 class locallib_test extends \advanced_testcase { 43 44 public function test_quiz_rescale_grade() { 45 $quiz = new \stdClass(); 46 $quiz->decimalpoints = 2; 47 $quiz->questiondecimalpoints = 3; 48 $quiz->grade = 10; 49 $quiz->sumgrades = 10; 50 $this->assertEquals(quiz_rescale_grade(0.12345678, $quiz, false), 0.12345678); 51 $this->assertEquals(quiz_rescale_grade(0.12345678, $quiz, true), format_float(0.12, 2)); 52 $this->assertEquals(quiz_rescale_grade(0.12345678, $quiz, 'question'), 53 format_float(0.123, 3)); 54 $quiz->sumgrades = 5; 55 $this->assertEquals(quiz_rescale_grade(0.12345678, $quiz, false), 0.24691356); 56 $this->assertEquals(quiz_rescale_grade(0.12345678, $quiz, true), format_float(0.25, 2)); 57 $this->assertEquals(quiz_rescale_grade(0.12345678, $quiz, 'question'), 58 format_float(0.247, 3)); 59 } 60 61 public function quiz_attempt_state_data_provider() { 62 return [ 63 [quiz_attempt::IN_PROGRESS, null, null, display_options::DURING], 64 [quiz_attempt::FINISHED, -90, null, display_options::IMMEDIATELY_AFTER], 65 [quiz_attempt::FINISHED, -7200, null, display_options::LATER_WHILE_OPEN], 66 [quiz_attempt::FINISHED, -7200, 3600, display_options::LATER_WHILE_OPEN], 67 [quiz_attempt::FINISHED, -30, 30, display_options::IMMEDIATELY_AFTER], 68 [quiz_attempt::FINISHED, -90, -30, display_options::AFTER_CLOSE], 69 [quiz_attempt::FINISHED, -7200, -3600, display_options::AFTER_CLOSE], 70 [quiz_attempt::FINISHED, -90, -3600, display_options::AFTER_CLOSE], 71 [quiz_attempt::ABANDONED, -10000000, null, display_options::LATER_WHILE_OPEN], 72 [quiz_attempt::ABANDONED, -7200, 3600, display_options::LATER_WHILE_OPEN], 73 [quiz_attempt::ABANDONED, -7200, -3600, display_options::AFTER_CLOSE], 74 ]; 75 } 76 77 /** 78 * @dataProvider quiz_attempt_state_data_provider 79 * 80 * @param string $attemptstate as in the quiz_attempts.state DB column. 81 * @param int|null $relativetimefinish time relative to now when the attempt finished, or null for 0. 82 * @param int|null $relativetimeclose time relative to now when the quiz closes, or null for 0. 83 * @param int $expectedstate expected result. One of the display_options constants. 84 * @covers ::quiz_attempt_state 85 */ 86 public function test_quiz_attempt_state(string $attemptstate, 87 ?int $relativetimefinish, ?int $relativetimeclose, int $expectedstate) { 88 89 $attempt = new \stdClass(); 90 $attempt->state = $attemptstate; 91 if ($relativetimefinish === null) { 92 $attempt->timefinish = 0; 93 } else { 94 $attempt->timefinish = time() + $relativetimefinish; 95 } 96 97 $quiz = new \stdClass(); 98 if ($relativetimeclose === null) { 99 $quiz->timeclose = 0; 100 } else { 101 $quiz->timeclose = time() + $relativetimeclose; 102 } 103 104 $this->assertEquals($expectedstate, quiz_attempt_state($quiz, $attempt)); 105 } 106 107 /** 108 * @covers ::quiz_question_tostring 109 */ 110 public function test_quiz_question_tostring() { 111 $question = new \stdClass(); 112 $question->qtype = 'multichoice'; 113 $question->name = 'The question name'; 114 $question->questiontext = '<p>What sort of <b>inequality</b> is x < y<img alt="?" src="..."></p>'; 115 $question->questiontextformat = FORMAT_HTML; 116 117 $summary = quiz_question_tostring($question); 118 $this->assertEquals('<span class="questionname">The question name</span> ' . 119 '<span class="questiontext">What sort of INEQUALITY is x < y[?]' . "\n" . '</span>', $summary); 120 } 121 122 /** 123 * @covers ::quiz_question_tostring 124 */ 125 public function test_quiz_question_tostring_does_not_filter() { 126 $question = new \stdClass(); 127 $question->qtype = 'multichoice'; 128 $question->name = 'The question name'; 129 $question->questiontext = '<p>No emoticons here :-)</p>'; 130 $question->questiontextformat = FORMAT_HTML; 131 132 $summary = quiz_question_tostring($question); 133 $this->assertEquals('<span class="questionname">The question name</span> ' . 134 '<span class="questiontext">No emoticons here :-)' . "\n</span>", $summary); 135 } 136 137 /** 138 * Test quiz_view 139 * @return void 140 */ 141 public function test_quiz_view() { 142 global $CFG; 143 144 $CFG->enablecompletion = 1; 145 $this->resetAfterTest(); 146 147 $this->setAdminUser(); 148 // Setup test data. 149 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]); 150 $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id], 151 ['completion' => 2, 'completionview' => 1]); 152 $context = \context_module::instance($quiz->cmid); 153 $cm = get_coursemodule_from_instance('quiz', $quiz->id); 154 155 // Trigger and capture the event. 156 $sink = $this->redirectEvents(); 157 158 quiz_view($quiz, $course, $cm, $context); 159 160 $events = $sink->get_events(); 161 // 2 additional events thanks to completion. 162 $this->assertCount(3, $events); 163 $event = array_shift($events); 164 165 // Checking that the event contains the expected values. 166 $this->assertInstanceOf('\mod_quiz\event\course_module_viewed', $event); 167 $this->assertEquals($context, $event->get_context()); 168 $moodleurl = new \moodle_url('/mod/quiz/view.php', ['id' => $cm->id]); 169 $this->assertEquals($moodleurl, $event->get_url()); 170 $this->assertEventContextNotUsed($event); 171 $this->assertNotEmpty($event->get_name()); 172 // Check completion status. 173 $completion = new \completion_info($course); 174 $completiondata = $completion->get_data($cm); 175 $this->assertEquals(1, $completiondata->completionstate); 176 } 177 178 /** 179 * Return false when there are not overrides for this quiz instance. 180 */ 181 public function test_quiz_is_overriden_calendar_event_no_override() { 182 global $CFG, $DB; 183 184 $this->resetAfterTest(); 185 $this->setAdminUser(); 186 187 $generator = $this->getDataGenerator(); 188 $user = $generator->create_user(); 189 $course = $generator->create_course(); 190 $quizgenerator = $generator->get_plugin_generator('mod_quiz'); 191 $quiz = $quizgenerator->create_instance(['course' => $course->id]); 192 193 $event = new \calendar_event((object)[ 194 'modulename' => 'quiz', 195 'instance' => $quiz->id, 196 'userid' => $user->id 197 ]); 198 199 $this->assertFalse(quiz_is_overriden_calendar_event($event)); 200 } 201 202 /** 203 * Return false if the given event isn't an quiz module event. 204 */ 205 public function test_quiz_is_overriden_calendar_event_no_module_event() { 206 global $CFG, $DB; 207 208 $this->resetAfterTest(); 209 $this->setAdminUser(); 210 211 $generator = $this->getDataGenerator(); 212 $user = $generator->create_user(); 213 $course = $generator->create_course(); 214 $quizgenerator = $generator->get_plugin_generator('mod_quiz'); 215 $quiz = $quizgenerator->create_instance(['course' => $course->id]); 216 217 $event = new \calendar_event((object)[ 218 'userid' => $user->id 219 ]); 220 221 $this->assertFalse(quiz_is_overriden_calendar_event($event)); 222 } 223 224 /** 225 * Return false if there is overrides for this use but they belong to another quiz 226 * instance. 227 */ 228 public function test_quiz_is_overriden_calendar_event_different_quiz_instance() { 229 global $CFG, $DB; 230 231 $this->resetAfterTest(); 232 $this->setAdminUser(); 233 234 $generator = $this->getDataGenerator(); 235 $user = $generator->create_user(); 236 $course = $generator->create_course(); 237 $quizgenerator = $generator->get_plugin_generator('mod_quiz'); 238 $quiz = $quizgenerator->create_instance(['course' => $course->id]); 239 $quiz2 = $quizgenerator->create_instance(['course' => $course->id]); 240 241 $event = new \calendar_event((object) [ 242 'modulename' => 'quiz', 243 'instance' => $quiz->id, 244 'userid' => $user->id 245 ]); 246 247 $record = (object) [ 248 'quiz' => $quiz2->id, 249 'userid' => $user->id 250 ]; 251 252 $DB->insert_record('quiz_overrides', $record); 253 254 $this->assertFalse(quiz_is_overriden_calendar_event($event)); 255 } 256 257 /** 258 * Return true if there is a user override for this event and quiz instance. 259 */ 260 public function test_quiz_is_overriden_calendar_event_user_override() { 261 global $CFG, $DB; 262 263 $this->resetAfterTest(); 264 $this->setAdminUser(); 265 266 $generator = $this->getDataGenerator(); 267 $user = $generator->create_user(); 268 $course = $generator->create_course(); 269 $quizgenerator = $generator->get_plugin_generator('mod_quiz'); 270 $quiz = $quizgenerator->create_instance(['course' => $course->id]); 271 272 $event = new \calendar_event((object) [ 273 'modulename' => 'quiz', 274 'instance' => $quiz->id, 275 'userid' => $user->id 276 ]); 277 278 $record = (object) [ 279 'quiz' => $quiz->id, 280 'userid' => $user->id 281 ]; 282 283 $DB->insert_record('quiz_overrides', $record); 284 285 $this->assertTrue(quiz_is_overriden_calendar_event($event)); 286 } 287 288 /** 289 * Return true if there is a group override for the event and quiz instance. 290 */ 291 public function test_quiz_is_overriden_calendar_event_group_override() { 292 global $CFG, $DB; 293 294 $this->resetAfterTest(); 295 $this->setAdminUser(); 296 297 $generator = $this->getDataGenerator(); 298 $user = $generator->create_user(); 299 $course = $generator->create_course(); 300 $quizgenerator = $generator->get_plugin_generator('mod_quiz'); 301 $quiz = $quizgenerator->create_instance(['course' => $course->id]); 302 $group = $this->getDataGenerator()->create_group(['courseid' => $quiz->course]); 303 $groupid = $group->id; 304 $userid = $user->id; 305 306 $event = new \calendar_event((object) [ 307 'modulename' => 'quiz', 308 'instance' => $quiz->id, 309 'groupid' => $groupid 310 ]); 311 312 $record = (object) [ 313 'quiz' => $quiz->id, 314 'groupid' => $groupid 315 ]; 316 317 $DB->insert_record('quiz_overrides', $record); 318 319 $this->assertTrue(quiz_is_overriden_calendar_event($event)); 320 } 321 322 /** 323 * Test test_quiz_get_user_timeclose(). 324 */ 325 public function test_quiz_get_user_timeclose() { 326 global $DB; 327 328 $this->resetAfterTest(); 329 $this->setAdminUser(); 330 331 $basetimestamp = time(); // The timestamp we will base the enddates on. 332 333 // Create generator, course and quizzes. 334 $student1 = $this->getDataGenerator()->create_user(); 335 $student2 = $this->getDataGenerator()->create_user(); 336 $student3 = $this->getDataGenerator()->create_user(); 337 $teacher = $this->getDataGenerator()->create_user(); 338 $course = $this->getDataGenerator()->create_course(); 339 $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); 340 341 // Both quizzes close in two hours. 342 $quiz1 = $quizgenerator->create_instance(['course' => $course->id, 'timeclose' => $basetimestamp + 7200]); 343 $quiz2 = $quizgenerator->create_instance(['course' => $course->id, 'timeclose' => $basetimestamp + 7200]); 344 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 345 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 346 347 $student1id = $student1->id; 348 $student2id = $student2->id; 349 $student3id = $student3->id; 350 $teacherid = $teacher->id; 351 352 // Users enrolments. 353 $studentrole = $DB->get_record('role', ['shortname' => 'student']); 354 $teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']); 355 $this->getDataGenerator()->enrol_user($student1id, $course->id, $studentrole->id, 'manual'); 356 $this->getDataGenerator()->enrol_user($student2id, $course->id, $studentrole->id, 'manual'); 357 $this->getDataGenerator()->enrol_user($student3id, $course->id, $studentrole->id, 'manual'); 358 $this->getDataGenerator()->enrol_user($teacherid, $course->id, $teacherrole->id, 'manual'); 359 360 // Create groups. 361 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 362 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 363 $group1id = $group1->id; 364 $group2id = $group2->id; 365 $this->getDataGenerator()->create_group_member(['userid' => $student1id, 'groupid' => $group1id]); 366 $this->getDataGenerator()->create_group_member(['userid' => $student2id, 'groupid' => $group2id]); 367 368 // Group 1 gets an group override for quiz 1 to close in three hours. 369 $record1 = (object) [ 370 'quiz' => $quiz1->id, 371 'groupid' => $group1id, 372 'timeclose' => $basetimestamp + 10800 // In three hours. 373 ]; 374 $DB->insert_record('quiz_overrides', $record1); 375 376 // Let's test quiz 1 closes in three hours for user student 1 since member of group 1. 377 // Quiz 2 closes in two hours. 378 $this->setUser($student1id); 379 $params = new \stdClass(); 380 381 $comparearray = []; 382 $object = new \stdClass(); 383 $object->id = $quiz1->id; 384 $object->usertimeclose = $basetimestamp + 10800; // The overriden timeclose for quiz 1. 385 386 $comparearray[$quiz1->id] = $object; 387 388 $object = new \stdClass(); 389 $object->id = $quiz2->id; 390 $object->usertimeclose = $basetimestamp + 7200; // The unchanged timeclose for quiz 2. 391 392 $comparearray[$quiz2->id] = $object; 393 394 $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id)); 395 396 // Let's test quiz 1 closes in two hours (the original value) for user student 3 since member of no group. 397 $this->setUser($student3id); 398 $params = new \stdClass(); 399 400 $comparearray = []; 401 $object = new \stdClass(); 402 $object->id = $quiz1->id; 403 $object->usertimeclose = $basetimestamp + 7200; // The original timeclose for quiz 1. 404 405 $comparearray[$quiz1->id] = $object; 406 407 $object = new \stdClass(); 408 $object->id = $quiz2->id; 409 $object->usertimeclose = $basetimestamp + 7200; // The original timeclose for quiz 2. 410 411 $comparearray[$quiz2->id] = $object; 412 413 $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id)); 414 415 // User 2 gets an user override for quiz 1 to close in four hours. 416 $record2 = (object) [ 417 'quiz' => $quiz1->id, 418 'userid' => $student2id, 419 'timeclose' => $basetimestamp + 14400 // In four hours. 420 ]; 421 $DB->insert_record('quiz_overrides', $record2); 422 423 // Let's test quiz 1 closes in four hours for user student 2 since personally overriden. 424 // Quiz 2 closes in two hours. 425 $this->setUser($student2id); 426 427 $comparearray = []; 428 $object = new \stdClass(); 429 $object->id = $quiz1->id; 430 $object->usertimeclose = $basetimestamp + 14400; // The overriden timeclose for quiz 1. 431 432 $comparearray[$quiz1->id] = $object; 433 434 $object = new \stdClass(); 435 $object->id = $quiz2->id; 436 $object->usertimeclose = $basetimestamp + 7200; // The unchanged timeclose for quiz 2. 437 438 $comparearray[$quiz2->id] = $object; 439 440 $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id)); 441 442 // Let's test a teacher sees the original times. 443 // Quiz 1 and quiz 2 close in two hours. 444 $this->setUser($teacherid); 445 446 $comparearray = []; 447 $object = new \stdClass(); 448 $object->id = $quiz1->id; 449 $object->usertimeclose = $basetimestamp + 7200; // The unchanged timeclose for quiz 1. 450 451 $comparearray[$quiz1->id] = $object; 452 453 $object = new \stdClass(); 454 $object->id = $quiz2->id; 455 $object->usertimeclose = $basetimestamp + 7200; // The unchanged timeclose for quiz 2. 456 457 $comparearray[$quiz2->id] = $object; 458 459 $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id)); 460 } 461 462 /** 463 * This function creates a quiz with some standard (non-random) and some random questions. 464 * The standard questions are created first and then random questions follow them. 465 * So in a quiz with 3 standard question and 2 random question, the first random question is at slot 4. 466 * 467 * @param int $qnum Number of standard questions that should be created in the quiz. 468 * @param int $randomqnum Number of random questions that should be created in the quiz. 469 * @param array $questiontags Tags to be used for random questions. 470 * This is an array in the following format: 471 * [ 472 * 0 => ['foo', 'bar'], 473 * 1 => ['baz', 'qux'] 474 * ] 475 * @param string[] $unusedtags Some additional tags to be created. 476 * @return array An array of 2 elements: $quiz and $tagobjects. 477 * $tagobjects is an associative array of all created tag objects with its key being tag names. 478 */ 479 private function setup_quiz_and_tags($qnum, $randomqnum, $questiontags = [], $unusedtags = []) { 480 global $SITE; 481 482 $tagobjects = []; 483 484 // Get all the tags that need to be created. 485 $alltags = []; 486 foreach ($questiontags as $questiontag) { 487 $alltags = array_merge($alltags, $questiontag); 488 } 489 $alltags = array_merge($alltags, $unusedtags); 490 $alltags = array_unique($alltags); 491 492 // Create tags. 493 foreach ($alltags as $tagname) { 494 $tagrecord = [ 495 'isstandard' => 1, 496 'flag' => 0, 497 'rawname' => $tagname, 498 'description' => $tagname . ' desc' 499 ]; 500 $tagobjects[$tagname] = $this->getDataGenerator()->create_tag($tagrecord); 501 } 502 503 // Create a quiz. 504 $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); 505 $quiz = $quizgenerator->create_instance(['course' => $SITE->id, 'questionsperpage' => 3, 'grade' => 100.0]); 506 507 // Create a question category in the system context. 508 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 509 $cat = $questiongenerator->create_question_category(); 510 511 // Setup standard questions. 512 for ($i = 0; $i < $qnum; $i++) { 513 $question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]); 514 quiz_add_quiz_question($question->id, $quiz); 515 } 516 // Setup random questions. 517 for ($i = 0; $i < $randomqnum; $i++) { 518 // Just create a standard question first, so there would be enough questions to pick a random question from. 519 $question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]); 520 $tagids = []; 521 if (!empty($questiontags[$i])) { 522 foreach ($questiontags[$i] as $tagname) { 523 $tagids[] = $tagobjects[$tagname]->id; 524 } 525 } 526 quiz_add_random_questions($quiz, 0, $cat->id, 1, false, $tagids); 527 } 528 529 return [$quiz, $tagobjects]; 530 } 531 532 public function test_quiz_override_summary() { 533 global $DB, $PAGE; 534 $this->resetAfterTest(); 535 $generator = $this->getDataGenerator(); 536 /** @var mod_quiz_generator $quizgenerator */ 537 $quizgenerator = $generator->get_plugin_generator('mod_quiz'); 538 /** @var renderer $renderer */ 539 $renderer = $PAGE->get_renderer('mod_quiz'); 540 541 // Course with quiz and a group - plus some others, to verify they don't get counted. 542 $course = $generator->create_course(); 543 $quiz = $quizgenerator->create_instance(['course' => $course->id, 'groupmode' => SEPARATEGROUPS]); 544 $cm = get_coursemodule_from_id('quiz', $quiz->cmid, $course->id); 545 $group = $generator->create_group(['courseid' => $course->id]); 546 $othergroup = $generator->create_group(['courseid' => $course->id]); 547 $otherquiz = $quizgenerator->create_instance(['course' => $course->id]); 548 549 // Initial test (as admin) with no data. 550 $this->setAdminUser(); 551 $this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'allgroups'], 552 quiz_override_summary($quiz, $cm)); 553 $this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'onegroup'], 554 quiz_override_summary($quiz, $cm, $group->id)); 555 556 // Editing teacher. 557 $teacher = $generator->create_user(); 558 $generator->enrol_user($teacher->id, $course->id, 'editingteacher'); 559 560 // Non-editing teacher. 561 $tutor = $generator->create_user(); 562 $generator->enrol_user($tutor->id, $course->id, 'teacher'); 563 $generator->create_group_member(['userid' => $tutor->id, 'groupid' => $group->id]); 564 565 // Three students. 566 $student1 = $generator->create_user(); 567 $generator->enrol_user($student1->id, $course->id, 'student'); 568 $generator->create_group_member(['userid' => $student1->id, 'groupid' => $group->id]); 569 570 $student2 = $generator->create_user(); 571 $generator->enrol_user($student2->id, $course->id, 'student'); 572 $generator->create_group_member(['userid' => $student2->id, 'groupid' => $othergroup->id]); 573 574 $student3 = $generator->create_user(); 575 $generator->enrol_user($student3->id, $course->id, 'student'); 576 577 // Initial test now users exist, but before overrides. 578 // Test as teacher. 579 $this->setUser($teacher); 580 $this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'allgroups'], 581 quiz_override_summary($quiz, $cm)); 582 $this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'onegroup'], 583 quiz_override_summary($quiz, $cm, $group->id)); 584 585 // Test as tutor. 586 $this->setUser($tutor); 587 $this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'somegroups'], 588 quiz_override_summary($quiz, $cm)); 589 $this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'onegroup'], 590 quiz_override_summary($quiz, $cm, $group->id)); 591 $this->assertEquals('', $renderer->quiz_override_summary_links($quiz, $cm)); 592 593 // Quiz setting overrides for students 1 and 3. 594 $quizgenerator->create_override(['quiz' => $quiz->id, 'userid' => $student1->id, 'attempts' => 2]); 595 $quizgenerator->create_override(['quiz' => $quiz->id, 'userid' => $student3->id, 'attempts' => 2]); 596 $quizgenerator->create_override(['quiz' => $quiz->id, 'groupid' => $group->id, 'attempts' => 3]); 597 $quizgenerator->create_override(['quiz' => $quiz->id, 'groupid' => $othergroup->id, 'attempts' => 3]); 598 $quizgenerator->create_override(['quiz' => $otherquiz->id, 'userid' => $student2->id, 'attempts' => 2]); 599 600 // Test as teacher. 601 $this->setUser($teacher); 602 $this->assertEquals(['group' => 2, 'user' => 2, 'mode' => 'allgroups'], 603 quiz_override_summary($quiz, $cm)); 604 $this->assertEquals('Settings overrides exist (Groups: 2, Users: 2)', 605 // Links checked by Behat, so strip them for these tests. 606 html_to_text($renderer->quiz_override_summary_links($quiz, $cm), 0, false)); 607 $this->assertEquals(['group' => 1, 'user' => 1, 'mode' => 'onegroup'], 608 quiz_override_summary($quiz, $cm, $group->id)); 609 $this->assertEquals('Settings overrides exist (Groups: 1, Users: 1) for this group', 610 html_to_text($renderer->quiz_override_summary_links($quiz, $cm, $group->id), 0, false)); 611 612 // Test as tutor. 613 $this->setUser($tutor); 614 $this->assertEquals(['group' => 1, 'user' => 1, 'mode' => 'somegroups'], 615 quiz_override_summary($quiz, $cm)); 616 $this->assertEquals('Settings overrides exist (Groups: 1, Users: 1) for your groups', 617 html_to_text($renderer->quiz_override_summary_links($quiz, $cm), 0, false)); 618 $this->assertEquals(['group' => 1, 'user' => 1, 'mode' => 'onegroup'], 619 quiz_override_summary($quiz, $cm, $group->id)); 620 $this->assertEquals('Settings overrides exist (Groups: 1, Users: 1) for this group', 621 html_to_text($renderer->quiz_override_summary_links($quiz, $cm, $group->id), 0, false)); 622 623 // Now set the quiz to be group mode: no groups, and re-test as tutor. 624 // In this case, the tutor should see all groups. 625 $DB->set_field('course_modules', 'groupmode', NOGROUPS, ['id' => $cm->id]); 626 $cm = get_coursemodule_from_id('quiz', $quiz->cmid, $course->id); 627 628 $this->assertEquals(['group' => 2, 'user' => 2, 'mode' => 'allgroups'], 629 quiz_override_summary($quiz, $cm)); 630 $this->assertEquals('Settings overrides exist (Groups: 2, Users: 2)', 631 html_to_text($renderer->quiz_override_summary_links($quiz, $cm), 0, false)); 632 } 633 634 /** 635 * Test quiz_send_confirmation function. 636 */ 637 public function test_quiz_send_confirmation() { 638 global $CFG, $DB; 639 640 $this->resetAfterTest(); 641 $this->setAdminUser(); 642 $this->preventResetByRollback(); 643 644 $course = $this->getDataGenerator()->create_course(); 645 $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); 646 $quiz = $quizgenerator->create_instance(['course' => $course->id]); 647 $cm = get_coursemodule_from_instance('quiz', $quiz->id); 648 649 $recipient = $this->getDataGenerator()->create_user(['email' => 'student@example.com']); 650 651 // Allow recipent to receive email confirm submission. 652 $studentrole = $DB->get_record('role', ['shortname' => 'student']); 653 assign_capability('mod/quiz:emailconfirmsubmission', CAP_ALLOW, $studentrole->id, 654 \context_course::instance($course->id), true); 655 $this->getDataGenerator()->enrol_user($recipient->id, $course->id, $studentrole->id, 'manual'); 656 657 $timenow = time(); 658 $data = new \stdClass(); 659 // Course info. 660 $data->courseid = $course->id; 661 $data->coursename = $course->fullname; 662 // Quiz info. 663 $data->quizname = $quiz->name; 664 $data->quizurl = $CFG->wwwroot . '/mod/quiz/view.php?id=' . $cm->id; 665 $data->quizid = $quiz->id; 666 $data->quizcmid = $quiz->cmid; 667 $data->attemptid = 1; 668 $data->submissiontime = userdate($timenow); 669 670 $sink = $this->redirectEmails(); 671 quiz_send_confirmation($recipient, $data, true); 672 $messages = $sink->get_messages(); 673 $message = reset($messages); 674 $this->assertStringContainsString("Thank you for submitting your answers" , 675 quoted_printable_decode($message->body)); 676 $sink->close(); 677 678 $sink = $this->redirectEmails(); 679 quiz_send_confirmation($recipient, $data, false); 680 $messages = $sink->get_messages(); 681 $message = reset($messages); 682 $this->assertStringContainsString("Your answers were submitted automatically" , 683 quoted_printable_decode($message->body)); 684 $sink->close(); 685 } 686 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body