Proyectos de Subversion Moodle

Rev

Rev 1 | | 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
namespace mod_quiz;
18
 
19
use core_question\question_reference_manager;
20
use mod_quiz\question\display_options;
21
 
22
defined('MOODLE_INTERNAL') || die();
23
 
24
global $CFG;
25
require_once(__DIR__ . '/quiz_question_helper_test_trait.php');
26
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
27
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
28
 
29
/**
30
 * Quiz backup and restore tests.
31
 *
32
 * @package    mod_quiz
33
 * @category   test
34
 * @copyright  2021 Catalyst IT Australia Pty Ltd
35
 * @author     Safat Shahin <safatshahin@catalyst-au.net>
36
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37
 */
38
class quiz_question_restore_test extends \advanced_testcase {
39
    use \quiz_question_helper_test_trait;
40
 
41
    /**
42
     * @var \stdClass test student user.
43
     */
44
    protected $student;
45
 
46
    /**
47
     * Called before every test.
48
     */
49
    public function setUp(): void {
50
        global $USER;
51
        parent::setUp();
52
        $this->setAdminUser();
53
        $this->course = $this->getDataGenerator()->create_course();
54
        $this->student = $this->getDataGenerator()->create_user();
55
        $this->user = $USER;
56
    }
57
 
58
    /**
59
     * Test a quiz backup and restore in a different course without attempts for course question bank.
60
     *
11 efrain 61
     * @covers \mod_quiz\question\bank\qbank_helper::get_question_structure
1 efrain 62
     */
11 efrain 63
    public function test_quiz_restore_in_a_different_course_using_course_question_bank(): void {
1 efrain 64
        $this->resetAfterTest();
65
 
66
        // Create the test quiz.
67
        $quiz = $this->create_test_quiz($this->course);
68
        $oldquizcontext = \context_module::instance($quiz->cmid);
69
        // Test for questions from a different context.
70
        $coursecontext = \context_course::instance($this->course->id);
71
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
72
        $this->add_two_regular_questions($questiongenerator, $quiz, ['contextid' => $coursecontext->id]);
73
        $this->add_one_random_question($questiongenerator, $quiz, ['contextid' => $coursecontext->id]);
74
 
75
        // Make the backup.
76
        $backupid = $this->backup_quiz($quiz, $this->user);
77
 
78
        // Delete the current course to make sure there is no data.
79
        delete_course($this->course, false);
80
 
81
        // Check if the questions and associated data are deleted properly.
82
        $this->assertEquals(0, count(\mod_quiz\question\bank\qbank_helper::get_question_structure(
83
                $quiz->id, $oldquizcontext)));
84
 
85
        // Restore the course.
86
        $newcourse = $this->getDataGenerator()->create_course();
87
        $this->restore_quiz($backupid, $newcourse, $this->user);
88
 
89
        // Verify.
90
        $modules = get_fast_modinfo($newcourse->id)->get_instances_of('quiz');
91
        $module = reset($modules);
92
        $questions = \mod_quiz\question\bank\qbank_helper::get_question_structure(
93
                $module->instance, $module->context);
94
        $this->assertCount(3, $questions);
95
    }
96
 
97
    /**
98
     * Test a quiz backup and restore in a different course without attempts for quiz question bank.
99
     *
11 efrain 100
     * @covers \mod_quiz\question\bank\qbank_helper::get_question_structure
1 efrain 101
     */
11 efrain 102
    public function test_quiz_restore_in_a_different_course_using_quiz_question_bank(): void {
1 efrain 103
        $this->resetAfterTest();
104
 
105
        // Create the test quiz.
106
        $quiz = $this->create_test_quiz($this->course);
107
        // Test for questions from a different context.
108
        $quizcontext = \context_module::instance($quiz->cmid);
109
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
110
        $this->add_two_regular_questions($questiongenerator, $quiz, ['contextid' => $quizcontext->id]);
111
        $this->add_one_random_question($questiongenerator, $quiz, ['contextid' => $quizcontext->id]);
112
 
113
        // Make the backup.
114
        $backupid = $this->backup_quiz($quiz, $this->user);
115
 
116
        // Delete the current course to make sure there is no data.
117
        delete_course($this->course, false);
118
 
119
        // Check if the questions and associated datas are deleted properly.
120
        $this->assertEquals(0, count(\mod_quiz\question\bank\qbank_helper::get_question_structure(
121
                $quiz->id, $quizcontext)));
122
 
123
        // Restore the course.
124
        $newcourse = $this->getDataGenerator()->create_course();
125
        $this->restore_quiz($backupid, $newcourse, $this->user);
126
 
127
        // Verify.
128
        $modules = get_fast_modinfo($newcourse->id)->get_instances_of('quiz');
129
        $module = reset($modules);
130
        $this->assertEquals(3, count(\mod_quiz\question\bank\qbank_helper::get_question_structure(
131
                $module->instance, $module->context)));
132
    }
133
 
134
    /**
135
     * Count the questions for the context.
136
     *
137
     * @param int $contextid
138
     * @param string $extracondition
139
     * @return int the number of questions.
140
     */
141
    protected function question_count(int $contextid, string $extracondition = ''): int {
142
        global $DB;
143
        return $DB->count_records_sql(
144
            "SELECT COUNT(q.id)
145
               FROM {question} q
146
               JOIN {question_versions} qv ON qv.questionid = q.id
147
               JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
148
               JOIN {question_categories} qc on qc.id = qbe.questioncategoryid
149
              WHERE qc.contextid = ?
150
              $extracondition", [$contextid]);
151
    }
152
 
153
    /**
154
     * Test if a duplicate does not duplicate questions in course question bank.
155
     *
156
     * @covers ::duplicate_module
157
     */
11 efrain 158
    public function test_quiz_duplicate_does_not_duplicate_course_question_bank_questions(): void {
1 efrain 159
        $this->resetAfterTest();
160
        $quiz = $this->create_test_quiz($this->course);
161
        // Test for questions from a different context.
162
        $context = \context_course::instance($this->course->id);
163
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
164
        $this->add_two_regular_questions($questiongenerator, $quiz, ['contextid' => $context->id]);
165
        $this->add_one_random_question($questiongenerator, $quiz, ['contextid' => $context->id]);
166
        // Count the questions in course context.
167
        $this->assertEquals(7, $this->question_count($context->id));
168
        $newquiz = $this->duplicate_quiz($this->course, $quiz);
169
        $this->assertEquals(7, $this->question_count($context->id));
170
        $context = \context_module::instance($newquiz->id);
171
        // Count the questions in the quiz context.
172
        $this->assertEquals(0, $this->question_count($context->id));
173
    }
174
 
175
    /**
176
     * Test quiz duplicate for quiz question bank.
177
     *
178
     * @covers ::duplicate_module
179
     */
11 efrain 180
    public function test_quiz_duplicate_for_quiz_question_bank_questions(): void {
1 efrain 181
        $this->resetAfterTest();
182
        $quiz = $this->create_test_quiz($this->course);
183
        // Test for questions from a different context.
184
        $context = \context_module::instance($quiz->cmid);
185
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
186
        $this->add_two_regular_questions($questiongenerator, $quiz, ['contextid' => $context->id]);
187
        $this->add_one_random_question($questiongenerator, $quiz, ['contextid' => $context->id]);
188
        // Count the questions in course context.
189
        $this->assertEquals(7, $this->question_count($context->id));
190
        $newquiz = $this->duplicate_quiz($this->course, $quiz);
191
        $this->assertEquals(7, $this->question_count($context->id));
192
        $context = \context_module::instance($newquiz->id);
193
        // Count the questions in the quiz context.
194
        $this->assertEquals(7, $this->question_count($context->id));
195
    }
196
 
197
    /**
198
     * Test quiz restore with attempts.
199
     *
11 efrain 200
     * @covers \mod_quiz\question\bank\qbank_helper::get_question_structure
1 efrain 201
     */
11 efrain 202
    public function test_quiz_restore_with_attempts(): void {
1 efrain 203
        $this->resetAfterTest();
204
 
205
        // Create a quiz.
206
        $quiz = $this->create_test_quiz($this->course);
207
        $quizcontext = \context_module::instance($quiz->cmid);
208
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
209
        $this->add_two_regular_questions($questiongenerator, $quiz, ['contextid' => $quizcontext->id]);
210
        $this->add_one_random_question($questiongenerator, $quiz, ['contextid' => $quizcontext->id]);
211
 
212
        // Attempt it as a student, and check.
213
        /** @var \question_usage_by_activity $quba */
214
        [, $quba] = $this->attempt_quiz($quiz, $this->student);
215
        $this->assertEquals(3, $quba->question_count());
216
        $this->assertCount(1, quiz_get_user_attempts($quiz->id, $this->student->id));
217
 
218
        // Make the backup.
219
        $backupid = $this->backup_quiz($quiz, $this->user);
220
 
221
        // Delete the current course to make sure there is no data.
222
        delete_course($this->course, false);
223
 
224
        // Restore the backup.
225
        $newcourse = $this->getDataGenerator()->create_course();
226
        $this->restore_quiz($backupid, $newcourse, $this->user);
227
 
228
        // Verify.
229
        $modules = get_fast_modinfo($newcourse->id)->get_instances_of('quiz');
230
        $module = reset($modules);
231
        $this->assertCount(1, quiz_get_user_attempts($module->instance, $this->student->id));
232
        $this->assertCount(3, \mod_quiz\question\bank\qbank_helper::get_question_structure(
233
                $module->instance, $module->context));
234
    }
235
 
236
    /**
237
     * Test pre 4.0 quiz restore for regular questions.
238
     *
239
     * Also, for efficiency, tests restore of the review options.
240
     *
11 efrain 241
     * @covers \restore_quiz_activity_structure_step::process_quiz_question_legacy_instance
1 efrain 242
     */
11 efrain 243
    public function test_pre_4_quiz_restore_for_regular_questions(): void {
1 efrain 244
        global $USER, $DB;
245
        $this->resetAfterTest();
246
        $backupid = 'abc';
247
        $backuppath = make_backup_temp_directory($backupid);
248
        get_file_packer('application/vnd.moodle.backup')->extract_to_pathname(
249
            __DIR__ . "/fixtures/moodle_28_quiz.mbz", $backuppath);
250
 
251
        // Do the restore to new course with default settings.
252
        $categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
253
        $newcourseid = \restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid);
254
        $rc = new \restore_controller($backupid, $newcourseid, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $USER->id,
255
            \backup::TARGET_NEW_COURSE);
256
 
257
        $this->assertTrue($rc->execute_precheck());
258
        $rc->execute_plan();
259
        $rc->destroy();
260
 
261
        // Get the information about the resulting course and check that it is set up correctly.
262
        $modinfo = get_fast_modinfo($newcourseid);
263
        $quiz = array_values($modinfo->get_instances_of('quiz'))[0];
264
        $quizobj = \mod_quiz\quiz_settings::create($quiz->instance);
265
        $structure = structure::create_for_quiz($quizobj);
266
 
267
        // Verify the restored review options setting.
268
        $this->assertEquals(display_options::DURING |
269
                    display_options::IMMEDIATELY_AFTER |
270
                    display_options::LATER_WHILE_OPEN |
271
                    display_options::AFTER_CLOSE, $quizobj->get_quiz()->reviewmaxmarks);
272
 
273
        // Are the correct slots returned?
274
        $slots = $structure->get_slots();
275
        $this->assertCount(2, $slots);
276
 
277
        $quizobj->preload_questions();
278
        $quizobj->load_questions();
279
        $questions = $quizobj->get_questions();
280
        $this->assertCount(2, $questions);
281
 
282
        // Count the questions in quiz qbank.
283
        $this->assertEquals(2, $this->question_count($quizobj->get_context()->id));
284
    }
285
 
286
    /**
287
     * Test pre 4.0 quiz restore for random questions.
288
     *
11 efrain 289
     * @covers \restore_quiz_activity_structure_step::process_quiz_question_legacy_instance
1 efrain 290
     */
11 efrain 291
    public function test_pre_4_quiz_restore_for_random_questions(): void {
1 efrain 292
        global $USER, $DB;
293
        $this->resetAfterTest();
294
 
295
        $backupid = 'abc';
296
        $backuppath = make_backup_temp_directory($backupid);
297
        get_file_packer('application/vnd.moodle.backup')->extract_to_pathname(
298
            __DIR__ . "/fixtures/random_by_tag_quiz.mbz", $backuppath);
299
 
300
        // Do the restore to new course with default settings.
301
        $categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
302
        $newcourseid = \restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid);
303
        $rc = new \restore_controller($backupid, $newcourseid, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $USER->id,
304
            \backup::TARGET_NEW_COURSE);
305
 
306
        $this->assertTrue($rc->execute_precheck());
307
        $rc->execute_plan();
308
        $rc->destroy();
309
 
310
        // Get the information about the resulting course and check that it is set up correctly.
311
        $modinfo = get_fast_modinfo($newcourseid);
312
        $quiz = array_values($modinfo->get_instances_of('quiz'))[0];
313
        $quizobj = \mod_quiz\quiz_settings::create($quiz->instance);
314
        $structure = structure::create_for_quiz($quizobj);
315
 
316
        // Are the correct slots returned?
317
        $slots = $structure->get_slots();
318
        $this->assertCount(1, $slots);
319
 
320
        $quizobj->preload_questions();
321
        $quizobj->load_questions();
322
        $questions = $quizobj->get_questions();
323
        $this->assertCount(1, $questions);
324
 
325
        // Count the questions for course question bank.
326
        $this->assertEquals(6, $this->question_count(\context_course::instance($newcourseid)->id));
327
        $this->assertEquals(6, $this->question_count(\context_course::instance($newcourseid)->id,
328
            "AND q.qtype <> 'random'"));
329
 
330
        // Count the questions in quiz qbank.
331
        $this->assertEquals(0, $this->question_count($quizobj->get_context()->id));
332
    }
333
 
334
    /**
335
     * Test pre 4.0 quiz restore for random question tags.
336
     *
11 efrain 337
     * @covers \restore_quiz_activity_structure_step::process_quiz_question_legacy_instance
1 efrain 338
     */
11 efrain 339
    public function test_pre_4_quiz_restore_for_random_question_tags(): void {
1 efrain 340
        global $USER, $DB;
341
        $this->resetAfterTest();
342
        $randomtags = [
343
            '1' => ['first question', 'one', 'number one'],
344
            '2' => ['first question', 'one', 'number one'],
345
            '3' => ['one', 'number one', 'second question'],
346
        ];
347
        $backupid = 'abc';
348
        $backuppath = make_backup_temp_directory($backupid);
349
        get_file_packer('application/vnd.moodle.backup')->extract_to_pathname(
350
            __DIR__ . "/fixtures/moodle_311_quiz.mbz", $backuppath);
351
 
352
        // Do the restore to new course with default settings.
353
        $categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
354
        $newcourseid = \restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid);
355
        $rc = new \restore_controller($backupid, $newcourseid, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $USER->id,
356
            \backup::TARGET_NEW_COURSE);
357
 
358
        $this->assertTrue($rc->execute_precheck());
359
        $rc->execute_plan();
360
        $rc->destroy();
361
 
362
        // Get the information about the resulting course and check that it is set up correctly.
363
        $modinfo = get_fast_modinfo($newcourseid);
364
        $quiz = array_values($modinfo->get_instances_of('quiz'))[0];
365
        $quizobj = \mod_quiz\quiz_settings::create($quiz->instance);
366
        $structure = \mod_quiz\structure::create_for_quiz($quizobj);
367
 
368
        // Count the questions in quiz qbank.
369
        $context = \context_module::instance(get_coursemodule_from_instance("quiz", $quizobj->get_quizid(), $newcourseid)->id);
370
        $this->assertEquals(2, $this->question_count($context->id));
371
 
372
        // Are the correct slots returned?
373
        $slots = $structure->get_slots();
374
        $this->assertCount(3, $slots);
375
 
376
        // Check if the tags match with the actual restored data.
377
        foreach ($slots as $slot) {
378
            $setreference = $DB->get_record('question_set_references',
379
                ['itemid' => $slot->id, 'component' => 'mod_quiz', 'questionarea' => 'slot']);
380
            $filterconditions = json_decode($setreference->filtercondition);
381
            $tags = [];
382
            foreach ($filterconditions->tags as $tagstring) {
383
                $tag = explode(',', $tagstring);
384
                $tags[] = $tag[1];
385
            }
386
            $this->assertEquals([], array_diff($randomtags[$slot->slot], $tags));
387
        }
388
 
389
    }
390
 
391
    /**
392
     * Test pre 4.0 quiz restore for random question used on multiple quizzes.
393
     *
11 efrain 394
     * @covers \restore_quiz_activity_structure_step::process_quiz_question_legacy_instance
1 efrain 395
     */
11 efrain 396
    public function test_pre_4_quiz_restore_shared_random_question(): void {
1 efrain 397
        global $USER, $DB;
398
        $this->resetAfterTest();
399
 
400
        $backupid = 'abc';
401
        $backuppath = make_backup_temp_directory($backupid);
402
        get_file_packer('application/vnd.moodle.backup')->extract_to_pathname(
403
                __DIR__ . "/fixtures/pre-40-shared-random-question.mbz", $backuppath);
404
 
405
        // Do the restore to new course with default settings.
406
        $categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
407
        $newcourseid = \restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid);
408
        $rc = new \restore_controller($backupid, $newcourseid, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $USER->id,
409
                \backup::TARGET_NEW_COURSE);
410
 
411
        $this->assertTrue($rc->execute_precheck());
412
        $rc->execute_plan();
413
        $rc->destroy();
414
 
415
        // Get the information about the resulting course and check that it is set up correctly.
416
        // Each quiz should contain an instance of the random question.
417
        $modinfo = get_fast_modinfo($newcourseid);
418
        $quizzes = $modinfo->get_instances_of('quiz');
419
        $this->assertCount(2, $quizzes);
420
        foreach ($quizzes as $quiz) {
421
            $quizobj = \mod_quiz\quiz_settings::create($quiz->instance);
422
            $structure = structure::create_for_quiz($quizobj);
423
 
424
            // Are the correct slots returned?
425
            $slots = $structure->get_slots();
426
            $this->assertCount(1, $slots);
427
 
428
            $quizobj->preload_questions();
429
            $quizobj->load_questions();
430
            $questions = $quizobj->get_questions();
431
            $this->assertCount(1, $questions);
432
        }
433
 
434
        // Count the questions for course question bank.
435
        // We should have a single question, the random question should have been deleted after the restore.
436
        $this->assertEquals(1, $this->question_count(\context_course::instance($newcourseid)->id));
437
        $this->assertEquals(1, $this->question_count(\context_course::instance($newcourseid)->id,
438
                "AND q.qtype <> 'random'"));
439
 
440
        // Count the questions in quiz qbank.
441
        $this->assertEquals(0, $this->question_count($quizobj->get_context()->id));
442
    }
443
 
444
    /**
445
     * Ensure that question slots are correctly backed up and restored with all properties.
446
     *
447
     * @covers \backup_quiz_activity_structure_step::define_structure()
448
     * @return void
449
     */
450
    public function test_backup_restore_question_slots(): void {
451
        $this->resetAfterTest(true);
452
 
453
        $course1 = $this->getDataGenerator()->create_course();
454
        $course2 = $this->getDataGenerator()->create_course();
455
 
456
        $user1 = $this->getDataGenerator()->create_and_enrol($course1, 'editingteacher');
457
        $this->getDataGenerator()->enrol_user($user1->id, $course2->id, 'editingteacher');
458
 
459
        // Make a quiz.
460
        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
461
 
462
        $quiz = $quizgenerator->create_instance(['course' => $course1->id, 'questionsperpage' => 0, 'grade' => 100.0,
463
                'sumgrades' => 3]);
464
 
465
        // Create some fixed and random questions.
466
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
467
 
468
        $cat = $questiongenerator->create_question_category();
469
        $saq = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
470
        $numq = $questiongenerator->create_question('numerical', null, ['category' => $cat->id]);
471
        $matchq = $questiongenerator->create_question('match', null, ['category' => $cat->id]);
472
        $randomcat = $questiongenerator->create_question_category();
473
        $questiongenerator->create_question('shortanswer', null, ['category' => $randomcat->id]);
474
        $questiongenerator->create_question('numerical', null, ['category' => $randomcat->id]);
475
        $questiongenerator->create_question('match', null, ['category' => $randomcat->id]);
476
 
477
        // Add them to the quiz.
478
        quiz_add_quiz_question($saq->id, $quiz, 1, 3);
479
        quiz_add_quiz_question($numq->id, $quiz, 2, 2);
480
        quiz_add_quiz_question($matchq->id, $quiz, 3, 1);
481
        $this->add_random_questions($quiz->id, 3, $randomcat->id, 2);
482
 
483
        $quizobj = quiz_settings::create($quiz->id, $user1->id);
484
        $originalstructure = \mod_quiz\structure::create_for_quiz($quizobj);
485
 
486
        // Set one slot to a non-default display number.
487
        $originalslots = $originalstructure->get_slots();
488
        $firstslot = reset($originalslots);
489
        $originalstructure->update_slot_display_number($firstslot->id, rand(5, 10));
490
 
491
        // Set one slot to requireprevious.
492
        $lastslot = end($originalslots);
493
        $originalstructure->update_question_dependency($lastslot->id, true);
494
 
495
        // Backup and restore the quiz.
496
        $backupid = $this->backup_quiz($quiz, $user1);
497
        $this->restore_quiz($backupid, $course2, $user1);
498
 
499
        // Ensure the restored slots match the original slots.
500
        $modinfo = get_fast_modinfo($course2);
501
        $quizzes = $modinfo->get_instances_of('quiz');
502
        $restoredquiz = reset($quizzes);
503
        $restoredquizobj = quiz_settings::create($restoredquiz->instance, $user1->id);
504
        $restoredstructure = \mod_quiz\structure::create_for_quiz($restoredquizobj);
505
        $restoredslots = array_values($restoredstructure->get_slots());
506
        $originalstructure = \mod_quiz\structure::create_for_quiz($quizobj);
507
        $originalslots = array_values($originalstructure->get_slots());
508
        foreach ($restoredslots as $key => $restoredslot) {
509
            $originalslot = $originalslots[$key];
510
            $this->assertEquals($originalslot->quizid, $quiz->id);
511
            $this->assertEquals($restoredslot->quizid, $restoredquiz->instance);
512
            $this->assertEquals($originalslot->slot, $restoredslot->slot);
513
            $this->assertEquals($originalslot->page, $restoredslot->page);
514
            $this->assertEquals($originalslot->displaynumber, $restoredslot->displaynumber);
515
            $this->assertEquals($originalslot->requireprevious, $restoredslot->requireprevious);
516
            $this->assertEquals($originalslot->maxmark, $restoredslot->maxmark);
517
        }
518
    }
519
 
520
    /**
521
     * Test pre 4.3 quiz restore for random question filter conditions.
522
     *
523
     * @covers \restore_question_set_reference_data_trait::process_question_set_reference
524
     */
11 efrain 525
    public function test_pre_43_quiz_restore_for_random_question_filtercondition(): void {
1 efrain 526
        global $USER, $DB;
527
        $this->resetAfterTest();
528
        $backupid = 'abc';
529
        $backuppath = make_backup_temp_directory($backupid);
530
        get_file_packer('application/vnd.moodle.backup')->extract_to_pathname(
531
                __DIR__ . "/fixtures/moodle_42_random_question.mbz", $backuppath);
532
 
533
        // Do the restore to new course with default settings.
534
        $categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
535
        $newcourseid = \restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid);
536
        $rc = new \restore_controller($backupid, $newcourseid, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $USER->id,
537
                \backup::TARGET_NEW_COURSE);
538
 
539
        $this->assertTrue($rc->execute_precheck());
540
        $rc->execute_plan();
541
        $rc->destroy();
542
 
543
        // Get the information about the resulting course and check that it is set up correctly.
544
        $modinfo = get_fast_modinfo($newcourseid);
545
        $quiz = array_values($modinfo->get_instances_of('quiz'))[0];
546
        $quizobj = \mod_quiz\quiz_settings::create($quiz->instance);
547
        $structure = \mod_quiz\structure::create_for_quiz($quizobj);
548
 
549
        // Count the questions in quiz qbank.
550
        $context = \context_module::instance(get_coursemodule_from_instance("quiz", $quizobj->get_quizid(), $newcourseid)->id);
551
        $this->assertEquals(2, $this->question_count($context->id));
552
 
553
        // Are the correct slots returned?
554
        $slots = $structure->get_slots();
555
        $this->assertCount(1, $slots);
556
 
557
        // Check that the filtercondition now matches the 4.3 structure.
558
        foreach ($slots as $slot) {
559
            $setreference = $DB->get_record('question_set_references',
560
                    ['itemid' => $slot->id, 'component' => 'mod_quiz', 'questionarea' => 'slot']);
561
            $filterconditions = json_decode($setreference->filtercondition, true);
562
            $this->assertArrayHasKey('cat', $filterconditions);
563
            $this->assertArrayHasKey('jointype', $filterconditions);
564
            $this->assertArrayHasKey('qpage', $filterconditions);
565
            $this->assertArrayHasKey('qperpage', $filterconditions);
566
            $this->assertArrayHasKey('filter', $filterconditions);
567
            $this->assertArrayHasKey('category', $filterconditions['filter']);
568
            $this->assertArrayHasKey('qtagids', $filterconditions['filter']);
569
            $this->assertArrayHasKey('filteroptions', $filterconditions['filter']['category']);
570
            $this->assertArrayHasKey('includesubcategories', $filterconditions['filter']['category']['filteroptions']);
571
 
572
            // MDL-79708: Bad filter conversion check.
573
            $this->assertArrayNotHasKey('includesubcategories', $filterconditions['filter']['category']);
574
 
575
            $this->assertArrayNotHasKey('questioncategoryid', $filterconditions);
576
            $this->assertArrayNotHasKey('tags', $filterconditions);
577
            $expectedtags = \core_tag_tag::get_by_name_bulk(1, ['foo', 'bar']);
578
            $expectedtagids = array_values(array_map(fn($expectedtag) => $expectedtag->id, $expectedtags));
579
            $this->assertEquals($expectedtagids, $filterconditions['filter']['qtagids']['values']);
580
            $expectedcategory = $DB->get_record('question_categories', ['idnumber' => 'RAND']);
581
            $this->assertEquals($expectedcategory->id, $filterconditions['filter']['category']['values'][0]);
582
            $expectedcat = implode(',', [$expectedcategory->id, $expectedcategory->contextid]);
583
            $this->assertEquals($expectedcat, $filterconditions['cat']);
584
 
585
            // MDL-79708: Try to convert already converted filter.
586
            $filterconditionsold = $filterconditions;
587
            $filterconditions = question_reference_manager::convert_legacy_set_reference_filter_condition($filterconditions);
588
            // Check that the filtercondition didn't change.
589
            $this->assertEquals($filterconditionsold, $filterconditions);
590
 
591
            // MDL-79708: Try to convert a filter with previously bad conversion.
592
            $filterconditions['filter']['category']['includesubcategories'] = 0;
593
            unset($filterconditions['filter']['category']['filteroptions']);
594
            $filterconditions = question_reference_manager::convert_legacy_set_reference_filter_condition($filterconditions);
595
            $this->assertEquals($filterconditionsold, $filterconditions);
596
        }
597
    }
598
}