Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | 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
namespace mod_quiz;
18
 
19
use core_question\local\bank\condition;
20
use core_question\local\bank\question_version_status;
21
use core_question_generator;
22
use mod_quiz_generator;
23
use question_engine;
24
 
25
defined('MOODLE_INTERNAL') || die();
26
 
27
global $CFG;
28
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
29
 
30
/**
31
 * Tests for the quiz_attempt class.
32
 *
33
 * @package   mod_quiz
34
 * @category  test
35
 * @copyright 2014 Tim Hunt
36
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37
 * @covers \mod_quiz\quiz_attempt
38
 */
39
class attempt_test extends \advanced_testcase {
40
 
41
    /**
42
     * Create quiz and attempt data with layout.
43
     *
44
     * @param string $layout layout to set. Like quiz attempt.layout. E.g. '1,2,0,3,4,0,'.
45
     * @param string $navmethod quiz navigation method (defaults to free)
46
     * @return quiz_attempt the new quiz_attempt object
47
     */
48
    protected function create_quiz_and_attempt_with_layout($layout, $navmethod = QUIZ_NAVMETHOD_FREE) {
49
        $this->resetAfterTest(true);
50
 
51
        // Make a user to do the quiz.
52
        $user = $this->getDataGenerator()->create_user();
53
        $course = $this->getDataGenerator()->create_course();
54
        // Make a quiz.
55
        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
56
        $quiz = $quizgenerator->create_instance(['course' => $course->id,
57
            'grade' => 100.0, 'sumgrades' => 2, 'navmethod' => $navmethod]);
58
 
59
        $quizobj = quiz_settings::create($quiz->id, $user->id);
60
 
61
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
62
        $cat = $questiongenerator->create_question_category();
63
 
64
        $page = 1;
65
        foreach (explode(',', $layout) as $slot) {
66
            if ($slot == 0) {
67
                $page += 1;
68
                continue;
69
            }
70
 
71
            $question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
72
            quiz_add_quiz_question($question->id, $quiz, $page);
73
        }
74
 
75
        $attempt = quiz_prepare_and_start_new_attempt($quizobj, 1, null, false, [], [], $user->id);
76
        return quiz_attempt::create($attempt->id);
77
    }
78
 
79
    public function test_attempt_url() {
80
        $attempt = $this->create_quiz_and_attempt_with_layout('1,2,0,3,4,0,5,6,0');
81
 
82
        $attemptid = $attempt->get_attempt()->id;
83
        $cmid = $attempt->get_cmid();
84
        $url = '/mod/quiz/attempt.php';
85
        $params = ['attempt' => $attemptid, 'cmid' => $cmid, 'page' => 2];
86
 
87
        $this->assertEquals(new \moodle_url($url, $params), $attempt->attempt_url(null, 2));
88
 
89
        $params['page'] = 1;
90
        $this->assertEquals(new \moodle_url($url, $params), $attempt->attempt_url(3));
91
 
92
        $questionattempt = $attempt->get_question_attempt(4);
93
        $expecteanchor = $questionattempt->get_outer_question_div_unique_id();
94
        $this->assertEquals(new \moodle_url($url, $params, $expecteanchor), $attempt->attempt_url(4));
95
 
96
        $questionattempt = $attempt->get_question_attempt(3);
97
        $expecteanchor = '#' . $questionattempt->get_outer_question_div_unique_id();
98
        $this->assertEquals(new \moodle_url('#'), $attempt->attempt_url(null, 2, 2));
99
        $this->assertEquals(new \moodle_url($expecteanchor), $attempt->attempt_url(3, -1, 1));
100
 
101
        $questionattempt = $attempt->get_question_attempt(4);
102
        $expecteanchor = $questionattempt->get_outer_question_div_unique_id();
103
        $this->assertEquals(new \moodle_url(null, null, $expecteanchor, null), $attempt->attempt_url(4, -1, 1));
104
 
105
        // Summary page.
106
        $url = '/mod/quiz/summary.php';
107
        unset($params['page']);
108
        $this->assertEquals(new \moodle_url($url, $params), $attempt->summary_url());
109
 
110
        // Review page.
111
        $url = '/mod/quiz/review.php';
112
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url());
113
 
114
        $params['page'] = 1;
115
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(3, -1, false));
116
        $this->assertEquals(new \moodle_url($url, $params, $expecteanchor), $attempt->review_url(4, -1, false));
117
 
118
        unset($params['page']);
119
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(null, 2, true));
120
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(1, -1, true));
121
 
122
        $params['page'] = 2;
123
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(null, 2, false));
124
        unset($params['page']);
125
 
126
        $params['showall'] = 0;
127
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(null, 0, false));
128
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(1, -1, false));
129
 
130
        $params['page'] = 1;
131
        unset($params['showall']);
132
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(3, -1, false));
133
 
134
        $params['page'] = 2;
135
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(null, 2));
136
        $this->assertEquals(new \moodle_url('#'), $attempt->review_url(null, -1, null, 0));
137
 
138
        $questionattempt = $attempt->get_question_attempt(3);
139
        $expecteanchor = '#' . $questionattempt->get_outer_question_div_unique_id();
140
        $this->assertEquals(new \moodle_url($expecteanchor), $attempt->review_url(3, -1, null, 0));
141
 
142
        $questionattempt = $attempt->get_question_attempt(4);
143
        $expecteanchor = '#' . $questionattempt->get_outer_question_div_unique_id();
144
        $this->assertEquals(new \moodle_url($expecteanchor), $attempt->review_url(4, -1, null, 0));
145
        $this->assertEquals(new \moodle_url('#'), $attempt->review_url(null, 2, true, 0));
146
 
147
        $questionattempt = $attempt->get_question_attempt(1);
148
        $expecteanchor = '#' . $questionattempt->get_outer_question_div_unique_id();
149
        $this->assertEquals(new \moodle_url($expecteanchor), $attempt->review_url(1, -1, true, 0));
150
        $this->assertEquals(new \moodle_url($expecteanchor), $attempt->review_url(1, -1, false, 0));
151
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(null, 2, false, 0));
152
        $this->assertEquals(new \moodle_url('#'), $attempt->review_url(null, 0, false, 0));
153
 
154
        $params['page'] = 1;
155
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(3, -1, false, 0));
156
 
157
        // Setup another attempt.
158
        $attempt = $this->create_quiz_and_attempt_with_layout(
159
            '1,2,3,4,5,6,7,8,9,10,0,11,12,13,14,15,16,17,18,19,20,0,' .
160
            '21,22,23,24,25,26,27,28,29,30,0,31,32,33,34,35,36,37,38,39,40,0,' .
161
            '41,42,43,44,45,46,47,48,49,50,0,51,52,53,54,55,56,57,58,59,60,0');
162
 
163
        $attemptid = $attempt->get_attempt()->id;
164
        $cmid = $attempt->get_cmid();
165
        $params = ['attempt' => $attemptid, 'cmid' => $cmid];
166
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url());
167
 
168
        $params['page'] = 2;
169
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(null, 2));
170
 
171
        $params['page'] = 1;
172
        unset($params['showall']);
173
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(11, -1, false));
174
 
175
        $questionattempt = $attempt->get_question_attempt(12);
176
        $expecteanchor = $questionattempt->get_outer_question_div_unique_id();
177
        $this->assertEquals(new \moodle_url($url, $params, $expecteanchor), $attempt->review_url(12, -1, false));
178
 
179
        $params['showall'] = 1;
180
        unset($params['page']);
181
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(null, 2, true));
182
 
183
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(1, -1, true));
184
        $params['page'] = 2;
185
        unset($params['showall']);
186
        $this->assertEquals(new \moodle_url($url, $params),  $attempt->review_url(null, 2, false));
187
        unset($params['page']);
188
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(null, 0, false));
189
        $params['page'] = 1;
190
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(11, -1, false));
191
        $this->assertEquals(new \moodle_url($url, $params, $expecteanchor), $attempt->review_url(12, -1, false));
192
        $params['page'] = 2;
193
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(null, 2));
194
        $this->assertEquals(new \moodle_url('#'), $attempt->review_url(null, -1, null, 0));
195
 
196
        $questionattempt = $attempt->get_question_attempt(3);
197
        $expecteanchor = $questionattempt->get_outer_question_div_unique_id();
198
        $this->assertEquals(new \moodle_url(null, null, $expecteanchor), $attempt->review_url(3, -1, null, 0));
199
 
200
        $questionattempt = $attempt->get_question_attempt(4);
201
        $expecteanchor = $questionattempt->get_outer_question_div_unique_id();
202
        $this->assertEquals(new \moodle_url(null, null, $expecteanchor), $attempt->review_url(4, -1, null, 0));
203
 
204
        $questionattempt = $attempt->get_question_attempt(1);
205
        $expecteanchor = '#' . $questionattempt->get_outer_question_div_unique_id();
206
        $this->assertEquals(new \moodle_url($expecteanchor), $attempt->review_url(1, -1, true, 0));
207
        $this->assertEquals(new \moodle_url('#'), $attempt->review_url(null, 2, true, 0));
208
 
209
        $params['page'] = 2;
210
        $questionattempt = $attempt->get_question_attempt(1);
211
        $expecteanchor = '#' . $questionattempt->get_outer_question_div_unique_id();
212
        $this->assertEquals(new \moodle_url($expecteanchor), $attempt->review_url(1, -1, false, 0));
213
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(null, 2, false, 0));
214
        $this->assertEquals(new \moodle_url('#'), $attempt->review_url(null, 0, false, 0));
215
 
216
        $params['page'] = 1;
217
        $this->assertEquals(new \moodle_url($url, $params), $attempt->review_url(11, -1, false, 0));
218
    }
219
 
220
    /**
221
     * Tests attempt page titles when all questions are on a single page.
222
     */
223
    public function test_attempt_titles_single() {
224
        $attempt = $this->create_quiz_and_attempt_with_layout('1,2,0');
225
 
226
        // Attempt page.
227
        $this->assertEquals('Quiz 1', $attempt->attempt_page_title(0));
228
 
229
        // Summary page.
230
        $this->assertEquals('Quiz 1: Attempt summary', $attempt->summary_page_title());
231
 
232
        // Review page.
233
        $this->assertEquals('Quiz 1: Attempt review', $attempt->review_page_title(0));
234
    }
235
 
236
    /**
237
     * Tests attempt page titles when questions are on multiple pages, but are reviewed on a single page.
238
     */
239
    public function test_attempt_titles_multiple_single() {
240
        $attempt = $this->create_quiz_and_attempt_with_layout('1,2,0,3,4,0,5,6,0');
241
 
242
        // Attempt page.
243
        $this->assertEquals('Quiz 1 (page 1 of 3)', $attempt->attempt_page_title(0));
244
        $this->assertEquals('Quiz 1 (page 2 of 3)', $attempt->attempt_page_title(1));
245
        $this->assertEquals('Quiz 1 (page 3 of 3)', $attempt->attempt_page_title(2));
246
 
247
        // Summary page.
248
        $this->assertEquals('Quiz 1: Attempt summary', $attempt->summary_page_title());
249
 
250
        // Review page.
251
        $this->assertEquals('Quiz 1: Attempt review', $attempt->review_page_title(0, true));
252
    }
253
 
254
    /**
255
     * Tests attempt page titles when questions are on multiple pages, and they are reviewed on multiple pages as well.
256
     */
257
    public function test_attempt_titles_multiple_multiple() {
258
        $attempt = $this->create_quiz_and_attempt_with_layout(
259
                '1,2,3,4,5,6,7,8,9,10,0,11,12,13,14,15,16,17,18,19,20,0,' .
260
                '21,22,23,24,25,26,27,28,29,30,0,31,32,33,34,35,36,37,38,39,40,0,' .
261
                '41,42,43,44,45,46,47,48,49,50,0,51,52,53,54,55,56,57,58,59,60,0');
262
 
263
        // Attempt page.
264
        $this->assertEquals('Quiz 1 (page 1 of 6)', $attempt->attempt_page_title(0));
265
        $this->assertEquals('Quiz 1 (page 2 of 6)', $attempt->attempt_page_title(1));
266
        $this->assertEquals('Quiz 1 (page 6 of 6)', $attempt->attempt_page_title(5));
267
 
268
        // Summary page.
269
        $this->assertEquals('Quiz 1: Attempt summary', $attempt->summary_page_title());
270
 
271
        // Review page.
272
        $this->assertEquals('Quiz 1: Attempt review (page 1 of 6)', $attempt->review_page_title(0));
273
        $this->assertEquals('Quiz 1: Attempt review (page 2 of 6)', $attempt->review_page_title(1));
274
        $this->assertEquals('Quiz 1: Attempt review (page 6 of 6)', $attempt->review_page_title(5));
275
 
276
        // When all questions are shown.
277
        $this->assertEquals('Quiz 1: Attempt review', $attempt->review_page_title(0, true));
278
        $this->assertEquals('Quiz 1: Attempt review', $attempt->review_page_title(1, true));
279
    }
280
 
281
    public function test_is_participant() {
282
        global $USER;
283
        $this->resetAfterTest();
284
        $this->setAdminUser();
285
        $course = $this->getDataGenerator()->create_course();
286
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
287
        $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student', [], 'manual', 0, 0, ENROL_USER_SUSPENDED);
288
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
289
        $quizobj = quiz_settings::create($quiz->id);
290
 
291
        // Login as student.
292
        $this->setUser($student);
293
        // Convert to a lesson object.
294
        $this->assertEquals(true, $quizobj->is_participant($student->id),
295
            'Student is enrolled, active and can participate');
296
 
297
        // Login as student2.
298
        $this->setUser($student2);
299
        $this->assertEquals(false, $quizobj->is_participant($student2->id),
300
            'Student is enrolled, suspended and can NOT participate');
301
 
302
        // Login as an admin.
303
        $this->setAdminUser();
304
        $this->assertEquals(false, $quizobj->is_participant($USER->id),
305
            'Admin is not enrolled and can NOT participate');
306
 
307
        $this->getDataGenerator()->enrol_user(2, $course->id);
308
        $this->assertEquals(true, $quizobj->is_participant($USER->id),
309
            'Admin is enrolled and can participate');
310
 
311
        $this->getDataGenerator()->enrol_user(2, $course->id, [], 'manual', 0, 0, ENROL_USER_SUSPENDED);
312
        $this->assertEquals(true, $quizobj->is_participant($USER->id),
313
            'Admin is enrolled, suspended and can participate');
314
    }
315
 
316
    /**
317
     * Test quiz_prepare_and_start_new_attempt function
318
     */
319
    public function test_quiz_prepare_and_start_new_attempt() {
320
        global $USER;
321
        $this->resetAfterTest();
322
 
323
        // Create course.
324
        $course = $this->getDataGenerator()->create_course();
325
        // Create students.
326
        $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
327
        $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
328
        // Create quiz.
329
        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
330
        $quiz = $quizgenerator->create_instance(['course' => $course->id, 'grade' => 100.0, 'sumgrades' => 2, 'layout' => '1,0']);
331
        // Create question and add it to quiz.
332
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
333
        $cat = $questiongenerator->create_question_category();
334
        $question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
335
        quiz_add_quiz_question($question->id, $quiz, 1);
336
 
337
        $quizobj = quiz_settings::create($quiz->id);
338
 
339
        // Login as student1.
340
        $this->setUser($student1);
341
        // Create attempt for student1.
342
        $attempt = quiz_prepare_and_start_new_attempt($quizobj, 1, null, false, [], []);
343
        $this->assertEquals($student1->id, $attempt->userid);
344
        $this->assertEquals(0, $attempt->preview);
345
 
346
        // Login as student2.
347
        $this->setUser($student2);
348
        // Create attempt for student2.
349
        $attempt = quiz_prepare_and_start_new_attempt($quizobj, 1, null, false, [], []);
350
        $this->assertEquals($student2->id, $attempt->userid);
351
        $this->assertEquals(0, $attempt->preview);
352
 
353
        // Login as admin.
354
        $this->setAdminUser();
355
        // Create attempt for student1.
356
        $attempt = quiz_prepare_and_start_new_attempt($quizobj, 2, null, false, [], [], $student1->id);
357
        $this->assertEquals($student1->id, $attempt->userid);
358
        $this->assertEquals(0, $attempt->preview);
359
        $student1attempt = $attempt; // Save for extra verification below.
360
        // Create attempt for student2.
361
        $attempt = quiz_prepare_and_start_new_attempt($quizobj, 2, null, false, [], [], $student2->id);
362
        $this->assertEquals($student2->id, $attempt->userid);
363
        $this->assertEquals(0, $attempt->preview);
364
        // Create attempt for user id that the same with current $USER->id.
365
        $attempt = quiz_prepare_and_start_new_attempt($quizobj, 2, null, false, [], [], $USER->id);
366
        $this->assertEquals($USER->id, $attempt->userid);
367
        $this->assertEquals(1, $attempt->preview);
368
 
369
        // Check that the userid stored in the first step is the user the attempt is for,
370
        // not the user who triggered the creation.
371
        $quba = question_engine::load_questions_usage_by_activity($student1attempt->uniqueid);
372
        $step = $quba->get_question_attempt(1)->get_step(0);
373
        $this->assertEquals($student1->id, $step->get_user_id());
374
    }
375
 
376
    /**
377
     * Test quiz_prepare_and_start_new_attempt function
378
     */
379
    public function test_quiz_prepare_and_start_new_attempt_random_draft(): void {
380
        $this->resetAfterTest();
381
        $this->setAdminUser();
382
 
383
        // Create course.
384
        $course = $this->getDataGenerator()->create_course();
385
        // Create quiz.
386
        /** @var mod_quiz_generator $quizgenerator */
387
        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
388
        $quiz = $quizgenerator->create_instance(['course' => $course->id]);
389
 
390
        // Create question with 2 versions. V1 ready. V2 draft.
391
        /** @var core_question_generator $questiongenerator */
392
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
393
        $category = $questiongenerator->create_question_category();
394
        $question = $questiongenerator->create_question('shortanswer', null,
395
                ['questiontext' => 'V1', 'category' => $category->id]);
396
        $questiongenerator->update_question($question, null,
397
                ['questiontext' => 'V2', 'status' => question_version_status::QUESTION_STATUS_DRAFT]);
398
 
399
        // Add a random question form that category.
400
        $filtercondition = [
401
            'filter' => [
402
                'category' => [
403
                    'jointype' => condition::JOINTYPE_DEFAULT,
404
                    'values' => [$category->id],
405
                    'filteroptions' => ['includesubcategories' => false],
406
                ],
407
            ],
408
        ];
409
        $quizobj = quiz_settings::create($quiz->id);
410
        $quizobj->get_structure()->add_random_questions(1, 1, $filtercondition);
411
        $quizobj->get_grade_calculator()->recompute_quiz_sumgrades();
412
 
413
        // Create an attempt.
414
        $quizobj = quiz_settings::create($quiz->id);
415
        $attempt = quiz_prepare_and_start_new_attempt($quizobj, 1, null);
416
        $this->assertEquals(1, $attempt->preview);
417
    }
418
 
419
    /**
420
     * Test check_page_access function
421
     * @covers \quiz_attempt::check_page_access
422
     */
423
    public function test_check_page_access() {
424
        $timenow = time();
425
 
426
        // Free navigation.
427
        $attempt = $this->create_quiz_and_attempt_with_layout('1,0,2,0,3,0,4,0,5,0', QUIZ_NAVMETHOD_FREE);
428
 
429
        // Check access.
430
        $this->assertTrue($attempt->check_page_access(4));
431
        $this->assertTrue($attempt->check_page_access(3));
432
        $this->assertTrue($attempt->check_page_access(2));
433
        $this->assertTrue($attempt->check_page_access(1));
434
        $this->assertTrue($attempt->check_page_access(0));
435
        $this->assertTrue($attempt->check_page_access(2));
436
 
437
        // Access page 2.
438
        $attempt->set_currentpage(2);
439
        $attempt = quiz_attempt::create($attempt->get_attempt()->id);
440
 
441
        // Check access.
442
        $this->assertTrue($attempt->check_page_access(0));
443
        $this->assertTrue($attempt->check_page_access(1));
444
        $this->assertTrue($attempt->check_page_access(2));
445
        $this->assertTrue($attempt->check_page_access(3));
446
        $this->assertTrue($attempt->check_page_access(4));
447
 
448
        // Sequential navigation.
449
        $attempt = $this->create_quiz_and_attempt_with_layout('1,0,2,0,3,0,4,0,5,0', QUIZ_NAVMETHOD_SEQ);
450
 
451
        // Check access.
452
        $this->assertTrue($attempt->check_page_access(0));
453
        $this->assertTrue($attempt->check_page_access(1));
454
        $this->assertFalse($attempt->check_page_access(2));
455
        $this->assertFalse($attempt->check_page_access(3));
456
        $this->assertFalse($attempt->check_page_access(4));
457
 
458
        // Access page 1.
459
        $attempt->set_currentpage(1);
460
        $attempt = quiz_attempt::create($attempt->get_attempt()->id);
461
        $this->assertTrue($attempt->check_page_access(1));
462
 
463
        // Access page 2.
464
        $attempt->set_currentpage(2);
465
        $attempt = quiz_attempt::create($attempt->get_attempt()->id);
466
        $this->assertTrue($attempt->check_page_access(2));
467
 
468
        $this->assertTrue($attempt->check_page_access(3));
469
        $this->assertFalse($attempt->check_page_access(4));
470
        $this->assertFalse($attempt->check_page_access(1));
471
    }
472
 
473
    /**
474
     * Starting a new attempt with a question in draft status should throw an exception.
475
     *
476
     * @covers ::quiz_start_new_attempt()
477
     * @return void
478
     */
479
    public function test_start_new_attempt_with_draft(): void {
480
        $this->resetAfterTest();
481
 
482
        // Create course.
483
        $course = $this->getDataGenerator()->create_course();
484
        // Create students.
485
        $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
486
        $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
487
        // Create quiz.
488
        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
489
        $quiz = $quizgenerator->create_instance(['course' => $course->id, 'grade' => 100.0, 'sumgrades' => 2, 'layout' => '1,0']);
490
        // Create question and add it to quiz.
491
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
492
        $cat = $questiongenerator->create_question_category();
493
        $question = $questiongenerator->create_question('shortanswer', null,
494
                ['category' => $cat->id, 'status' => question_version_status::QUESTION_STATUS_DRAFT]);
495
        quiz_add_quiz_question($question->id, $quiz, 1);
496
 
497
        $quizobj = quiz_settings::create($quiz->id);
498
        $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
499
        $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
500
        $attempt = quiz_create_attempt($quizobj, 1, false, time(), false, $student1->id);
501
 
502
        $this->expectExceptionObject(new \moodle_exception('questiondraftonly', 'mod_quiz', '', $question->name));
503
        quiz_start_new_attempt($quizobj, $quba, $attempt, 1, time());
504
    }
505
 
506
    /**
507
     * Starting a new attempt built on last with a question in draft status should throw an exception.
508
     *
509
     * @covers ::quiz_start_attempt_built_on_last()
510
     * @return void
511
     */
512
    public function test_quiz_start_attempt_built_on_last_with_draft(): void {
513
        global $DB;
514
        $this->resetAfterTest();
515
 
516
        // Create course.
517
        $course = $this->getDataGenerator()->create_course();
518
        // Create students.
519
        $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
520
        $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
521
        // Create quiz.
522
        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
523
        $quiz = $quizgenerator->create_instance(['course' => $course->id, 'grade' => 100.0, 'sumgrades' => 2, 'layout' => '1,0']);
524
        // Create question and add it to quiz.
525
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
526
        $cat = $questiongenerator->create_question_category();
527
        $question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
528
        quiz_add_quiz_question($question->id, $quiz, 1);
529
 
530
        $quizobj = quiz_settings::create($quiz->id);
531
        $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
532
        $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
533
        $attempt = quiz_create_attempt($quizobj, 1, false, time(), false, $student1->id);
534
        $attempt = quiz_start_new_attempt($quizobj, $quba, $attempt, 1, time());
535
        $attempt = quiz_attempt_save_started($quizobj, $quba, $attempt);
536
        $DB->set_field('question_versions', 'status', question_version_status::QUESTION_STATUS_DRAFT,
537
                ['questionid' => $question->id]);
538
        // We need to reset the cache since the question has been edited by changing its status to draft.
539
        \question_bank::notify_question_edited($question->id);
540
        $quizobj = quiz_settings::create($quiz->id);
541
        $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
542
        $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
543
        $newattempt = quiz_create_attempt($quizobj, 2, $attempt, time(), false, $student1->id);
544
 
545
        $this->expectExceptionObject(new \moodle_exception('questiondraftonly', 'mod_quiz', '', $question->name));
546
        quiz_start_attempt_built_on_last($quba, $newattempt, $attempt);
547
    }
548
 
549
    public function test_get_grade_item_totals(): void {
550
        $attemptobj = $this->create_quiz_and_attempt_with_layout('1,2,3,0');
551
        /** @var mod_quiz_generator $quizgenerator */
552
        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
553
 
554
        // Set up some section grades.
555
        $listeninggrade = $quizgenerator->create_grade_item(['quizid' => $attemptobj->get_quizid(), 'name' => 'Listening']);
556
        $readinggrade = $quizgenerator->create_grade_item(['quizid' => $attemptobj->get_quizid(), 'name' => 'Reading']);
557
        $structure = $attemptobj->get_quizobj()->get_structure();
558
        $structure->update_slot_grade_item($structure->get_slot_by_number(1), $listeninggrade->id);
559
        $structure->update_slot_grade_item($structure->get_slot_by_number(2), $listeninggrade->id);
560
        $structure->update_slot_grade_item($structure->get_slot_by_number(3), $readinggrade->id);
561
 
562
        // Reload the attempt and verify.
563
        $attemptobj = quiz_attempt::create($attemptobj->get_attemptid());
564
        $grades = $attemptobj->get_grade_item_totals();
565
 
566
        // All grades zero because student has not done the quiz yet, but this is a sufficent test.
567
        $this->assertEquals('Listening', $grades[$listeninggrade->id]->name);
568
        $this->assertEquals(0, $grades[$listeninggrade->id]->grade);
569
        $this->assertEquals(2, $grades[$listeninggrade->id]->maxgrade);
570
        $this->assertEquals('Reading', $grades[$readinggrade->id]->name);
571
        $this->assertEquals(0, $grades[$readinggrade->id]->grade);
572
        $this->assertEquals(1, $grades[$readinggrade->id]->maxgrade);
573
    }
574
}