Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

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