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