Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | Ultima modificación | Ver Log |

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