MDL-77564 Quiz display options: Hide or show the grade information
[moodle.git] / mod / quiz / tests / locallib_test.php
blob83b368da1418cf08de1d96969a8900e13d88bf95
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.
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/>.
17 /**
18 * Unit tests for (some of) mod/quiz/locallib.php.
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
25 namespace mod_quiz;
27 use mod_quiz\output\renderer;
28 use mod_quiz\question\display_options;
30 defined('MOODLE_INTERNAL') || die();
32 global $CFG;
33 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
36 /**
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;
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));
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],
77 /**
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;
93 } else {
94 $attempt->timefinish = time() + $relativetimefinish;
97 $quiz = new \stdClass();
98 if ($relativetimeclose === null) {
99 $quiz->timeclose = 0;
100 } else {
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 &lt; 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 &lt; 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);
138 * Test quiz_view
139 * @return void
141 public function test_quiz_view() {
142 global $CFG;
144 $CFG->enablecompletion = 1;
145 $this->resetAfterTest();
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);
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() {
182 global $CFG, $DB;
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() {
206 global $CFG, $DB;
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
226 * instance.
228 public function test_quiz_is_overriden_calendar_event_different_quiz_instance() {
229 global $CFG, $DB;
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
247 $record = (object) [
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() {
261 global $CFG, $DB;
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
278 $record = (object) [
279 'quiz' => $quiz->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() {
292 global $CFG, $DB;
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;
304 $userid = $user->id;
306 $event = new \calendar_event((object) [
307 'modulename' => 'quiz',
308 'instance' => $quiz->id,
309 'groupid' => $groupid
312 $record = (object) [
313 'quiz' => $quiz->id,
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() {
326 global $DB;
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;
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');
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]);
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();
381 $comparearray = [];
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();
400 $comparearray = [];
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);
427 $comparearray = [];
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);
446 $comparearray = [];
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 = []) {
480 global $SITE;
482 $tagobjects = [];
484 // Get all the tags that need to be created.
485 $alltags = [];
486 foreach ($questiontags as $questiontag) {
487 $alltags = array_merge($alltags, $questiontag);
489 $alltags = array_merge($alltags, $unusedtags);
490 $alltags = array_unique($alltags);
492 // Create tags.
493 foreach ($alltags as $tagname) {
494 $tagrecord = [
495 'isstandard' => 1,
496 'flag' => 0,
497 'rawname' => $tagname,
498 'description' => $tagname . ' desc'
500 $tagobjects[$tagname] = $this->getDataGenerator()->create_tag($tagrecord);
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]);
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]);
520 $tagids = [];
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() {
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');
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));
556 // Editing teacher.
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]);
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]);
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.
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));
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));
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]);
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));
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));
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() {
638 global $CFG, $DB;
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');
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);
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();
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();