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 310] [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   * Test for extensions manager.
  19   *
  20   * @package    core_contentbank
  21   * @category   test
  22   * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace core_contentbank;
  27  
  28  defined('MOODLE_INTERNAL') || die();
  29  
  30  use advanced_testcase;
  31  use context_course;
  32  use context_coursecat;
  33  use context_system;
  34  
  35  global $CFG;
  36  require_once($CFG->dirroot . '/contentbank/tests/fixtures/testable_contenttype.php');
  37  require_once($CFG->dirroot . '/contentbank/tests/fixtures/testable_content.php');
  38  
  39  /**
  40   * Test for extensions manager.
  41   *
  42   * @package    core_contentbank
  43   * @category   test
  44   * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
  45   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  46   * @coversDefaultClass \core_contentbank\contentbank
  47   */
  48  class core_contentbank_testcase extends advanced_testcase {
  49  
  50      /**
  51       * Setup to ensure that fixtures are loaded.
  52       */
  53      public static function setupBeforeClass(): void {
  54          global $CFG;
  55  
  56          require_once($CFG->dirroot . '/contentbank/tests/fixtures/testable_contenttype.php');
  57      }
  58  
  59      /**
  60       * Data provider for test_get_extension_supporter.
  61       *
  62       * @return  array
  63       */
  64      public function get_extension_provider() {
  65          return [
  66              'H5P file' => ['something.h5p', '.h5p'],
  67              'PDF file' => ['something.pdf', '.pdf']
  68          ];
  69      }
  70  
  71      /**
  72       * Tests for get_extension() function.
  73       *
  74       * @dataProvider    get_extension_provider
  75       * @param   string  $filename    The filename given
  76       * @param   string   $expected   The extension of the file
  77       *
  78       * @covers ::get_extension
  79       */
  80      public function test_get_extension(string $filename, string $expected) {
  81          $this->resetAfterTest();
  82  
  83          $cb = new contentbank();
  84  
  85          $extension = $cb->get_extension($filename);
  86          $this->assertEquals($expected, $extension);
  87      }
  88  
  89      /**
  90       * Data provider for test_load_context_supported_extensions.
  91       *
  92       * @return  array
  93       */
  94      public function get_extension_supporters_provider() {
  95          return [
  96              'H5P first' => [['.h5p' => ['h5p', 'testable']], '.h5p', 'h5p'],
  97              'Testable first (but upload not implemented)' => [['.h5p' => ['testable', 'h5p']], '.h5p', 'h5p'],
  98          ];
  99      }
 100  
 101      /**
 102       * Tests for get_extension_supporter() function with admin permissions.
 103       *
 104       * @dataProvider    get_extension_supporters_provider
 105       * @param   array   $supporters   The content type plugin supporters for each extension
 106       * @param   string  $extension    The extension of the file given
 107       * @param   string  $expected   The supporter contenttype of the file
 108       *
 109       * @covers ::load_context_supported_extensions
 110       */
 111      public function test_get_extension_supporter_for_admins(array $supporters, string $extension, string $expected) {
 112          $this->resetAfterTest();
 113  
 114          $cb = new contentbank();
 115  
 116          $systemcontext = context_system::instance();
 117  
 118          // All contexts allowed for admins.
 119          $this->setAdminUser();
 120          $contextsupporters = $cb->load_context_supported_extensions($systemcontext);
 121          $this->assertArrayHasKey($extension, $contextsupporters);
 122          $this->assertEquals($expected, $contextsupporters[$extension]);
 123      }
 124  
 125      /**
 126       * Tests for get_extension_supporter() function with user default permissions.
 127       *
 128       * @dataProvider    get_extension_supporters_provider
 129       * @param   array   $supporters   The content type plugin supporters for each extension
 130       * @param   string  $extension    The extension of the file given
 131       * @param   string  $expected   The supporter contenttype of the file
 132       *
 133       * @covers ::load_context_supported_extensions
 134       */
 135      public function test_get_extension_supporter_for_users(array $supporters, string $extension, string $expected) {
 136          $this->resetAfterTest();
 137  
 138          $cb = new contentbank();
 139          $systemcontext = context_system::instance();
 140  
 141          // Set a user with no permissions.
 142          $user = $this->getDataGenerator()->create_user();
 143          $this->setUser($user);
 144  
 145          // Users with no capabilities can't upload content.
 146          $contextsupporters = $cb->load_context_supported_extensions($systemcontext);
 147          $this->assertEquals([], $contextsupporters);
 148      }
 149  
 150      /**
 151       * Tests for get_extension_supporter() function with teacher defaul permissions.
 152       *
 153       * @dataProvider    get_extension_supporters_provider
 154       * @param   array   $supporters   The content type plugin supporters for each extension
 155       * @param   string  $extension    The extension of the file given
 156       * @param   string  $expected   The supporter contenttype of the file
 157       *
 158       * @covers ::load_context_supported_extensions
 159       */
 160      public function test_get_extension_supporter_for_teachers(array $supporters, string $extension, string $expected) {
 161          $this->resetAfterTest();
 162  
 163          $cb = new contentbank();
 164  
 165          $course = $this->getDataGenerator()->create_course();
 166          $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
 167          $this->setUser($teacher);
 168          $coursecontext = context_course::instance($course->id);
 169  
 170          // Teachers has permission in their context to upload supported by H5P content type.
 171          $contextsupporters = $cb->load_context_supported_extensions($coursecontext);
 172          $this->assertArrayHasKey($extension, $contextsupporters);
 173          $this->assertEquals($expected, $contextsupporters[$extension]);
 174      }
 175  
 176      /**
 177       * Tests for get_extension_supporter() function.
 178       *
 179       * @dataProvider    get_extension_supporters_provider
 180       * @param   array   $supporters   The content type plugin supporters for each extension
 181       * @param   string  $extension    The extension of the file given
 182       * @param   string  $expected   The supporter contenttype of the file
 183       *
 184       * @covers ::get_extension_supporter
 185       */
 186      public function test_get_extension_supporter(array $supporters, string $extension, string $expected) {
 187          $this->resetAfterTest();
 188  
 189          $cb = new contentbank();
 190          $systemcontext = context_system::instance();
 191          $this->setAdminUser();
 192  
 193          $supporter = $cb->get_extension_supporter($extension, $systemcontext);
 194          $this->assertEquals($expected, $supporter);
 195      }
 196  
 197      /**
 198       * Test the behaviour of search_contents().
 199       *
 200       * @dataProvider search_contents_provider
 201       * @param  string $search String to search.
 202       * @param  string $where Context where to search.
 203       * @param  int $expectedresult Expected result.
 204       * @param  array $contexts List of contexts where to create content.
 205       */
 206      public function test_search_contents(?string $search, string $where, int $expectedresult, array $contexts = [],
 207              array $contenttypes = null): void {
 208          global $DB;
 209  
 210          $this->resetAfterTest();
 211  
 212          // Create users.
 213          $managerroleid = $DB->get_field('role', 'id', ['shortname' => 'manager']);
 214          $manager = $this->getDataGenerator()->create_user();
 215          $this->getDataGenerator()->role_assign($managerroleid, $manager->id);
 216  
 217          // Create a category and a course.
 218          $coursecat = $this->getDataGenerator()->create_category();
 219          $course = $this->getDataGenerator()->create_course();
 220          $existingcontexts = [];
 221          $existingcontexts['system'] = \context_system::instance();
 222          $existingcontexts['category'] = \context_coursecat::instance($coursecat->id);
 223          $existingcontexts['course'] = \context_course::instance($course->id);
 224  
 225          if (empty($where)) {
 226              $contextid = 0;
 227          } else {
 228              $contextid = $existingcontexts[$where]->id;
 229          }
 230  
 231          // Add some content to the content bank.
 232          $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank');
 233          foreach ($contexts as $context) {
 234              $contextinstance = $existingcontexts[$context];
 235              $records = $generator->generate_contentbank_data('contenttype_h5p', 3,
 236                  $manager->id, $contextinstance, false);
 237          }
 238  
 239          // Search for some content.
 240          $cb = new contentbank();
 241          $contents = $cb->search_contents($search, $contextid, $contenttypes);
 242  
 243          $this->assertCount($expectedresult, $contents);
 244          if (!empty($contents) && !empty($search)) {
 245              foreach ($contents as $content) {
 246                  $this->assertContains($search, $content->get_name());
 247              }
 248          }
 249      }
 250  
 251      /**
 252       * Data provider for test_search_contents().
 253       *
 254       * @return array
 255       */
 256      public function search_contents_provider(): array {
 257  
 258          return [
 259              'Search all content in all contexts' => [
 260                  null,
 261                  '',
 262                  9,
 263                  ['system', 'category', 'course']
 264              ],
 265              'Search in all contexts for existing string in all contents' => [
 266                  'content',
 267                  '',
 268                  9,
 269                  ['system', 'category', 'course']
 270              ],
 271              'Search in all contexts for unexisting string in all contents' => [
 272                  'chocolate',
 273                  '',
 274                  0,
 275                  ['system', 'category', 'course']
 276              ],
 277              'Search in all contexts for existing string in some contents' => [
 278                  '1',
 279                  '',
 280                  3,
 281                  ['system', 'category', 'course']
 282              ],
 283              'Search in all contexts for existing string in some contents (create only 1 context)' => [
 284                  '1',
 285                  '',
 286                  1,
 287                  ['system']
 288              ],
 289              'Search in system context for existing string in all contents' => [
 290                  'content',
 291                  'system',
 292                  3,
 293                  ['system', 'category', 'course']
 294              ],
 295              'Search in category context for unexisting string in all contents' => [
 296                  'chocolate',
 297                  'category',
 298                  0,
 299                  ['system', 'category', 'course']
 300              ],
 301              'Search in course context for existing string in some contents' => [
 302                  '1',
 303                  'course',
 304                  1,
 305                  ['system', 'category', 'course']
 306              ],
 307              'Search in system context' => [
 308                  null,
 309                  'system',
 310                  3,
 311                  ['system', 'category', 'course']
 312              ],
 313              'Search in course context with existing content' => [
 314                  null,
 315                  'course',
 316                  3,
 317                  ['system', 'category', 'course']
 318              ],
 319              'Search in course context without existing content' => [
 320                  null,
 321                  'course',
 322                  0,
 323                  ['system', 'category']
 324              ],
 325              'Search in an empty contentbank' => [
 326                  null,
 327                  '',
 328                  0,
 329                  []
 330              ],
 331              'Search in a context in an empty contentbank' => [
 332                  null,
 333                  'system',
 334                  0,
 335                  []
 336              ],
 337              'Search for a string in an empty contentbank' => [
 338                  'content',
 339                  '',
 340                  0,
 341                  []
 342              ],
 343              'Search with unexisting content-type' => [
 344                  null,
 345                  'course',
 346                  0,
 347                  ['system', 'category', 'course'],
 348                  ['contenttype_unexisting'],
 349              ],
 350          ];
 351      }
 352  
 353      /**
 354       * Test create_content_from_file function.
 355       *
 356       * @covers ::create_content_from_file
 357       */
 358      public function test_create_content_from_file() {
 359          global $USER;
 360  
 361          $this->resetAfterTest();
 362          $this->setAdminUser();
 363          $systemcontext = \context_system::instance();
 364          $name = 'dummy_h5p.h5p';
 365  
 366          // Create a dummy H5P file.
 367          $dummyh5p = array(
 368              'contextid' => $systemcontext->id,
 369              'component' => 'contentbank',
 370              'filearea' => 'public',
 371              'itemid' => 1,
 372              'filepath' => '/',
 373              'filename' => $name,
 374              'userid' => $USER->id
 375          );
 376          $fs = get_file_storage();
 377          $dummyh5pfile = $fs->create_file_from_string($dummyh5p, 'Dummy H5Pcontent');
 378  
 379          $cb = new contentbank();
 380          $content = $cb->create_content_from_file($systemcontext, $USER->id, $dummyh5pfile);
 381  
 382          $this->assertEquals('contenttype_h5p', $content->get_content_type());
 383          $this->assertInstanceOf('\\contenttype_h5p\\content', $content);
 384          $this->assertEquals($name, $content->get_name());
 385      }
 386  
 387      /**
 388       * Test the behaviour of delete_contents().
 389       *
 390       * @covers  ::delete_contents
 391       */
 392      public function test_delete_contents() {
 393          global $DB;
 394  
 395          $this->resetAfterTest();
 396          $cb = new \core_contentbank\contentbank();
 397  
 398          // Create a category and two courses.
 399          $systemcontext = context_system::instance();
 400          $coursecat = $this->getDataGenerator()->create_category();
 401          $coursecatcontext = context_coursecat::instance($coursecat->id);
 402          $course1 = $this->getDataGenerator()->create_course();
 403          $course1context = context_course::instance($course1->id);
 404          $course2 = $this->getDataGenerator()->create_course();
 405          $course2context = context_course::instance($course2->id);
 406  
 407          // Add some content to the content bank.
 408          $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank');
 409          $systemcontent = $generator->generate_contentbank_data(null, 3, 0, $systemcontext);
 410          $categorycontent = $generator->generate_contentbank_data(null, 3, 0, $coursecatcontext);
 411          $course1content = $generator->generate_contentbank_data(null, 3, 0, $course1context);
 412          $course2content = $generator->generate_contentbank_data(null, 3, 0, $course2context);
 413  
 414          // Check the content has been created as expected.
 415          $this->assertEquals(12, $DB->count_records('contentbank_content'));
 416  
 417          // Check the system content is deleted as expected and the rest of the content is not.
 418          $this->assertTrue($cb->delete_contents($systemcontext));
 419          $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $systemcontext->id]));
 420          // And the rest of the context content exists.
 421          $this->assertEquals(9, $DB->count_records('contentbank_content'));
 422  
 423          // Check the course category content is deleted as expected and the rest of the content is not.
 424          $this->assertTrue($cb->delete_contents($coursecatcontext));
 425          $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $coursecatcontext->id]));
 426          // And the rest of the context content exists.
 427          $this->assertEquals(6, $DB->count_records('contentbank_content'));
 428  
 429          // Check the course content is deleted as expected and the rest of the content is not.
 430          $this->assertTrue($cb->delete_contents($course1context));
 431          $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $course1context->id]));
 432          // And the rest of the context content exists.
 433          $this->assertEquals(3, $DB->count_records('contentbank_content'));
 434      }
 435  
 436      /**
 437       * Test the behaviour of delete_contents() for empty content bank.
 438       *
 439       * @covers  ::delete_contents
 440       */
 441      public function test_delete_contents_for_empty_contentbank() {
 442  
 443          $this->resetAfterTest();
 444          $cb = new \core_contentbank\contentbank();
 445  
 446          // Create a category and two courses.
 447          $systemcontext = \context_system::instance();
 448          $coursecat = $this->getDataGenerator()->create_category();
 449          $coursecatcontext = \context_coursecat::instance($coursecat->id);
 450          $course = $this->getDataGenerator()->create_course();
 451          $coursecontext = \context_course::instance($course->id);
 452  
 453          // Check there's no error when trying to delete content from an empty content bank.
 454          $this->assertTrue($cb->delete_contents($systemcontext));
 455          $this->assertTrue($cb->delete_contents($coursecatcontext));
 456          $this->assertTrue($cb->delete_contents($coursecontext));
 457      }
 458  
 459      /**
 460       * Test the behaviour of move_contents().
 461       *
 462       * @covers  ::move_contents
 463       */
 464      public function test_move_contents() {
 465          global $DB;
 466  
 467          $this->resetAfterTest();
 468          $cb = new \core_contentbank\contentbank();
 469  
 470          // Create a category and two courses.
 471          $course1 = $this->getDataGenerator()->create_course();
 472          $course1context = context_course::instance($course1->id);
 473          $course2 = $this->getDataGenerator()->create_course();
 474          $course2context = context_course::instance($course2->id);
 475  
 476          // Add some content to the content bank.
 477          $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank');
 478          $course1content = $generator->generate_contentbank_data(null, 3, 0, $course1context);
 479          $course2content = $generator->generate_contentbank_data(null, 3, 0, $course2context);
 480  
 481          // Check the content has been created as expected.
 482          $this->assertEquals(6, $DB->count_records('contentbank_content'));
 483          $this->assertEquals(3, $DB->count_records('contentbank_content', ['contextid' => $course1context->id]));
 484  
 485          // Check the content is moved to another context as expected and the rest of the content is not.
 486          $this->assertTrue($cb->move_contents($course1context, $course2context));
 487          $this->assertEquals(6, $DB->count_records('contentbank_content'));
 488          $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $course1context->id]));
 489          $this->assertEquals(6, $DB->count_records('contentbank_content', ['contextid' => $course2context->id]));
 490      }
 491  
 492      /**
 493       * Test the behaviour of move_contents() for empty content bank.
 494       *
 495       * @covers  ::move_contents
 496       */
 497      public function test_move_contents_for_empty_contentbank() {
 498  
 499          $this->resetAfterTest();
 500          $cb = new \core_contentbank\contentbank();
 501  
 502          // Create a category and two courses.
 503          $systemcontext = \context_system::instance();
 504          $course = $this->getDataGenerator()->create_course();
 505          $coursecontext = \context_course::instance($course->id);
 506  
 507          // Check there's no error when trying to move content context from an empty content bank.
 508          $this->assertTrue($cb->delete_contents($systemcontext, $coursecontext));
 509      }
 510  
 511      /**
 512       * Data provider for get_contenttypes_with_capability_feature.
 513       *
 514       * @return  array
 515       */
 516      public function get_contenttypes_with_capability_feature_provider(): array {
 517          return [
 518              'no-contenttypes_enabled' => [
 519                  'contenttypesenabled' => [],
 520                  'contenttypescanfeature' => [],
 521              ],
 522              'contenttype_enabled_noeditable' => [
 523                  'contenttypesenabled' => ['testable'],
 524                  'contenttypescanfeature' => [],
 525              ],
 526              'contenttype_enabled_editable' => [
 527                  'contenttypesenabled' => ['testable'],
 528                  'contenttypescanfeature' => ['testable'],
 529              ],
 530              'no-contenttype_enabled_editable' => [
 531                  'contenttypesenabled' => [],
 532                  'contenttypescanfeature' => ['testable'],
 533              ],
 534          ];
 535      }
 536  
 537      /**
 538       * Tests for get_contenttypes_with_capability_feature() function.
 539       *
 540       * @dataProvider    get_contenttypes_with_capability_feature_provider
 541       * @param   array $contenttypesenabled Content types enabled.
 542       * @param   array $contenttypescanfeature Content types the user has the permission to use the feature.
 543       *
 544       * @covers ::get_contenttypes_with_capability_feature
 545       */
 546      public function test_get_contenttypes_with_capability_feature(array $contenttypesenabled, array $contenttypescanfeature): void {
 547          $this->resetAfterTest();
 548  
 549          $cb = new contentbank();
 550  
 551          $plugins = [];
 552  
 553          // Content types not enabled where the user has permission to use a feature.
 554          if (empty($contenttypesenabled) && !empty($contenttypescanfeature)) {
 555              $enabled = false;
 556  
 557              // Mock core_plugin_manager class and the method get_plugins_of_type.
 558              $pluginmanager = $this->getMockBuilder(\core_plugin_manager::class)
 559                  ->disableOriginalConstructor()
 560                  ->setMethods(['get_plugins_of_type'])
 561                  ->getMock();
 562  
 563              // Replace protected singletoninstance reference (core_plugin_manager property) with mock object.
 564              $ref = new \ReflectionProperty(\core_plugin_manager::class, 'singletoninstance');
 565              $ref->setAccessible(true);
 566              $ref->setValue(null, $pluginmanager);
 567  
 568              // Return values of get_plugins_of_type method.
 569              foreach ($contenttypescanfeature as $contenttypepluginname) {
 570                  $contenttypeplugin = new \stdClass();
 571                  $contenttypeplugin->name = $contenttypepluginname;
 572                  $contenttypeplugin->type = 'contenttype';
 573                  // Add the feature to the fake content type.
 574                  $classname = "\\contenttype_$contenttypepluginname\\contenttype";
 575                  $classname::$featurestotest = ['test2'];
 576                  $plugins[] = $contenttypeplugin;
 577              }
 578  
 579              // Set expectations and return values.
 580              $pluginmanager->expects($this->once())
 581                  ->method('get_plugins_of_type')
 582                  ->with('contenttype')
 583                  ->willReturn($plugins);
 584          } else {
 585              $enabled = true;
 586              // Get access to private property enabledcontenttypes.
 587              $rc = new \ReflectionClass(\core_contentbank\contentbank::class);
 588              $rcp = $rc->getProperty('enabledcontenttypes');
 589              $rcp->setAccessible(true);
 590  
 591              foreach ($contenttypesenabled as $contenttypename) {
 592                  $plugins["\\contenttype_$contenttypename\\contenttype"] = $contenttypename;
 593                  // Add to the testable contenttype the feature to test.
 594                  if (in_array($contenttypename, $contenttypescanfeature)) {
 595                      $classname = "\\contenttype_$contenttypename\\contenttype";
 596                      $classname::$featurestotest = ['test2'];
 597                  }
 598              }
 599              // Set as enabled content types only those in the test.
 600              $rcp->setValue($cb, $plugins);
 601          }
 602  
 603          $actual = $cb->get_contenttypes_with_capability_feature('test2', null, $enabled);
 604          $this->assertEquals($contenttypescanfeature, array_values($actual));
 605      }
 606  
 607      /**
 608       * Test the behaviour of is_context_allowed().
 609       *
 610       * @dataProvider context_provider
 611       * @param  \Closure $getcontext Get the context to check.
 612       * @param  bool $expectedresult Expected result.
 613       *
 614       * @covers ::is_context_allowed
 615       */
 616      public function test_is_context_allowed(\Closure $getcontext, bool $expectedresult): void {
 617          $this->resetAfterTest();
 618  
 619          $cb = new contentbank();
 620          $context = $getcontext();
 621          $this->assertEquals($expectedresult, $cb->is_context_allowed($context));
 622      }
 623  
 624      /**
 625       * Data provider for test_is_context_allowed().
 626       *
 627       * @return array
 628       */
 629      public function context_provider(): array {
 630  
 631          return [
 632              'System context' => [
 633                  function (): \context {
 634                      return \context_system::instance();
 635                  },
 636                  true,
 637              ],
 638              'User context' => [
 639                  function (): \context {
 640                      $user = $this->getDataGenerator()->create_user();
 641                      return \context_user::instance($user->id);
 642                  },
 643                  false,
 644              ],
 645              'Course category context' => [
 646                  function (): \context {
 647                      $coursecat = $this->getDataGenerator()->create_category();
 648                      return \context_coursecat::instance($coursecat->id);
 649                  },
 650                  true,
 651              ],
 652              'Course context' => [
 653                  function (): \context {
 654                      $course = $this->getDataGenerator()->create_course();
 655                      return \context_course::instance($course->id);
 656                  },
 657                  true,
 658              ],
 659              'Module context' => [
 660                  function (): \context {
 661                      $course = $this->getDataGenerator()->create_course();
 662                      $module = $this->getDataGenerator()->create_module('page', ['course' => $course->id]);
 663                      return \context_module::instance($module->cmid);
 664                  },
 665                  false,
 666              ],
 667              'Block context' => [
 668                  function (): \context {
 669                      $course = $this->getDataGenerator()->create_course();
 670                      $coursecontext = context_course::instance($course->id);
 671                      $block = $this->getDataGenerator()->create_block('online_users', ['parentcontextid' => $coursecontext->id]);
 672                      return \context_block::instance($block->id);
 673                  },
 674                  false,
 675              ],
 676          ];
 677      }
 678  }