2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Unit tests for (some of) mod/quiz/locallib.php.
22 * @copyright 2008 Tim Hunt
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 use mod_quiz\output\renderer
;
28 use mod_quiz\question\display_options
;
30 defined('MOODLE_INTERNAL') ||
die();
33 require_once($CFG->dirroot
. '/mod/quiz/locallib.php');
37 * Unit tests for (some of) mod/quiz/locallib.php.
39 * @copyright 2008 Tim Hunt
40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42 class locallib_test
extends \advanced_testcase
{
44 public function test_quiz_rescale_grade() {
45 $quiz = new \
stdClass();
46 $quiz->decimalpoints
= 2;
47 $quiz->questiondecimalpoints
= 3;
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));
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));
61 public function quiz_attempt_state_data_provider() {
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
],
78 * @dataProvider quiz_attempt_state_data_provider
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
86 public function test_quiz_attempt_state(string $attemptstate,
87 ?
int $relativetimefinish, ?
int $relativetimeclose, int $expectedstate) {
89 $attempt = new \
stdClass();
90 $attempt->state
= $attemptstate;
91 if ($relativetimefinish === null) {
92 $attempt->timefinish
= 0;
94 $attempt->timefinish
= time() +
$relativetimefinish;
97 $quiz = new \
stdClass();
98 if ($relativetimeclose === null) {
101 $quiz->timeclose
= time() +
$relativetimeclose;
104 $this->assertEquals($expectedstate, quiz_attempt_state($quiz, $attempt));
108 * @covers ::quiz_question_tostring
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
;
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);
123 * @covers ::quiz_question_tostring
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
;
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);
141 public function test_quiz_view() {
144 $CFG->enablecompletion
= 1;
145 $this->resetAfterTest();
147 $this->setAdminUser();
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
);
155 // Trigger and capture the event.
156 $sink = $this->redirectEvents();
158 quiz_view($quiz, $course, $cm, $context);
160 $events = $sink->get_events();
161 // 2 additional events thanks to completion.
162 $this->assertCount(3, $events);
163 $event = array_shift($events);
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
);
179 * Return false when there are not overrides for this quiz instance.
181 public function test_quiz_is_overriden_calendar_event_no_override() {
184 $this->resetAfterTest();
185 $this->setAdminUser();
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
]);
193 $event = new \
calendar_event((object)[
194 'modulename' => 'quiz',
195 'instance' => $quiz->id
,
196 'userid' => $user->id
199 $this->assertFalse(quiz_is_overriden_calendar_event($event));
203 * Return false if the given event isn't an quiz module event.
205 public function test_quiz_is_overriden_calendar_event_no_module_event() {
208 $this->resetAfterTest();
209 $this->setAdminUser();
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
]);
217 $event = new \
calendar_event((object)[
218 'userid' => $user->id
221 $this->assertFalse(quiz_is_overriden_calendar_event($event));
225 * Return false if there is overrides for this use but they belong to another quiz
228 public function test_quiz_is_overriden_calendar_event_different_quiz_instance() {
231 $this->resetAfterTest();
232 $this->setAdminUser();
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
]);
241 $event = new \
calendar_event((object) [
242 'modulename' => 'quiz',
243 'instance' => $quiz->id
,
244 'userid' => $user->id
248 'quiz' => $quiz2->id
,
249 'userid' => $user->id
252 $DB->insert_record('quiz_overrides', $record);
254 $this->assertFalse(quiz_is_overriden_calendar_event($event));
258 * Return true if there is a user override for this event and quiz instance.
260 public function test_quiz_is_overriden_calendar_event_user_override() {
263 $this->resetAfterTest();
264 $this->setAdminUser();
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
]);
272 $event = new \
calendar_event((object) [
273 'modulename' => 'quiz',
274 'instance' => $quiz->id
,
275 'userid' => $user->id
280 'userid' => $user->id
283 $DB->insert_record('quiz_overrides', $record);
285 $this->assertTrue(quiz_is_overriden_calendar_event($event));
289 * Return true if there is a group override for the event and quiz instance.
291 public function test_quiz_is_overriden_calendar_event_group_override() {
294 $this->resetAfterTest();
295 $this->setAdminUser();
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
;
306 $event = new \
calendar_event((object) [
307 'modulename' => 'quiz',
308 'instance' => $quiz->id
,
309 'groupid' => $groupid
314 'groupid' => $groupid
317 $DB->insert_record('quiz_overrides', $record);
319 $this->assertTrue(quiz_is_overriden_calendar_event($event));
323 * Test test_quiz_get_user_timeclose().
325 public function test_quiz_get_user_timeclose() {
328 $this->resetAfterTest();
329 $this->setAdminUser();
331 $basetimestamp = time(); // The timestamp we will base the enddates on.
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');
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
]);
347 $student1id = $student1->id
;
348 $student2id = $student2->id
;
349 $student3id = $student3->id
;
350 $teacherid = $teacher->id
;
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');
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]);
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.
374 $DB->insert_record('quiz_overrides', $record1);
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();
382 $object = new \
stdClass();
383 $object->id
= $quiz1->id
;
384 $object->usertimeclose
= $basetimestamp +
10800; // The overriden timeclose for quiz 1.
386 $comparearray[$quiz1->id
] = $object;
388 $object = new \
stdClass();
389 $object->id
= $quiz2->id
;
390 $object->usertimeclose
= $basetimestamp +
7200; // The unchanged timeclose for quiz 2.
392 $comparearray[$quiz2->id
] = $object;
394 $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id
));
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();
401 $object = new \
stdClass();
402 $object->id
= $quiz1->id
;
403 $object->usertimeclose
= $basetimestamp +
7200; // The original timeclose for quiz 1.
405 $comparearray[$quiz1->id
] = $object;
407 $object = new \
stdClass();
408 $object->id
= $quiz2->id
;
409 $object->usertimeclose
= $basetimestamp +
7200; // The original timeclose for quiz 2.
411 $comparearray[$quiz2->id
] = $object;
413 $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id
));
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.
421 $DB->insert_record('quiz_overrides', $record2);
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);
428 $object = new \
stdClass();
429 $object->id
= $quiz1->id
;
430 $object->usertimeclose
= $basetimestamp +
14400; // The overriden timeclose for quiz 1.
432 $comparearray[$quiz1->id
] = $object;
434 $object = new \
stdClass();
435 $object->id
= $quiz2->id
;
436 $object->usertimeclose
= $basetimestamp +
7200; // The unchanged timeclose for quiz 2.
438 $comparearray[$quiz2->id
] = $object;
440 $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id
));
442 // Let's test a teacher sees the original times.
443 // Quiz 1 and quiz 2 close in two hours.
444 $this->setUser($teacherid);
447 $object = new \
stdClass();
448 $object->id
= $quiz1->id
;
449 $object->usertimeclose
= $basetimestamp +
7200; // The unchanged timeclose for quiz 1.
451 $comparearray[$quiz1->id
] = $object;
453 $object = new \
stdClass();
454 $object->id
= $quiz2->id
;
455 $object->usertimeclose
= $basetimestamp +
7200; // The unchanged timeclose for quiz 2.
457 $comparearray[$quiz2->id
] = $object;
459 $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id
));
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.
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:
472 * 0 => ['foo', 'bar'],
473 * 1 => ['baz', 'qux']
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.
479 private function setup_quiz_and_tags($qnum, $randomqnum, $questiontags = [], $unusedtags = []) {
484 // Get all the tags that need to be created.
486 foreach ($questiontags as $questiontag) {
487 $alltags = array_merge($alltags, $questiontag);
489 $alltags = array_merge($alltags, $unusedtags);
490 $alltags = array_unique($alltags);
493 foreach ($alltags as $tagname) {
497 'rawname' => $tagname,
498 'description' => $tagname . ' desc'
500 $tagobjects[$tagname] = $this->getDataGenerator()->create_tag($tagrecord);
504 $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
505 $quiz = $quizgenerator->create_instance(['course' => $SITE->id
, 'questionsperpage' => 3, 'grade' => 100.0]);
507 // Create a question category in the system context.
508 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
509 $cat = $questiongenerator->create_question_category();
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);
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
]);
521 if (!empty($questiontags[$i])) {
522 foreach ($questiontags[$i] as $tagname) {
523 $tagids[] = $tagobjects[$tagname]->id
;
526 quiz_add_random_questions($quiz, 0, $cat->id
, 1, false, $tagids);
529 return [$quiz, $tagobjects];
532 public function test_quiz_override_summary() {
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');
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
]);
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
));
557 $teacher = $generator->create_user();
558 $generator->enrol_user($teacher->id
, $course->id
, 'editingteacher');
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
]);
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
]);
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
]);
574 $student3 = $generator->create_user();
575 $generator->enrol_user($student3->id
, $course->id
, 'student');
577 // Initial test now users exist, but before overrides.
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
));
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));
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]);
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));
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));
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
);
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));
635 * Test quiz_send_confirmation function.
637 public function test_quiz_send_confirmation() {
640 $this->resetAfterTest();
641 $this->setAdminUser();
642 $this->preventResetByRollback();
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
);
649 $recipient = $this->getDataGenerator()->create_user(['email' => 'student@example.com']);
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');
658 $data = new \
stdClass();
660 $data->courseid
= $course->id
;
661 $data->coursename
= $course->fullname
;
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);
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
));
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
));