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 core_analytics;
18
 
19
defined('MOODLE_INTERNAL') || die();
20
 
21
require_once(__DIR__ . '/fixtures/test_indicator_max.php');
22
require_once(__DIR__ . '/fixtures/test_indicator_min.php');
23
require_once(__DIR__ . '/fixtures/test_indicator_fullname.php');
24
require_once(__DIR__ . '/fixtures/test_target_course_level_shortname.php');
25
 
26
/**
27
 * Unit tests for the core_analytics manager.
28
 *
29
 * @package   core_analytics
30
 * @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
31
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32
 * @covers    \core_analytics\manager
33
 */
34
class manager_test extends \advanced_testcase {
35
 
36
    /**
37
     * test_deleted_context
38
     */
39
    public function test_deleted_context() {
40
        global $DB;
41
 
42
        $this->resetAfterTest(true);
43
        $this->setAdminuser();
44
        set_config('enabled_stores', 'logstore_standard', 'tool_log');
45
 
46
        $target = \core_analytics\manager::get_target('test_target_course_level_shortname');
47
        $indicators = array('test_indicator_max', 'test_indicator_min', 'test_indicator_fullname');
48
        foreach ($indicators as $key => $indicator) {
49
            $indicators[$key] = \core_analytics\manager::get_indicator($indicator);
50
        }
51
 
52
        $model = \core_analytics\model::create($target, $indicators);
53
        $modelobj = $model->get_model_obj();
54
 
55
        $coursepredict1 = $this->getDataGenerator()->create_course(array('visible' => 0));
56
        $coursepredict2 = $this->getDataGenerator()->create_course(array('visible' => 0));
57
        $coursetrain1 = $this->getDataGenerator()->create_course(array('visible' => 1));
58
        $coursetrain2 = $this->getDataGenerator()->create_course(array('visible' => 1));
59
 
60
        $model->enable('\core\analytics\time_splitting\no_splitting');
61
 
62
        $model->train();
63
        $model->predict();
64
 
65
        // Generate a prediction action to confirm that it is deleted when there is an important update.
66
        $predictions = $DB->get_records('analytics_predictions');
67
        $prediction = reset($predictions);
68
        $prediction = new \core_analytics\prediction($prediction, array('whatever' => 'not used'));
69
        $prediction->action_executed(\core_analytics\prediction::ACTION_USEFUL, $model->get_target());
70
 
71
        $predictioncontextid = $prediction->get_prediction_data()->contextid;
72
 
73
        $npredictions = $DB->count_records('analytics_predictions', array('contextid' => $predictioncontextid));
74
        $npredictionactions = $DB->count_records('analytics_prediction_actions',
75
            array('predictionid' => $prediction->get_prediction_data()->id));
76
        $nindicatorcalc = $DB->count_records('analytics_indicator_calc', array('contextid' => $predictioncontextid));
77
 
78
        \core_analytics\manager::cleanup();
79
 
80
        // Nothing is incorrectly deleted.
81
        $this->assertEquals($npredictions, $DB->count_records('analytics_predictions',
82
            array('contextid' => $predictioncontextid)));
83
        $this->assertEquals($npredictionactions, $DB->count_records('analytics_prediction_actions',
84
            array('predictionid' => $prediction->get_prediction_data()->id)));
85
        $this->assertEquals($nindicatorcalc, $DB->count_records('analytics_indicator_calc',
86
            array('contextid' => $predictioncontextid)));
87
 
88
        // Now we delete a context, the course predictions and prediction actions should be deleted.
89
        $deletedcontext = \context::instance_by_id($predictioncontextid);
90
        delete_course($deletedcontext->instanceid, false);
91
 
92
        \core_analytics\manager::cleanup();
93
 
94
        $this->assertEmpty($DB->count_records('analytics_predictions', array('contextid' => $predictioncontextid)));
95
        $this->assertEmpty($DB->count_records('analytics_prediction_actions',
96
            array('predictionid' => $prediction->get_prediction_data()->id)));
97
        $this->assertEmpty($DB->count_records('analytics_indicator_calc', array('contextid' => $predictioncontextid)));
98
 
99
        set_config('enabled_stores', '', 'tool_log');
100
        get_log_manager(true);
101
    }
102
 
103
    /**
104
     * test_deleted_analysable
105
     */
106
    public function test_deleted_analysable() {
107
        global $DB;
108
 
109
        $this->resetAfterTest(true);
110
        $this->setAdminuser();
111
        set_config('enabled_stores', 'logstore_standard', 'tool_log');
112
 
113
        $target = \core_analytics\manager::get_target('test_target_course_level_shortname');
114
        $indicators = array('test_indicator_max', 'test_indicator_min', 'test_indicator_fullname');
115
        foreach ($indicators as $key => $indicator) {
116
            $indicators[$key] = \core_analytics\manager::get_indicator($indicator);
117
        }
118
 
119
        $model = \core_analytics\model::create($target, $indicators);
120
        $modelobj = $model->get_model_obj();
121
 
122
        $coursepredict1 = $this->getDataGenerator()->create_course(array('visible' => 0));
123
        $coursepredict2 = $this->getDataGenerator()->create_course(array('visible' => 0));
124
        $coursetrain1 = $this->getDataGenerator()->create_course(array('visible' => 1));
125
        $coursetrain2 = $this->getDataGenerator()->create_course(array('visible' => 1));
126
 
127
        $model->enable('\core\analytics\time_splitting\no_splitting');
128
 
129
        $model->train();
130
        $model->predict();
131
 
132
        $this->assertNotEmpty($DB->count_records('analytics_predict_samples'));
133
        $this->assertNotEmpty($DB->count_records('analytics_train_samples'));
134
        $this->assertNotEmpty($DB->count_records('analytics_used_analysables'));
135
 
136
        // Now we delete an analysable, stored predict and training samples should be deleted.
137
        $deletedcontext = \context_course::instance($coursepredict1->id);
138
        delete_course($coursepredict1, false);
139
 
140
        \core_analytics\manager::cleanup();
141
 
142
        $this->assertEmpty($DB->count_records('analytics_predict_samples', array('analysableid' => $coursepredict1->id)));
143
        $this->assertEmpty($DB->count_records('analytics_train_samples', array('analysableid' => $coursepredict1->id)));
144
        $this->assertEmpty($DB->count_records('analytics_used_analysables', array('analysableid' => $coursepredict1->id)));
145
 
146
        set_config('enabled_stores', '', 'tool_log');
147
        get_log_manager(true);
148
    }
149
 
150
    /**
151
     * Tests for the {@link \core_analytics\manager::load_default_models_for_component()} implementation.
152
     */
153
    public function test_load_default_models_for_component() {
154
        $this->resetAfterTest();
155
 
156
        // Attempting to load builtin models should always work without throwing exception.
157
        \core_analytics\manager::load_default_models_for_component('core');
158
 
159
        // Attempting to load from a core subsystem without its own subsystem directory.
160
        $this->assertSame([], \core_analytics\manager::load_default_models_for_component('core_access'));
161
 
162
        // Attempting to load from a non-existing subsystem.
163
        $this->assertSame([], \core_analytics\manager::load_default_models_for_component('core_nonexistingsubsystem'));
164
 
165
        // Attempting to load from a non-existing plugin of a known plugin type.
166
        $this->assertSame([], \core_analytics\manager::load_default_models_for_component('mod_foobarbazquaz12240996776'));
167
 
168
        // Attempting to load from a non-existing plugin type.
169
        $this->assertSame([], \core_analytics\manager::load_default_models_for_component('foo_bar2776327736558'));
170
    }
171
 
172
    /**
173
     * Tests for the {@link \core_analytics\manager::load_default_models_for_all_components()} implementation.
174
     */
175
    public function test_load_default_models_for_all_components() {
176
        $this->resetAfterTest();
177
 
178
        $models = \core_analytics\manager::load_default_models_for_all_components();
179
 
180
        $this->assertTrue(is_array($models['core']));
181
        $this->assertNotEmpty($models['core']);
182
        $this->assertNotEmpty($models['core'][0]['target']);
183
        $this->assertNotEmpty($models['core'][0]['indicators']);
184
    }
185
 
186
    /**
187
     * Tests for the successful execution of the {@link \core_analytics\manager::validate_models_declaration()}.
188
     */
189
    public function test_validate_models_declaration() {
190
        $this->resetAfterTest();
191
 
192
        // This is expected to run without an exception.
193
        $models = $this->load_models_from_fixture_file('no_teaching');
194
        \core_analytics\manager::validate_models_declaration($models);
195
    }
196
 
197
    /**
198
     * Tests for the exceptions thrown by {@link \core_analytics\manager::validate_models_declaration()}.
199
     *
200
     * @dataProvider validate_models_declaration_exceptions_provider
201
     * @param array $models Models declaration.
202
     * @param string $exception Expected coding exception message.
203
     */
204
    public function test_validate_models_declaration_exceptions(array $models, string $exception) {
205
        $this->resetAfterTest();
206
 
207
        $this->expectException(\coding_exception::class);
208
        $this->expectExceptionMessage($exception);
209
        \core_analytics\manager::validate_models_declaration($models);
210
    }
211
 
212
    /**
213
     * Data provider for the {@link self::test_validate_models_declaration_exceptions()}.
214
     *
215
     * @return array of (string)testcase => [(array)models, (string)expected exception message]
216
     */
217
    public function validate_models_declaration_exceptions_provider() {
218
        return [
219
            'missing_target' => [
220
                $this->load_models_from_fixture_file('missing_target'),
221
                'Missing target declaration',
222
            ],
223
            'invalid_target' => [
224
                $this->load_models_from_fixture_file('invalid_target'),
225
                'Invalid target classname',
226
            ],
227
            'missing_indicators' => [
228
                $this->load_models_from_fixture_file('missing_indicators'),
229
                'Missing indicators declaration',
230
            ],
231
            'invalid_indicators' => [
232
                $this->load_models_from_fixture_file('invalid_indicators'),
233
                'Invalid indicator classname',
234
            ],
235
            'invalid_time_splitting' => [
236
                $this->load_models_from_fixture_file('invalid_time_splitting'),
237
                'Invalid time splitting classname',
238
            ],
239
            'invalid_time_splitting_fq' => [
240
                $this->load_models_from_fixture_file('invalid_time_splitting_fq'),
241
                'Expecting fully qualified time splitting classname',
242
            ],
243
            'invalid_enabled' => [
244
                $this->load_models_from_fixture_file('invalid_enabled'),
245
                'Cannot enable a model without time splitting method specified',
246
            ],
247
        ];
248
    }
249
 
250
    /**
251
     * Loads models as declared in the given fixture file.
252
     *
253
     * @param string $filename
254
     * @return array
255
     */
256
    protected function load_models_from_fixture_file(string $filename) {
257
        global $CFG;
258
 
259
        $models = null;
260
 
261
        require($CFG->dirroot.'/analytics/tests/fixtures/db_analytics_php/'.$filename.'.php');
262
 
263
        return $models;
264
    }
265
 
266
    /**
267
     * Test the implementation of the {@link \core_analytics\manager::create_declared_model()}.
268
     */
269
    public function test_create_declared_model() {
270
        global $DB;
271
 
272
        $this->resetAfterTest();
273
        $this->setAdminuser();
274
 
275
        $declaration = [
276
            'target' => 'test_target_course_level_shortname',
277
            'indicators' => [
278
                'test_indicator_max',
279
                'test_indicator_min',
280
                'test_indicator_fullname',
281
            ],
282
        ];
283
 
284
        $declarationwithtimesplitting = array_merge($declaration, [
285
            'timesplitting' => '\core\analytics\time_splitting\no_splitting',
286
        ]);
287
 
288
        $declarationwithtimesplittingenabled = array_merge($declarationwithtimesplitting, [
289
            'enabled' => true,
290
        ]);
291
 
292
        // Check that no such model exists yet.
293
        $target = \core_analytics\manager::get_target('test_target_course_level_shortname');
294
        $this->assertEquals(0, $DB->count_records('analytics_models', ['target' => $target->get_id()]));
295
        $this->assertFalse(\core_analytics\model::exists($target));
296
 
297
        // Check that the model is created.
298
        $created = \core_analytics\manager::create_declared_model($declaration);
299
        $this->assertTrue($created instanceof \core_analytics\model);
300
        $this->assertTrue(\core_analytics\model::exists($target));
301
        $this->assertEquals(1, $DB->count_records('analytics_models', ['target' => $target->get_id()]));
302
        $modelid = $created->get_id();
303
 
304
        // Check that created models are disabled by default.
305
        $existing = new \core_analytics\model($modelid);
306
        $this->assertEquals(0, $existing->get_model_obj()->enabled);
307
        $this->assertEquals(0, $DB->get_field('analytics_models', 'enabled', ['target' => $target->get_id()], MUST_EXIST));
308
 
309
        // Let the admin enable the model.
310
        $existing->enable('\core\analytics\time_splitting\no_splitting');
311
        $this->assertEquals(1, $DB->get_field('analytics_models', 'enabled', ['target' => $target->get_id()], MUST_EXIST));
312
 
313
        // Check that further calls create a new model.
314
        $repeated = \core_analytics\manager::create_declared_model($declaration);
315
        $this->assertTrue($repeated instanceof \core_analytics\model);
316
        $this->assertEquals(2, $DB->count_records('analytics_models', ['target' => $target->get_id()]));
317
 
318
        // Delete the models.
319
        $existing->delete();
320
        $repeated->delete();
321
        $this->assertEquals(0, $DB->count_records('analytics_models', ['target' => $target->get_id()]));
322
        $this->assertFalse(\core_analytics\model::exists($target));
323
 
324
        // Create it again, this time with time splitting method specified.
325
        $created = \core_analytics\manager::create_declared_model($declarationwithtimesplitting);
326
        $this->assertTrue($created instanceof \core_analytics\model);
327
        $this->assertTrue(\core_analytics\model::exists($target));
328
        $this->assertEquals(1, $DB->count_records('analytics_models', ['target' => $target->get_id()]));
329
        $modelid = $created->get_id();
330
 
331
        // Even if the time splitting method was specified, the model is still not enabled automatically.
332
        $existing = new \core_analytics\model($modelid);
333
        $this->assertEquals(0, $existing->get_model_obj()->enabled);
334
        $this->assertEquals(0, $DB->get_field('analytics_models', 'enabled', ['target' => $target->get_id()], MUST_EXIST));
335
        $existing->delete();
336
 
337
        // Let's define the model so that it is enabled by default.
338
        $enabled = \core_analytics\manager::create_declared_model($declarationwithtimesplittingenabled);
339
        $this->assertTrue($enabled instanceof \core_analytics\model);
340
        $this->assertTrue(\core_analytics\model::exists($target));
341
        $this->assertEquals(1, $DB->count_records('analytics_models', ['target' => $target->get_id()]));
342
        $modelid = $enabled->get_id();
343
        $existing = new \core_analytics\model($modelid);
344
        $this->assertEquals(1, $existing->get_model_obj()->enabled);
345
        $this->assertEquals(1, $DB->get_field('analytics_models', 'enabled', ['target' => $target->get_id()], MUST_EXIST));
346
 
347
        // Let the admin disable the model.
348
        $existing->update(0, false, false);
349
        $this->assertEquals(0, $DB->get_field('analytics_models', 'enabled', ['target' => $target->get_id()], MUST_EXIST));
350
    }
351
 
352
    /**
353
     * Test the implementation of the {@link \core_analytics\manager::update_default_models_for_component()}.
354
     */
355
    public function test_update_default_models_for_component() {
356
 
357
        $this->resetAfterTest();
358
        $this->setAdminuser();
359
 
360
        $noteaching = \core_analytics\manager::get_target('\core_course\analytics\target\no_teaching');
361
        $dropout = \core_analytics\manager::get_target('\core_course\analytics\target\course_dropout');
362
        $upcomingactivities = \core_analytics\manager::get_target('\core_user\analytics\target\upcoming_activities_due');
363
        $norecentaccesses = \core_analytics\manager::get_target('\core_course\analytics\target\no_recent_accesses');
364
        $noaccesssincestart = \core_analytics\manager::get_target('\core_course\analytics\target\no_access_since_course_start');
365
 
366
        $this->assertTrue(\core_analytics\model::exists($noteaching));
367
        $this->assertTrue(\core_analytics\model::exists($dropout));
368
        $this->assertTrue(\core_analytics\model::exists($upcomingactivities));
369
        $this->assertTrue(\core_analytics\model::exists($norecentaccesses));
370
        $this->assertTrue(\core_analytics\model::exists($noaccesssincestart));
371
 
372
        foreach (\core_analytics\manager::get_all_models() as $model) {
373
            $model->delete();
374
        }
375
 
376
        $this->assertFalse(\core_analytics\model::exists($noteaching));
377
        $this->assertFalse(\core_analytics\model::exists($dropout));
378
        $this->assertFalse(\core_analytics\model::exists($upcomingactivities));
379
        $this->assertFalse(\core_analytics\model::exists($norecentaccesses));
380
        $this->assertFalse(\core_analytics\model::exists($noaccesssincestart));
381
 
382
        $updated = \core_analytics\manager::update_default_models_for_component('moodle');
383
 
384
        $this->assertEquals(5, count($updated));
385
        $this->assertTrue(array_pop($updated) instanceof \core_analytics\model);
386
        $this->assertTrue(array_pop($updated) instanceof \core_analytics\model);
387
        $this->assertTrue(array_pop($updated) instanceof \core_analytics\model);
388
        $this->assertTrue(array_pop($updated) instanceof \core_analytics\model);
389
        $this->assertTrue(array_pop($updated) instanceof \core_analytics\model);
390
        $this->assertTrue(\core_analytics\model::exists($noteaching));
391
        $this->assertTrue(\core_analytics\model::exists($dropout));
392
        $this->assertTrue(\core_analytics\model::exists($upcomingactivities));
393
        $this->assertTrue(\core_analytics\model::exists($norecentaccesses));
394
        $this->assertTrue(\core_analytics\model::exists($noaccesssincestart));
395
 
396
        $repeated = \core_analytics\manager::update_default_models_for_component('moodle');
397
 
398
        $this->assertSame([], $repeated);
399
    }
400
 
401
    /**
402
     * test_get_time_splitting_methods description
403
     * @return null
404
     */
405
    public function test_get_time_splitting_methods() {
406
        $this->resetAfterTest(true);
407
 
408
        $all = \core_analytics\manager::get_all_time_splittings();
409
        $this->assertArrayHasKey('\core\analytics\time_splitting\upcoming_week', $all);
410
        $this->assertArrayHasKey('\core\analytics\time_splitting\quarters', $all);
411
 
412
        $allforevaluation = \core_analytics\manager::get_time_splitting_methods_for_evaluation(true);
413
        $this->assertArrayNotHasKey('\core\analytics\time_splitting\upcoming_week', $allforevaluation);
414
        $this->assertArrayHasKey('\core\analytics\time_splitting\quarters', $allforevaluation);
415
 
416
        $defaultforevaluation = \core_analytics\manager::get_time_splitting_methods_for_evaluation(false);
417
        $this->assertArrayNotHasKey('\core\analytics\time_splitting\upcoming_week', $defaultforevaluation);
418
        $this->assertArrayHasKey('\core\analytics\time_splitting\quarters', $defaultforevaluation);
419
 
420
        $sometimesplittings = '\core\analytics\time_splitting\single_range,' .
421
            '\core\analytics\time_splitting\tenths';
422
        set_config('defaulttimesplittingsevaluation', $sometimesplittings, 'analytics');
423
 
424
        $defaultforevaluation = \core_analytics\manager::get_time_splitting_methods_for_evaluation(false);
425
        $this->assertArrayNotHasKey('\core\analytics\time_splitting\quarters', $defaultforevaluation);
426
    }
427
 
428
    /**
429
     * Test the implementation of the {@link \core_analytics\manager::model_declaration_identifier()}.
430
     */
431
    public function test_model_declaration_identifier() {
432
 
433
        $noteaching1 = $this->load_models_from_fixture_file('no_teaching');
434
        $noteaching2 = $this->load_models_from_fixture_file('no_teaching');
435
        $noteaching3 = $this->load_models_from_fixture_file('no_teaching');
436
 
437
        // Same model declaration should always lead to same identifier.
438
        $this->assertEquals(
439
            \core_analytics\manager::model_declaration_identifier(reset($noteaching1)),
440
            \core_analytics\manager::model_declaration_identifier(reset($noteaching2))
441
        );
442
 
443
        // If something is changed, the identifier should change, too.
444
        $noteaching2[0]['target'] .= '_';
445
        $this->assertNotEquals(
446
            \core_analytics\manager::model_declaration_identifier(reset($noteaching1)),
447
            \core_analytics\manager::model_declaration_identifier(reset($noteaching2))
448
        );
449
 
450
        $noteaching3[0]['indicators'][] = '\core_analytics\local\indicator\binary';
451
        $this->assertNotEquals(
452
            \core_analytics\manager::model_declaration_identifier(reset($noteaching1)),
453
            \core_analytics\manager::model_declaration_identifier(reset($noteaching3))
454
        );
455
 
456
        // The identifier is supposed to contain PARAM_ALPHANUM only.
457
        $this->assertEquals(
458
            \core_analytics\manager::model_declaration_identifier(reset($noteaching1)),
459
            clean_param(\core_analytics\manager::model_declaration_identifier(reset($noteaching1)), PARAM_ALPHANUM)
460
        );
461
        $this->assertEquals(
462
            \core_analytics\manager::model_declaration_identifier(reset($noteaching2)),
463
            clean_param(\core_analytics\manager::model_declaration_identifier(reset($noteaching2)), PARAM_ALPHANUM)
464
        );
465
        $this->assertEquals(
466
            \core_analytics\manager::model_declaration_identifier(reset($noteaching3)),
467
            clean_param(\core_analytics\manager::model_declaration_identifier(reset($noteaching3)), PARAM_ALPHANUM)
468
        );
469
    }
470
 
471
    /**
472
     * Tests for the {@link \core_analytics\manager::get_declared_target_and_indicators_instances()}.
473
     */
474
    public function test_get_declared_target_and_indicators_instances() {
475
        $this->resetAfterTest();
476
 
477
        $definition = $this->load_models_from_fixture_file('no_teaching');
478
 
479
        list($target, $indicators) = \core_analytics\manager::get_declared_target_and_indicators_instances($definition[0]);
480
 
481
        $this->assertTrue($target instanceof \core_analytics\local\target\base);
482
        $this->assertNotEmpty($indicators);
483
        $this->assertContainsOnlyInstancesOf(\core_analytics\local\indicator\base::class, $indicators);
484
    }
485
 
486
    /**
487
     * test_get_potential_context_restrictions description
488
     */
489
    public function test_get_potential_context_restrictions() {
490
        $this->resetAfterTest();
491
 
492
        // No potential context restrictions.
493
        $this->assertFalse(\core_analytics\manager::get_potential_context_restrictions([]));
494
 
495
        $defaultcategory = \core_course_category::get_default();
496
        $defaultcategorycontext = $defaultcategory->get_context();
497
 
498
        // Include the all context levels so the misc. category get included.
499
        $this->assertEquals([
500
            $defaultcategorycontext->id => "Category: {$defaultcategory->name}",
501
        ], manager::get_potential_context_restrictions());
502
 
503
        $category = $this->getDataGenerator()->create_category(['name' => 'My category']);
504
        $categorycontext = $category->get_context();
505
 
506
        $courseone = $this->getDataGenerator()->create_course(['fullname' => 'Course one', 'shortname' => 'CS1']);
507
        $courseonecontext = \context_course::instance($courseone->id);
508
 
509
        $coursetwo = $this->getDataGenerator()->create_course(['fullname' => 'Course two', 'shortname' => 'CS2']);
510
        $coursetwocontext = \context_course::instance($coursetwo->id);
511
 
512
        // All context levels.
513
        $this->assertEqualsCanonicalizing([
514
            $defaultcategorycontext->id => "Category: {$defaultcategory->name}",
515
            $categorycontext->id => "Category: {$category->name}",
516
            $courseonecontext->id => "Course: {$courseone->shortname}",
517
            $coursetwocontext->id => "Course: {$coursetwo->shortname}",
518
        ], manager::get_potential_context_restrictions());
519
 
520
        // All category/course context levels.
521
        $this->assertEqualsCanonicalizing([
522
            $defaultcategorycontext->id => "Category: {$defaultcategory->name}",
523
            $categorycontext->id => "Category: {$category->name}",
524
            $courseonecontext->id => "Course: {$courseone->shortname}",
525
            $coursetwocontext->id => "Course: {$coursetwo->shortname}",
526
        ], manager::get_potential_context_restrictions([CONTEXT_COURSECAT, CONTEXT_COURSE]));
527
 
528
        // All category context levels.
529
        $this->assertEqualsCanonicalizing([
530
            $defaultcategorycontext->id => "Category: {$defaultcategory->name}",
531
            $categorycontext->id => "Category: {$category->name}",
532
        ], manager::get_potential_context_restrictions([CONTEXT_COURSECAT]));
533
 
534
        // Filtered category context levels.
535
        $this->assertEquals([
536
            $categorycontext->id => "Category: {$category->name}",
537
        ], manager::get_potential_context_restrictions([CONTEXT_COURSECAT], 'My cat'));
538
 
539
        $this->assertEmpty(manager::get_potential_context_restrictions([CONTEXT_COURSECAT], 'nothing'));
540
 
541
        // All course context levels.
542
        $this->assertEqualsCanonicalizing([
543
            $courseonecontext->id => "Course: {$courseone->shortname}",
544
            $coursetwocontext->id => "Course: {$coursetwo->shortname}",
545
        ], manager::get_potential_context_restrictions([CONTEXT_COURSE]));
546
 
547
        // Filtered course context levels.
548
        $this->assertEquals([
549
            $courseonecontext->id => "Course: {$courseone->shortname}",
550
        ], manager::get_potential_context_restrictions([CONTEXT_COURSE], 'one'));
551
 
552
        $this->assertEmpty(manager::get_potential_context_restrictions([CONTEXT_COURSE], 'nothing'));
553
    }
554
}