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 The Open University
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
24
 */
25
namespace mod_quiz;
26
 
27
use context_module;
28
use core_external\external_api;
29
use mod_quiz\quiz_settings;
30
 
31
defined('MOODLE_INTERNAL') || die();
32
 
33
global $CFG;
34
require_once($CFG->dirroot . '/mod/quiz/lib.php');
35
require_once($CFG->dirroot . '/mod/quiz/tests/quiz_question_helper_test_trait.php');
36
 
37
/**
38
 * @copyright  2008 The Open University
39
 * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
40
 */
1441 ariadna 41
final class lib_test extends \advanced_testcase {
1 efrain 42
    use \quiz_question_helper_test_trait;
43
 
11 efrain 44
    public function test_quiz_has_grades(): void {
1 efrain 45
        $quiz = new \stdClass();
46
        $quiz->grade = '100.0000';
47
        $quiz->sumgrades = '100.0000';
48
        $this->assertTrue(quiz_has_grades($quiz));
49
        $quiz->sumgrades = '0.0000';
50
        $this->assertFalse(quiz_has_grades($quiz));
51
        $quiz->grade = '0.0000';
52
        $this->assertFalse(quiz_has_grades($quiz));
53
        $quiz->sumgrades = '100.0000';
54
        $this->assertFalse(quiz_has_grades($quiz));
55
    }
56
 
11 efrain 57
    public function test_quiz_format_grade(): void {
1 efrain 58
        $quiz = new \stdClass();
59
        $quiz->decimalpoints = 2;
60
        $this->assertEquals(quiz_format_grade($quiz, 0.12345678), format_float(0.12, 2));
61
        $this->assertEquals(quiz_format_grade($quiz, 0), format_float(0, 2));
62
        $this->assertEquals(quiz_format_grade($quiz, 1.000000000000), format_float(1, 2));
63
        $quiz->decimalpoints = 0;
64
        $this->assertEquals(quiz_format_grade($quiz, 0.12345678), '0');
65
    }
66
 
11 efrain 67
    public function test_quiz_get_grade_format(): void {
1 efrain 68
        $quiz = new \stdClass();
69
        $quiz->decimalpoints = 2;
70
        $this->assertEquals(quiz_get_grade_format($quiz), 2);
71
        $this->assertEquals($quiz->questiondecimalpoints, -1);
72
        $quiz->questiondecimalpoints = 2;
73
        $this->assertEquals(quiz_get_grade_format($quiz), 2);
74
        $quiz->decimalpoints = 3;
75
        $quiz->questiondecimalpoints = -1;
76
        $this->assertEquals(quiz_get_grade_format($quiz), 3);
77
        $quiz->questiondecimalpoints = 4;
78
        $this->assertEquals(quiz_get_grade_format($quiz), 4);
79
    }
80
 
11 efrain 81
    public function test_quiz_format_question_grade(): void {
1 efrain 82
        $quiz = new \stdClass();
83
        $quiz->decimalpoints = 2;
84
        $quiz->questiondecimalpoints = 2;
85
        $this->assertEquals(quiz_format_question_grade($quiz, 0.12345678), format_float(0.12, 2));
86
        $this->assertEquals(quiz_format_question_grade($quiz, 0), format_float(0, 2));
87
        $this->assertEquals(quiz_format_question_grade($quiz, 1.000000000000), format_float(1, 2));
88
        $quiz->decimalpoints = 3;
89
        $quiz->questiondecimalpoints = -1;
90
        $this->assertEquals(quiz_format_question_grade($quiz, 0.12345678), format_float(0.123, 3));
91
        $this->assertEquals(quiz_format_question_grade($quiz, 0), format_float(0, 3));
92
        $this->assertEquals(quiz_format_question_grade($quiz, 1.000000000000), format_float(1, 3));
93
        $quiz->questiondecimalpoints = 4;
94
        $this->assertEquals(quiz_format_question_grade($quiz, 0.12345678), format_float(0.1235, 4));
95
        $this->assertEquals(quiz_format_question_grade($quiz, 0), format_float(0, 4));
96
        $this->assertEquals(quiz_format_question_grade($quiz, 1.000000000000), format_float(1, 4));
97
    }
98
 
99
    /**
100
     * Test deleting a quiz instance.
101
     */
11 efrain 102
    public function test_quiz_delete_instance(): void {
1 efrain 103
        global $SITE, $DB;
104
        $this->resetAfterTest(true);
105
        $this->setAdminUser();
106
 
107
        // Setup a quiz with 1 standard and 1 random question.
108
        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
109
        $quiz = $quizgenerator->create_instance(['course' => $SITE->id, 'questionsperpage' => 3, 'grade' => 100.0]);
110
        $context = context_module::instance($quiz->cmid);
111
 
112
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
113
        $cat = $questiongenerator->create_question_category();
114
        $standardq = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
115
 
116
        quiz_add_quiz_question($standardq->id, $quiz);
117
        $this->add_random_questions($quiz->id, 0, $cat->id, 1);
118
 
119
        // Get the random question.
120
        $randomq = $DB->get_record('question', ['qtype' => 'random']);
121
 
122
        quiz_delete_instance($quiz->id);
123
 
124
        // Check that the random question was deleted.
125
        if ($randomq) {
126
            $this->assertEquals(0, $DB->count_records('question', ['id' => $randomq->id]));
127
        }
128
        // Check that the standard question was not deleted.
129
        $this->assertEquals(1, $DB->count_records('question', ['id' => $standardq->id]));
130
 
131
        // Check that all the slots were removed.
132
        $this->assertEquals(0, $DB->count_records('quiz_slots', ['quizid' => $quiz->id]));
133
 
134
        // Check that the quiz was removed.
135
        $this->assertEquals(0, $DB->count_records('quiz', ['id' => $quiz->id]));
136
 
137
        // Check that any question references linked to this quiz are gone.
138
        $this->assertEquals(0, $DB->count_records('question_references', ['usingcontextid' => $context->id]));
139
        $this->assertEquals(0, $DB->count_records('question_set_references', ['usingcontextid' => $context->id]));
140
    }
141
 
1441 ariadna 142
    /**
143
     * Test deleting a quiz when the course is deleted.
144
     *
145
     * @covers ::quiz_delete_instance
146
     */
147
    public function test_quiz_when_delete_course(): void {
148
        global $DB, $USER;
149
 
150
        $this->resetAfterTest();
151
        $this->setAdminUser();
152
 
153
        // Step 1: Create one course and a user with editing teacher capabilities.
154
        $generator = $this->getDataGenerator();
155
        $course = $generator->create_course();
156
        $teacher = $USER;
157
        $generator->enrol_user($teacher->id, $course->id, 'editingteacher');
158
 
159
        // Create a quiz with questions in the first course.
160
        $quiz = $this->create_test_quiz($course);
161
        $context = \context_module::instance($quiz->cmid);
162
        // Create questions.
163
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
164
        $cat = $questiongenerator->create_question_category(['contextid' => $context->id]);
165
        $saq = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
166
 
167
        // Add to the quiz.
168
        quiz_add_quiz_question($saq->id, $quiz);
169
        // Delete the course.
170
        delete_course($course, false);
171
 
172
        // Check that the question was deleted.
173
        $this->assertFalse($DB->record_exists('question', ['id' => $saq->id]));
174
        // Check that all the slots were removed.
175
        $this->assertFalse($DB->record_exists('quiz_slots', ['quizid' => $quiz->id]));
176
        // Check that the quiz was removed.
177
        $this->assertFalse($DB->record_exists('quiz', ['id' => $quiz->id]));
178
        // Check that any question references linked to this quiz are gone.
179
        $this->assertFalse($DB->record_exists('question_references', ['usingcontextid' => $context->id]));
180
        $this->assertFalse($DB->record_exists('question_set_references', ['usingcontextid' => $context->id]));
181
    }
182
 
11 efrain 183
    public function test_quiz_get_user_attempts(): void {
1 efrain 184
        global $DB;
185
        $this->resetAfterTest();
186
 
187
        $dg = $this->getDataGenerator();
188
        $quizgen = $dg->get_plugin_generator('mod_quiz');
189
        $course = $dg->create_course();
190
        $u1 = $dg->create_user();
191
        $u2 = $dg->create_user();
192
        $u3 = $dg->create_user();
193
        $u4 = $dg->create_user();
194
        $role = $DB->get_record('role', ['shortname' => 'student']);
195
 
196
        $dg->enrol_user($u1->id, $course->id, $role->id);
197
        $dg->enrol_user($u2->id, $course->id, $role->id);
198
        $dg->enrol_user($u3->id, $course->id, $role->id);
199
        $dg->enrol_user($u4->id, $course->id, $role->id);
200
 
201
        $quiz1 = $quizgen->create_instance(['course' => $course->id, 'sumgrades' => 2]);
202
        $quiz2 = $quizgen->create_instance(['course' => $course->id, 'sumgrades' => 2]);
203
 
204
        // Questions.
205
        $questgen = $dg->get_plugin_generator('core_question');
206
        $quizcat = $questgen->create_question_category();
207
        $question = $questgen->create_question('numerical', null, ['category' => $quizcat->id]);
208
        quiz_add_quiz_question($question->id, $quiz1);
209
        quiz_add_quiz_question($question->id, $quiz2);
210
 
211
        $quizobj1a = quiz_settings::create($quiz1->id, $u1->id);
212
        $quizobj1b = quiz_settings::create($quiz1->id, $u2->id);
213
        $quizobj1c = quiz_settings::create($quiz1->id, $u3->id);
214
        $quizobj1d = quiz_settings::create($quiz1->id, $u4->id);
215
        $quizobj2a = quiz_settings::create($quiz2->id, $u1->id);
216
 
217
        // Set attempts.
218
        $quba1a = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1a->get_context());
219
        $quba1a->set_preferred_behaviour($quizobj1a->get_quiz()->preferredbehaviour);
220
        $quba1b = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1b->get_context());
221
        $quba1b->set_preferred_behaviour($quizobj1b->get_quiz()->preferredbehaviour);
222
        $quba1c = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1c->get_context());
223
        $quba1c->set_preferred_behaviour($quizobj1c->get_quiz()->preferredbehaviour);
224
        $quba1d = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1d->get_context());
225
        $quba1d->set_preferred_behaviour($quizobj1d->get_quiz()->preferredbehaviour);
226
        $quba2a = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context());
227
        $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour);
228
 
229
        $timenow = time();
230
 
231
        // User 1 passes quiz 1.
232
        $attempt = quiz_create_attempt($quizobj1a, 1, false, $timenow, false, $u1->id);
233
        quiz_start_new_attempt($quizobj1a, $quba1a, $attempt, 1, $timenow);
234
        quiz_attempt_save_started($quizobj1a, $quba1a, $attempt);
235
        $attemptobj = quiz_attempt::create($attempt->id);
236
        $attemptobj->process_submitted_actions($timenow, false, [1 => ['answer' => '3.14']]);
1441 ariadna 237
        $attemptobj->process_submit($timenow, false);
238
        $attemptobj->process_grade_submission($timenow);
1 efrain 239
 
240
        // User 2 goes overdue in quiz 1.
241
        $attempt = quiz_create_attempt($quizobj1b, 1, false, $timenow, false, $u2->id);
242
        quiz_start_new_attempt($quizobj1b, $quba1b, $attempt, 1, $timenow);
243
        quiz_attempt_save_started($quizobj1b, $quba1b, $attempt);
244
        $attemptobj = quiz_attempt::create($attempt->id);
245
        $attemptobj->process_going_overdue($timenow, true);
246
 
247
        // User 3 does not finish quiz 1.
248
        $attempt = quiz_create_attempt($quizobj1c, 1, false, $timenow, false, $u3->id);
249
        quiz_start_new_attempt($quizobj1c, $quba1c, $attempt, 1, $timenow);
250
        quiz_attempt_save_started($quizobj1c, $quba1c, $attempt);
251
 
252
        // User 4 abandons the quiz 1.
253
        $attempt = quiz_create_attempt($quizobj1d, 1, false, $timenow, false, $u4->id);
254
        quiz_start_new_attempt($quizobj1d, $quba1d, $attempt, 1, $timenow);
255
        quiz_attempt_save_started($quizobj1d, $quba1d, $attempt);
256
        $attemptobj = quiz_attempt::create($attempt->id);
257
        $attemptobj->process_abandon($timenow, true);
258
 
259
        // User 1 attempts the quiz three times (abandon, finish, in progress).
260
        $quba2a = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context());
261
        $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour);
262
 
263
        $attempt = quiz_create_attempt($quizobj2a, 1, false, $timenow, false, $u1->id);
264
        quiz_start_new_attempt($quizobj2a, $quba2a, $attempt, 1, $timenow);
265
        quiz_attempt_save_started($quizobj2a, $quba2a, $attempt);
266
        $attemptobj = quiz_attempt::create($attempt->id);
267
        $attemptobj->process_abandon($timenow, true);
268
 
269
        $quba2a = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context());
270
        $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour);
271
 
272
        $attempt = quiz_create_attempt($quizobj2a, 2, false, $timenow, false, $u1->id);
273
        quiz_start_new_attempt($quizobj2a, $quba2a, $attempt, 2, $timenow);
274
        quiz_attempt_save_started($quizobj2a, $quba2a, $attempt);
275
        $attemptobj = quiz_attempt::create($attempt->id);
1441 ariadna 276
        $attemptobj->process_submit($timenow, false);
277
        $attemptobj->process_grade_submission($timenow);
1 efrain 278
 
279
        $quba2a = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context());
280
        $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour);
281
 
282
        $attempt = quiz_create_attempt($quizobj2a, 3, false, $timenow, false, $u1->id);
283
        quiz_start_new_attempt($quizobj2a, $quba2a, $attempt, 3, $timenow);
284
        quiz_attempt_save_started($quizobj2a, $quba2a, $attempt);
285
 
286
        // Check for user 1.
287
        $attempts = quiz_get_user_attempts($quiz1->id, $u1->id, 'all');
288
        $this->assertCount(1, $attempts);
289
        $attempt = array_shift($attempts);
290
        $this->assertEquals(quiz_attempt::FINISHED, $attempt->state);
291
        $this->assertEquals($u1->id, $attempt->userid);
292
        $this->assertEquals($quiz1->id, $attempt->quiz);
293
 
294
        $attempts = quiz_get_user_attempts($quiz1->id, $u1->id, 'finished');
295
        $this->assertCount(1, $attempts);
296
        $attempt = array_shift($attempts);
297
        $this->assertEquals(quiz_attempt::FINISHED, $attempt->state);
298
        $this->assertEquals($u1->id, $attempt->userid);
299
        $this->assertEquals($quiz1->id, $attempt->quiz);
300
 
301
        $attempts = quiz_get_user_attempts($quiz1->id, $u1->id, 'unfinished');
302
        $this->assertCount(0, $attempts);
303
 
304
        // Check for user 2.
305
        $attempts = quiz_get_user_attempts($quiz1->id, $u2->id, 'all');
306
        $this->assertCount(1, $attempts);
307
        $attempt = array_shift($attempts);
308
        $this->assertEquals(quiz_attempt::OVERDUE, $attempt->state);
309
        $this->assertEquals($u2->id, $attempt->userid);
310
        $this->assertEquals($quiz1->id, $attempt->quiz);
311
 
312
        $attempts = quiz_get_user_attempts($quiz1->id, $u2->id, 'finished');
313
        $this->assertCount(0, $attempts);
314
 
315
        $attempts = quiz_get_user_attempts($quiz1->id, $u2->id, 'unfinished');
316
        $this->assertCount(1, $attempts);
317
        $attempt = array_shift($attempts);
318
        $this->assertEquals(quiz_attempt::OVERDUE, $attempt->state);
319
        $this->assertEquals($u2->id, $attempt->userid);
320
        $this->assertEquals($quiz1->id, $attempt->quiz);
321
 
322
        // Check for user 3.
323
        $attempts = quiz_get_user_attempts($quiz1->id, $u3->id, 'all');
324
        $this->assertCount(1, $attempts);
325
        $attempt = array_shift($attempts);
326
        $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state);
327
        $this->assertEquals($u3->id, $attempt->userid);
328
        $this->assertEquals($quiz1->id, $attempt->quiz);
329
 
330
        $attempts = quiz_get_user_attempts($quiz1->id, $u3->id, 'finished');
331
        $this->assertCount(0, $attempts);
332
 
333
        $attempts = quiz_get_user_attempts($quiz1->id, $u3->id, 'unfinished');
334
        $this->assertCount(1, $attempts);
335
        $attempt = array_shift($attempts);
336
        $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state);
337
        $this->assertEquals($u3->id, $attempt->userid);
338
        $this->assertEquals($quiz1->id, $attempt->quiz);
339
 
340
        // Check for user 4.
341
        $attempts = quiz_get_user_attempts($quiz1->id, $u4->id, 'all');
342
        $this->assertCount(1, $attempts);
343
        $attempt = array_shift($attempts);
344
        $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state);
345
        $this->assertEquals($u4->id, $attempt->userid);
346
        $this->assertEquals($quiz1->id, $attempt->quiz);
347
 
348
        $attempts = quiz_get_user_attempts($quiz1->id, $u4->id, 'finished');
349
        $this->assertCount(1, $attempts);
350
        $attempt = array_shift($attempts);
351
        $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state);
352
        $this->assertEquals($u4->id, $attempt->userid);
353
        $this->assertEquals($quiz1->id, $attempt->quiz);
354
 
355
        $attempts = quiz_get_user_attempts($quiz1->id, $u4->id, 'unfinished');
356
        $this->assertCount(0, $attempts);
357
 
358
        // Multiple attempts for user 1 in quiz 2.
359
        $attempts = quiz_get_user_attempts($quiz2->id, $u1->id, 'all');
360
        $this->assertCount(3, $attempts);
361
        $attempt = array_shift($attempts);
362
        $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state);
363
        $this->assertEquals($u1->id, $attempt->userid);
364
        $this->assertEquals($quiz2->id, $attempt->quiz);
365
        $attempt = array_shift($attempts);
366
        $this->assertEquals(quiz_attempt::FINISHED, $attempt->state);
367
        $this->assertEquals($u1->id, $attempt->userid);
368
        $this->assertEquals($quiz2->id, $attempt->quiz);
369
        $attempt = array_shift($attempts);
370
        $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state);
371
        $this->assertEquals($u1->id, $attempt->userid);
372
        $this->assertEquals($quiz2->id, $attempt->quiz);
373
 
374
        $attempts = quiz_get_user_attempts($quiz2->id, $u1->id, 'finished');
375
        $this->assertCount(2, $attempts);
376
        $attempt = array_shift($attempts);
377
        $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state);
378
        $attempt = array_shift($attempts);
379
        $this->assertEquals(quiz_attempt::FINISHED, $attempt->state);
380
 
381
        $attempts = quiz_get_user_attempts($quiz2->id, $u1->id, 'unfinished');
382
        $this->assertCount(1, $attempts);
383
        $attempt = array_shift($attempts);
384
 
385
        // Multiple quiz attempts fetched at once.
386
        $attempts = quiz_get_user_attempts([$quiz1->id, $quiz2->id], $u1->id, 'all');
387
        $this->assertCount(4, $attempts);
388
        $attempt = array_shift($attempts);
389
        $this->assertEquals(quiz_attempt::FINISHED, $attempt->state);
390
        $this->assertEquals($u1->id, $attempt->userid);
391
        $this->assertEquals($quiz1->id, $attempt->quiz);
392
        $attempt = array_shift($attempts);
393
        $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state);
394
        $this->assertEquals($u1->id, $attempt->userid);
395
        $this->assertEquals($quiz2->id, $attempt->quiz);
396
        $attempt = array_shift($attempts);
397
        $this->assertEquals(quiz_attempt::FINISHED, $attempt->state);
398
        $this->assertEquals($u1->id, $attempt->userid);
399
        $this->assertEquals($quiz2->id, $attempt->quiz);
400
        $attempt = array_shift($attempts);
401
        $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state);
402
        $this->assertEquals($u1->id, $attempt->userid);
403
        $this->assertEquals($quiz2->id, $attempt->quiz);
404
    }
405
 
406
    /**
407
     * Test for quiz_get_group_override_priorities().
408
     */
11 efrain 409
    public function test_quiz_get_group_override_priorities(): void {
1 efrain 410
        global $DB;
411
        $this->resetAfterTest();
412
 
413
        $dg = $this->getDataGenerator();
414
        $quizgen = $dg->get_plugin_generator('mod_quiz');
415
        $course = $dg->create_course();
416
 
417
        $quiz = $quizgen->create_instance(['course' => $course->id, 'sumgrades' => 2]);
418
 
419
        $this->assertNull(quiz_get_group_override_priorities($quiz->id));
420
 
421
        $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
422
        $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
423
 
424
        $now = 100;
425
        $override1 = (object)[
426
            'quiz' => $quiz->id,
427
            'groupid' => $group1->id,
428
            'timeopen' => $now,
429
            'timeclose' => $now + 20
430
        ];
431
        $DB->insert_record('quiz_overrides', $override1);
432
 
433
        $override2 = (object)[
434
            'quiz' => $quiz->id,
435
            'groupid' => $group2->id,
436
            'timeopen' => $now - 10,
437
            'timeclose' => $now + 10
438
        ];
439
        $DB->insert_record('quiz_overrides', $override2);
440
 
441
        $priorities = quiz_get_group_override_priorities($quiz->id);
442
        $this->assertNotEmpty($priorities);
443
 
444
        $openpriorities = $priorities['open'];
445
        // Override 2's time open has higher priority since it is sooner than override 1's.
446
        $this->assertEquals(2, $openpriorities[$override1->timeopen]);
447
        $this->assertEquals(1, $openpriorities[$override2->timeopen]);
448
 
449
        $closepriorities = $priorities['close'];
450
        // Override 1's time close has higher priority since it is later than override 2's.
451
        $this->assertEquals(1, $closepriorities[$override1->timeclose]);
452
        $this->assertEquals(2, $closepriorities[$override2->timeclose]);
453
    }
454
 
11 efrain 455
    public function test_quiz_core_calendar_provide_event_action_open(): void {
1 efrain 456
        $this->resetAfterTest();
457
 
458
        $this->setAdminUser();
459
 
460
        // Create a course.
461
        $course = $this->getDataGenerator()->create_course();
462
        // Create a student and enrol into the course.
463
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
464
        // Create a quiz.
465
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id,
466
            'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS]);
467
 
468
        // Create a calendar event.
469
        $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN);
470
        // Now, log in as student.
471
        $this->setUser($student);
472
        // Create an action factory.
473
        $factory = new \core_calendar\action_factory();
474
 
475
        // Decorate action event.
476
        $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory);
477
 
478
        // Confirm the event was decorated.
479
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
480
        $this->assertEquals(get_string('attemptquiznow', 'quiz'), $actionevent->get_name());
481
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
482
        $this->assertEquals(1, $actionevent->get_item_count());
483
        $this->assertTrue($actionevent->is_actionable());
484
    }
485
 
11 efrain 486
    public function test_quiz_core_calendar_provide_event_action_open_for_user(): void {
1 efrain 487
        $this->resetAfterTest();
488
 
489
        $this->setAdminUser();
490
 
491
        // Create a course.
492
        $course = $this->getDataGenerator()->create_course();
493
        // Create a student and enrol into the course.
494
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
495
        // Create a quiz.
496
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id,
497
            'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS]);
498
 
499
        // Create a calendar event.
500
        $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN);
501
 
502
        // Create an action factory.
503
        $factory = new \core_calendar\action_factory();
504
 
505
        // Decorate action event for the student.
506
        $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id);
507
 
508
        // Confirm the event was decorated.
509
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
510
        $this->assertEquals(get_string('attemptquiznow', 'quiz'), $actionevent->get_name());
511
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
512
        $this->assertEquals(1, $actionevent->get_item_count());
513
        $this->assertTrue($actionevent->is_actionable());
514
    }
515
 
11 efrain 516
    public function test_quiz_core_calendar_provide_event_action_closed(): void {
1 efrain 517
        $this->resetAfterTest();
518
 
519
        $this->setAdminUser();
520
 
521
        // Create a course.
522
        $course = $this->getDataGenerator()->create_course();
523
 
524
        // Create a quiz.
525
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id,
526
            'timeclose' => time() - DAYSECS]);
527
 
528
        // Create a calendar event.
529
        $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_CLOSE);
530
 
531
        // Create an action factory.
532
        $factory = new \core_calendar\action_factory();
533
 
534
        // Confirm the result was null.
535
        $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory));
536
    }
537
 
11 efrain 538
    public function test_quiz_core_calendar_provide_event_action_closed_for_user(): void {
1 efrain 539
        $this->resetAfterTest();
540
 
541
        $this->setAdminUser();
542
 
543
        // Create a course.
544
        $course = $this->getDataGenerator()->create_course();
545
 
546
        // Create a student.
547
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
548
 
549
        // Create a quiz.
550
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id,
551
            'timeclose' => time() - DAYSECS]);
552
 
553
        // Create a calendar event.
554
        $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_CLOSE);
555
 
556
        // Create an action factory.
557
        $factory = new \core_calendar\action_factory();
558
 
559
        // Confirm the result was null.
560
        $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id));
561
    }
562
 
11 efrain 563
    public function test_quiz_core_calendar_provide_event_action_open_in_future(): void {
1 efrain 564
        $this->resetAfterTest();
565
 
566
        $this->setAdminUser();
567
 
568
        // Create a course.
569
        $course = $this->getDataGenerator()->create_course();
570
        // Create a student and enrol into the course.
571
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
572
        // Create a quiz.
573
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id,
574
            'timeopen' => time() + DAYSECS]);
575
 
576
        // Create a calendar event.
577
        $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_CLOSE);
578
        // Now, log in as student.
579
        $this->setUser($student);
580
        // Create an action factory.
581
        $factory = new \core_calendar\action_factory();
582
 
583
        // Decorate action event.
584
        $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory);
585
 
586
        // Confirm the event was decorated.
587
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
588
        $this->assertEquals(get_string('attemptquiznow', 'quiz'), $actionevent->get_name());
589
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
590
        $this->assertEquals(1, $actionevent->get_item_count());
591
        $this->assertFalse($actionevent->is_actionable());
592
    }
593
 
11 efrain 594
    public function test_quiz_core_calendar_provide_event_action_open_in_future_for_user(): void {
1 efrain 595
        $this->resetAfterTest();
596
 
597
        $this->setAdminUser();
598
 
599
        // Create a course.
600
        $course = $this->getDataGenerator()->create_course();
601
        // Create a student and enrol into the course.
602
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
603
        // Create a quiz.
604
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id,
605
            'timeopen' => time() + DAYSECS]);
606
 
607
        // Create a calendar event.
608
        $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_CLOSE);
609
 
610
        // Create an action factory.
611
        $factory = new \core_calendar\action_factory();
612
 
613
        // Decorate action event for the student.
614
        $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id);
615
 
616
        // Confirm the event was decorated.
617
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
618
        $this->assertEquals(get_string('attemptquiznow', 'quiz'), $actionevent->get_name());
619
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
620
        $this->assertEquals(1, $actionevent->get_item_count());
621
        $this->assertFalse($actionevent->is_actionable());
622
    }
623
 
11 efrain 624
    public function test_quiz_core_calendar_provide_event_action_no_capability(): void {
1 efrain 625
        global $DB;
626
 
627
        $this->resetAfterTest();
628
        $this->setAdminUser();
629
 
630
        // Create a course.
631
        $course = $this->getDataGenerator()->create_course();
632
 
633
        // Create a student.
634
        $student = $this->getDataGenerator()->create_user();
635
        $studentrole = $DB->get_record('role', ['shortname' => 'student']);
636
 
637
        // Enrol student.
638
        $this->assertTrue($this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id));
639
 
640
        // Create a quiz.
641
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
642
 
643
        // Remove the permission to attempt or review the quiz for the student role.
644
        $coursecontext = \context_course::instance($course->id);
645
        assign_capability('mod/quiz:reviewmyattempts', CAP_PROHIBIT, $studentrole->id, $coursecontext);
646
        assign_capability('mod/quiz:attempt', CAP_PROHIBIT, $studentrole->id, $coursecontext);
647
 
648
        // Create a calendar event.
649
        $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN);
650
 
651
        // Create an action factory.
652
        $factory = new \core_calendar\action_factory();
653
 
654
        // Set current user to the student.
655
        $this->setUser($student);
656
 
657
        // Confirm null is returned.
658
        $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory));
659
    }
660
 
11 efrain 661
    public function test_quiz_core_calendar_provide_event_action_no_capability_for_user(): void {
1 efrain 662
        global $DB;
663
 
664
        $this->resetAfterTest();
665
        $this->setAdminUser();
666
 
667
        // Create a course.
668
        $course = $this->getDataGenerator()->create_course();
669
 
670
        // Create a student.
671
        $student = $this->getDataGenerator()->create_user();
672
        $studentrole = $DB->get_record('role', ['shortname' => 'student']);
673
 
674
        // Enrol student.
675
        $this->assertTrue($this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id));
676
 
677
        // Create a quiz.
678
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
679
 
680
        // Remove the permission to attempt or review the quiz for the student role.
681
        $coursecontext = \context_course::instance($course->id);
682
        assign_capability('mod/quiz:reviewmyattempts', CAP_PROHIBIT, $studentrole->id, $coursecontext);
683
        assign_capability('mod/quiz:attempt', CAP_PROHIBIT, $studentrole->id, $coursecontext);
684
 
685
        // Create a calendar event.
686
        $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN);
687
 
688
        // Create an action factory.
689
        $factory = new \core_calendar\action_factory();
690
 
691
        // Confirm null is returned.
692
        $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id));
693
    }
694
 
11 efrain 695
    public function test_quiz_core_calendar_provide_event_action_already_finished(): void {
1 efrain 696
        global $DB;
697
 
698
        $this->resetAfterTest();
699
 
700
        $this->setAdminUser();
701
 
702
        // Create a course.
703
        $course = $this->getDataGenerator()->create_course();
704
 
705
        // Create a student.
706
        $student = $this->getDataGenerator()->create_user();
707
        $studentrole = $DB->get_record('role', ['shortname' => 'student']);
708
 
709
        // Enrol student.
710
        $this->assertTrue($this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id));
711
 
712
        // Create a quiz.
713
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id,
714
            'sumgrades' => 1]);
715
 
716
        // Add a question to the quiz.
717
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
718
        $cat = $questiongenerator->create_question_category();
719
        $question = $questiongenerator->create_question('numerical', null, ['category' => $cat->id]);
720
        quiz_add_quiz_question($question->id, $quiz);
721
 
722
        // Get the quiz object.
723
        $quizobj = quiz_settings::create($quiz->id, $student->id);
724
 
725
        // Create an attempt for the student in the quiz.
726
        $timenow = time();
727
        $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $student->id);
728
        $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
729
        $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
730
        quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
731
        quiz_attempt_save_started($quizobj, $quba, $attempt);
732
 
733
        // Finish the attempt.
734
        $attemptobj = quiz_attempt::create($attempt->id);
1441 ariadna 735
        $attemptobj->process_submit($timenow, false);
736
        $attemptobj->process_grade_submission($timenow);
1 efrain 737
 
738
        // Create a calendar event.
739
        $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN);
740
 
741
        // Create an action factory.
742
        $factory = new \core_calendar\action_factory();
743
 
744
        // Set current user to the student.
745
        $this->setUser($student);
746
 
747
        // Confirm null is returned.
748
        $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory));
749
    }
750
 
11 efrain 751
    public function test_quiz_core_calendar_provide_event_action_already_finished_for_user(): void {
1 efrain 752
        global $DB;
753
 
754
        $this->resetAfterTest();
755
 
756
        $this->setAdminUser();
757
 
758
        // Create a course.
759
        $course = $this->getDataGenerator()->create_course();
760
 
761
        // Create a student.
762
        $student = $this->getDataGenerator()->create_user();
763
        $studentrole = $DB->get_record('role', ['shortname' => 'student']);
764
 
765
        // Enrol student.
766
        $this->assertTrue($this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id));
767
 
768
        // Create a quiz.
769
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id,
770
            'sumgrades' => 1]);
771
 
772
        // Add a question to the quiz.
773
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
774
        $cat = $questiongenerator->create_question_category();
775
        $question = $questiongenerator->create_question('numerical', null, ['category' => $cat->id]);
776
        quiz_add_quiz_question($question->id, $quiz);
777
 
778
        // Get the quiz object.
779
        $quizobj = quiz_settings::create($quiz->id, $student->id);
780
 
781
        // Create an attempt for the student in the quiz.
782
        $timenow = time();
783
        $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $student->id);
784
        $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
785
        $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
786
        quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
787
        quiz_attempt_save_started($quizobj, $quba, $attempt);
788
 
789
        // Finish the attempt.
790
        $attemptobj = quiz_attempt::create($attempt->id);
1441 ariadna 791
        $attemptobj->process_submit($timenow, false);
792
        $attemptobj->process_grade_submission($timenow);
1 efrain 793
 
794
        // Create a calendar event.
795
        $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN);
796
 
797
        // Create an action factory.
798
        $factory = new \core_calendar\action_factory();
799
 
800
        // Confirm null is returned.
801
        $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id));
802
    }
803
 
11 efrain 804
    public function test_quiz_core_calendar_provide_event_action_already_completed(): void {
1 efrain 805
        $this->resetAfterTest();
806
        set_config('enablecompletion', 1);
807
        $this->setAdminUser();
808
 
809
        // Create the activity.
810
        $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
811
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id],
812
            ['completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS]);
813
 
814
        // Get some additional data.
815
        $cm = get_coursemodule_from_instance('quiz', $quiz->id);
816
 
817
        // Create a calendar event.
818
        $event = $this->create_action_event($course->id, $quiz->id,
819
            \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
820
 
821
        // Mark the activity as completed.
822
        $completion = new \completion_info($course);
823
        $completion->set_module_viewed($cm);
824
 
825
        // Create an action factory.
826
        $factory = new \core_calendar\action_factory();
827
 
828
        // Decorate action event.
829
        $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory);
830
 
831
        // Ensure result was null.
832
        $this->assertNull($actionevent);
833
    }
834
 
11 efrain 835
    public function test_quiz_core_calendar_provide_event_action_already_completed_for_user(): void {
1 efrain 836
        $this->resetAfterTest();
837
        set_config('enablecompletion', 1);
838
        $this->setAdminUser();
839
 
840
        // Create the activity.
841
        $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
842
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id],
843
            ['completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS]);
844
 
845
        // Enrol a student in the course.
846
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
847
 
848
        // Get some additional data.
849
        $cm = get_coursemodule_from_instance('quiz', $quiz->id);
850
 
851
        // Create a calendar event.
852
        $event = $this->create_action_event($course->id, $quiz->id,
853
            \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
854
 
855
        // Mark the activity as completed for the student.
856
        $completion = new \completion_info($course);
857
        $completion->set_module_viewed($cm, $student->id);
858
 
859
        // Create an action factory.
860
        $factory = new \core_calendar\action_factory();
861
 
862
        // Decorate action event for the student.
863
        $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id);
864
 
865
        // Ensure result was null.
866
        $this->assertNull($actionevent);
867
    }
868
 
869
    /**
870
     * Creates an action event.
871
     *
872
     * @param int $courseid
873
     * @param int $instanceid The quiz id.
874
     * @param string $eventtype The event type. eg. QUIZ_EVENT_TYPE_OPEN.
875
     * @return bool|calendar_event
876
     */
877
    private function create_action_event($courseid, $instanceid, $eventtype) {
878
        $event = new \stdClass();
879
        $event->name = 'Calendar event';
880
        $event->modulename  = 'quiz';
881
        $event->courseid = $courseid;
882
        $event->instance = $instanceid;
883
        $event->type = CALENDAR_EVENT_TYPE_ACTION;
884
        $event->eventtype = $eventtype;
885
        $event->timestart = time();
886
 
887
        return \calendar_event::create($event);
888
    }
889
 
890
    /**
891
     * Test the callback responsible for returning the completion rule descriptions.
892
     * This function should work given either an instance of the module (cm_info), such as when checking the active rules,
893
     * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type.
894
     */
11 efrain 895
    public function test_mod_quiz_completion_get_active_rule_descriptions(): void {
1 efrain 896
        $this->resetAfterTest();
897
        $this->setAdminUser();
898
 
899
        // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't.
900
        $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]);
901
        $quiz1 = $this->getDataGenerator()->create_module('quiz', [
902
            'course' => $course->id,
903
            'completion' => 2,
904
            'completionusegrade' => 1,
905
            'completionpassgrade' => 1,
906
            'completionattemptsexhausted' => 1,
907
        ]);
908
        $quiz2 = $this->getDataGenerator()->create_module('quiz', [
909
            'course' => $course->id,
910
            'completion' => 2,
911
            'completionusegrade' => 0
912
        ]);
913
        $cm1 = \cm_info::create(get_coursemodule_from_instance('quiz', $quiz1->id));
914
        $cm2 = \cm_info::create(get_coursemodule_from_instance('quiz', $quiz2->id));
915
 
916
        // Data for the stdClass input type.
917
        // This type of input would occur when checking the default completion rules for an activity type, where we don't have
918
        // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info.
919
        $moddefaults = new \stdClass();
920
        $moddefaults->customdata = ['customcompletionrules' => [
921
            'completionattemptsexhausted' => 1,
922
        ]];
923
        $moddefaults->completion = 2;
924
 
925
        $activeruledescriptions = [
926
            get_string('completionpassorattemptsexhausteddesc', 'quiz'),
927
        ];
928
        $this->assertEquals(mod_quiz_get_completion_active_rule_descriptions($cm1), $activeruledescriptions);
929
        $this->assertEquals(mod_quiz_get_completion_active_rule_descriptions($cm2), []);
930
        $this->assertEquals(mod_quiz_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions);
931
        $this->assertEquals(mod_quiz_get_completion_active_rule_descriptions(new \stdClass()), []);
932
    }
933
 
934
    /**
935
     * A user who does not have capabilities to add events to the calendar should be able to create a quiz.
936
     */
11 efrain 937
    public function test_creation_with_no_calendar_capabilities(): void {
1 efrain 938
        $this->resetAfterTest();
939
        $course = self::getDataGenerator()->create_course();
940
        $context = \context_course::instance($course->id);
941
        $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher');
942
        $roleid = self::getDataGenerator()->create_role();
943
        self::getDataGenerator()->role_assign($roleid, $user->id, $context->id);
944
        assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true);
945
        $generator = self::getDataGenerator()->get_plugin_generator('mod_quiz');
946
        // Create an instance as a user without the calendar capabilities.
947
        $this->setUser($user);
948
        $time = time();
949
        $params = [
950
            'course' => $course->id,
951
            'timeopen' => $time + 200,
952
            'timeclose' => $time + 2000,
953
        ];
954
        $generator->create_instance($params);
955
    }
956
 
957
    /**
958
     * Data provider for summarise_response() test cases.
959
     *
960
     * @return array List of data sets (test cases)
961
     */
1441 ariadna 962
    public static function mod_quiz_inplace_editable_provider(): array {
1 efrain 963
        return [
964
            'set to A1' => [1, 'A1'],
965
            'set with HTML characters' => [2, 'A & &amp; <-:'],
966
            'set to integer' => [3, '3'],
967
            'set to blank' => [4, ''],
968
            'set with Unicode characters' => [1, 'L\'Aina Lluís^'],
969
            'set with Unicode at the truncation point' => [1, '123456789012345碁'],
970
            'set with HTML Char at the truncation point' => [1, '123456789012345>'],
971
        ];
972
    }
973
 
974
    /**
975
     * Test customised and automated question numbering for a given slot number and customised value.
976
     *
977
     * @dataProvider mod_quiz_inplace_editable_provider
978
     * @param int $slotnumber
979
     * @param string $newvalue
980
     * @covers ::mod_quiz_inplace_editable
981
     */
982
    public function test_mod_quiz_inplace_editable(int $slotnumber, string $newvalue): void {
983
        global $CFG;
984
        require_once($CFG->dirroot . '/lib/external/externallib.php');
985
        $this->resetAfterTest();
986
 
987
        $this->setAdminUser();
988
        $course = self::getDataGenerator()->create_course();
989
        $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id, 'sumgrades' => 1]);
990
        $cm = get_coursemodule_from_id('quiz', $quiz->cmid);
991
 
992
        // Add few questions to the quiz.
993
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
994
        $cat = $questiongenerator->create_question_category();
995
 
996
        $question = $questiongenerator->create_question('truefalse', null, ['category' => $cat->id]);
997
        quiz_add_quiz_question($question->id, $quiz);
998
 
999
        $question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
1000
        quiz_add_quiz_question($question->id, $quiz);
1001
 
1002
        $question = $questiongenerator->create_question('multichoice', null, ['category' => $cat->id]);
1003
        quiz_add_quiz_question($question->id, $quiz);
1004
 
1005
        $question = $questiongenerator->create_question('numerical', null, ['category' => $cat->id]);
1006
        quiz_add_quiz_question($question->id, $quiz);
1007
 
1008
        // Create the quiz object.
1009
        $quizobj = new quiz_settings($quiz, $cm, $course);
1010
        $structure = $quizobj->get_structure();
1011
 
1012
        $slots = $structure->get_slots();
1013
        $this->assertEquals(4, count($slots));
1014
 
1015
        $slotid = $structure->get_slot_id_for_slot($slotnumber);
1016
        $inplaceeditable = mod_quiz_inplace_editable('slotdisplaynumber', $slotid, $newvalue);
1017
        $result = \core_external::update_inplace_editable('mod_quiz', 'slotdisplaynumber', $slotid, $newvalue);
1018
        $result = external_api::clean_returnvalue(\core_external::update_inplace_editable_returns(), $result);
1019
 
1020
        $this->assertEquals(count((array) $inplaceeditable), count($result));
1021
        $this->assertEquals($slotid, $result['itemid']);
1022
        if ($newvalue === '' || is_null($newvalue)) {
1023
            // Check against default.
1024
            $this->assertEquals($slotnumber, $result['displayvalue']);
1025
            $this->assertEquals($slotnumber, $result['value']);
1026
        } else {
1027
            // Check against the custom number.
1028
            $this->assertEquals(s($newvalue), $result['displayvalue']);
1029
            $this->assertEquals($newvalue, $result['value']);
1030
        }
1031
    }
1032
}