Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

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