Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 400 and 401] [Versions 401 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   * Tests for the Big Blue Button Instance.
  18   *
  19   * @package   mod_bigbluebuttonbn
  20   * @copyright 2021 Andrew Lyons <andrew@nicols.co.uk>
  21   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22   */
  23  
  24  namespace mod_bigbluebuttonbn;
  25  
  26  use advanced_testcase;
  27  use moodle_exception;
  28  
  29  /**
  30   * Tests for the Big Blue Button Instance.
  31   *
  32   * @package   mod_bigbluebuttonbn
  33   * @copyright 2021 Andrew Lyons <andrew@nicols.co.uk>
  34   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   * @coversDefaultClass \mod_bigbluebuttonbn\instance
  36   */
  37  class instance_test extends advanced_testcase {
  38  
  39      /**
  40       * Test get from
  41       *
  42       * @param string $function
  43       * @param string $field
  44       * @dataProvider get_from_location_provider
  45       * @covers ::get_from_instanceid
  46       * @covers ::get_from_cmid
  47       */
  48      public function test_get_from(string $function, string $field): void {
  49          $this->resetAfterTest();
  50  
  51          [
  52              'record' => $record,
  53          ] = $this->get_test_instance();
  54  
  55          $instance = call_user_func("mod_bigbluebuttonbn\instance::{$function}", $record->{$field});
  56  
  57          $this->assertInstanceOf(instance::class, $instance);
  58          $this->assertEquals($record->id, $instance->get_instance_id());
  59          $this->assertEquals($record->cmid, $instance->get_cm_id());
  60          $this->assertEquals($record->cmid, $instance->get_cm()->id);
  61      }
  62  
  63      /**
  64       * Get from location provider
  65       *
  66       * @return string[][]
  67       */
  68      public function get_from_location_provider(): array {
  69          return [
  70              ['get_from_instanceid', 'id'],
  71              ['get_from_cmid', 'cmid'],
  72          ];
  73      }
  74  
  75      /**
  76       * Get an instance from a cmid.
  77       * @covers ::get_from_cmid
  78       */
  79      public function test_get_from_cmid(): void {
  80          $this->resetAfterTest();
  81  
  82          [
  83              'record' => $record,
  84              'cm' => $cm,
  85          ] = $this->get_test_instance();
  86  
  87          $instance = instance::get_from_cmid($cm->id);
  88  
  89          $this->assertInstanceOf(instance::class, $instance);
  90          $this->assertEquals($record->id, $instance->get_instance_id());
  91          $this->assertEquals($cm->id, $instance->get_cm()->id);
  92      }
  93  
  94      /**
  95       * If the instance was not found, and exception should be thrown.
  96       * @covers ::get_from_cmid
  97       */
  98      public function test_get_from_cmid_not_found(): void {
  99          $this->assertNull(instance::get_from_cmid(100));
 100      }
 101  
 102      /**
 103       * If the instance was not found, and exception should be thrown.
 104       * @covers ::get_from_instanceid
 105       */
 106      public function test_get_from_instance_not_found(): void {
 107          $this->assertNull(instance::get_from_instanceid(100));
 108      }
 109  
 110      /**
 111       * Get from meeting id
 112       *
 113       * @covers ::get_from_meetingid
 114       */
 115      public function test_get_from_meetingid(): void {
 116          $this->resetAfterTest();
 117  
 118          [
 119              'record' => $record,
 120          ] = $this->get_test_instance();
 121  
 122          // The meetingid is confusingly made up of a meetingid field, courseid, instanceid, and groupid.
 123          $instance = instance::get_from_meetingid(sprintf(
 124              "%s-%s-%s",
 125              $record->meetingid,
 126              $record->course,
 127              $record->id
 128          ));
 129  
 130          $this->assertInstanceOf(instance::class, $instance);
 131          $this->assertEquals($record->id, $instance->get_instance_id());
 132          $this->assertEquals($record->cmid, $instance->get_cm_id());
 133          $this->assertEquals($record->cmid, $instance->get_cm()->id);
 134      }
 135  
 136      /**
 137       * Get the get_from_meetingid() function where the meetingid includes a groupid.
 138       *
 139       * @covers ::get_from_meetingid
 140       */
 141      public function test_get_from_meetingid_group(): void {
 142          $this->resetAfterTest();
 143  
 144          [
 145              'record' => $record,
 146              'course' => $course,
 147              'cm' => $cm,
 148          ] = $this->get_test_instance();
 149  
 150          $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
 151  
 152          $instance = instance::get_from_meetingid(
 153              sprintf("%s-%s-%s[0]", $record->meetingid, $record->course, $record->id)
 154          );
 155  
 156          $this->assertEquals($cm->instance, $instance->get_instance_id());
 157          $this->assertEquals($cm->id, $instance->get_cm_id());
 158      }
 159  
 160      /**
 161       * Ensure that invalid meetingids throw an appropriate exception.
 162       *
 163       * @dataProvider invalid_meetingid_provider
 164       * @param string $meetingid
 165       * @covers ::get_from_meetingid
 166       */
 167      public function test_get_from_meetingid_invalid(string $meetingid): void {
 168          $this->expectException(moodle_exception::class);
 169          instance::get_from_meetingid($meetingid);
 170      }
 171  
 172      /**
 173       * Provide invalid meeting examples
 174       *
 175       * @return \string[][]
 176       */
 177      public function invalid_meetingid_provider(): array {
 178          // Meeting IDs are in the formats:
 179          // - <meetingid[string]>-<courseid[number]>-<instanceid[number]>
 180          // - <meetingid[string]>-<courseid[number]>-<instanceid[number]>[<groupid[number]>]
 181          // Note: deducing the group from meeting id will soon be deprecated.
 182          return [
 183              'Non-numeric instanceid' => ['aaa-123-aaa'],
 184          ];
 185      }
 186  
 187      /**
 188       * Test the get_all_instances_in_course function.
 189       *
 190       * @covers ::get_all_instances_in_course
 191       */
 192      public function test_get_all_instances_in_course(): void {
 193          $this->resetAfterTest();
 194  
 195          $course = $this->getDataGenerator()->create_course();
 196          $records = [];
 197          for ($i = 0; $i < 5; $i++) {
 198              $this->getDataGenerator()->create_module('bigbluebuttonbn', [
 199                  'course' => $course->id,
 200              ]);
 201          }
 202  
 203          $instances = instance::get_all_instances_in_course($course->id);
 204          $this->assertCount(5, $instances);
 205          foreach ($instances as $instance) {
 206              $this->assertInstanceOf(instance::class, $instance);
 207          }
 208      }
 209  
 210      /**
 211       * Get test instance from data
 212       *
 213       * @param array $data
 214       * @return array
 215       */
 216      protected function get_test_instance(array $data = []): array {
 217          $course = $this->getDataGenerator()->create_course();
 218          $record = $this->getDataGenerator()->create_module('bigbluebuttonbn', array_merge([
 219              'course' => $course->id,
 220          ], $data));
 221          $cm = get_fast_modinfo($course)->instances['bigbluebuttonbn'][$record->id];
 222  
 223          return [
 224              'course' => $course,
 225              'record' => $record,
 226              'cm' => $cm,
 227          ];
 228      }
 229  
 230      /**
 231       * Test the get_meeting_id function for a meeting configured for a group.
 232       *
 233       * @covers ::get_meeting_id
 234       */
 235      public function test_get_meeting_id_with_groups(): void {
 236          $this->resetAfterTest();
 237  
 238          [
 239              'record' => $record,
 240              'course' => $course,
 241          ] = $this->get_test_instance();
 242  
 243          $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
 244  
 245          $instance = instance::get_from_instanceid($record->id);
 246  
 247          // No group.
 248          $this->assertEquals(
 249              sprintf("%s-%s-%s[0]", $record->meetingid, $record->course, $record->id),
 250              $instance->get_meeting_id(0)
 251          );
 252  
 253          // Specified group.
 254          $this->assertEquals(
 255              sprintf("%s-%s-%s[%d]", $record->meetingid, $record->course, $record->id, $group->id),
 256              $instance->get_meeting_id($group->id)
 257          );
 258      }
 259  
 260      /**
 261       * Test the get_meeting_id function for a meeting configured for a group.
 262       *
 263       * @covers ::get_meeting_id
 264       */
 265      public function test_get_meeting_id_without_groups(): void {
 266          $this->resetAfterTest();
 267  
 268          [
 269              'record' => $record,
 270              'course' => $course,
 271          ] = $this->get_test_instance();
 272  
 273          $instance = instance::get_from_instanceid($record->id);
 274  
 275          // No group.
 276          $this->assertEquals(
 277              sprintf("%s-%s-%s[0]", $record->meetingid, $record->course, $record->id),
 278              $instance->get_meeting_id(null)
 279          );
 280      }
 281  
 282      /**
 283       * Data provider to check the various room_available scenarios'
 284       *
 285       * @return array
 286       */
 287      public function is_currently_open_provider(): array {
 288          return [
 289              'No opening or closing time set: Is open' => [null, null, true],
 290              'Opening time set in the past, no closing: Is open' => [-DAYSECS, null, true],
 291              'Opening time set in the future, no closing: Is closed' => [+DAYSECS, null, false],
 292              'Closing time set in the past, no opening: Is closed' => [null, -DAYSECS, false],
 293              'Closing time set in the future, no opening: Is open' => [null, +DAYSECS, true],
 294              'Opening and closing in the past: Is closed' => [-WEEKSECS, -DAYSECS, false],
 295              'Opening and closing in the future: Is closed' => [+DAYSECS, +WEEKSECS, false],
 296              'Opening in the past, Closing in the future: Is open' => [-DAYSECS, +DAYSECS, true],
 297          ];
 298      }
 299  
 300      /**
 301       * Check instance currently open
 302       *
 303       * @dataProvider is_currently_open_provider
 304       * @param null|int $openingtime
 305       * @param null|int $closingtime
 306       * @param bool $expected
 307       * @covers ::is_currently_open
 308       */
 309      public function test_is_currently_open(?int $openingtime, ?int $closingtime, bool $expected): void {
 310          $stub = $this->getMockBuilder(instance::class)
 311              ->onlyMethods(['get_instance_var'])
 312              ->disableOriginalConstructor()
 313              ->getMock();
 314  
 315          if ($openingtime) {
 316              $openingtime = $openingtime + time();
 317          }
 318  
 319          if ($closingtime) {
 320              $closingtime = $closingtime + time();
 321          }
 322  
 323          $stub->method('get_instance_var')
 324              ->willReturnCallback(function($var) use ($openingtime, $closingtime) {
 325                  if ($var === 'openingtime') {
 326                      return $openingtime;
 327                  }
 328  
 329                  return $closingtime;
 330              });
 331          $this->assertEquals($expected, $stub->is_currently_open());
 332      }
 333  
 334      /**
 335       * Ensure that the user_must_wait_to_join function works as expectd.
 336       *
 337       * @dataProvider user_must_wait_to_join_provider
 338       * @param bool $isadmin
 339       * @param bool $ismoderator
 340       * @param bool $haswaitingroom
 341       * @param bool $expected
 342       * @covers ::user_must_wait_to_join
 343       */
 344      public function test_user_must_wait_to_join(bool $isadmin, bool $ismoderator, bool $haswaitingroom, bool $expected): void {
 345          $stub = $this->getMockBuilder(instance::class)
 346              ->setMethods([
 347                  'get_instance_var',
 348                  'is_admin',
 349                  'is_moderator',
 350              ])
 351              ->disableOriginalConstructor()
 352              ->getMock();
 353  
 354          $stub->method('is_admin')->willReturn($isadmin);
 355          $stub->method('is_moderator')->willReturn($ismoderator);
 356          $stub->method('get_instance_var')->willReturn($haswaitingroom);
 357  
 358          $this->assertEquals($expected, $stub->user_must_wait_to_join());
 359      }
 360  
 361      /**
 362       * Data provider for the user_must_wait_to_join function.
 363       *
 364       * @return array
 365       */
 366      public function user_must_wait_to_join_provider(): array {
 367          return [
 368              'Admins must never wait to join (waiting disabled)' => [true, false, false, false],
 369              'Admins must never wait to join (waiting enabled)' => [true, false, true, false],
 370              'Moderators must never wait to join (waiting disabled)' => [false, true, false, false],
 371              'Moderators must never wait to join (waiting enabled)' => [false, true, true, false],
 372              'Other users must wait to join if waiting enabled' => [false, false, true, true],
 373              'Other users cannot wait to join if waiting disabled' => [false, false, false, false],
 374          ];
 375      }
 376  
 377      /**
 378       * Ensure that the does_current_user_count_towards_user_limit function works as expectd.
 379       *
 380       * @dataProvider does_current_user_count_towards_user_limit_provider
 381       * @param bool $isadmin
 382       * @param bool $ismoderator
 383       * @param bool $expected
 384       * @covers ::does_current_user_count_towards_user_limit
 385       */
 386      public function test_does_current_user_count_towards_user_limit(
 387          bool $isadmin,
 388          bool $ismoderator,
 389          bool $expected
 390      ): void {
 391          $stub = $this->getMockBuilder(instance::class)
 392              ->setMethods([
 393                  'is_admin',
 394                  'is_moderator',
 395              ])
 396              ->disableOriginalConstructor()
 397              ->getMock();
 398  
 399          $stub->method('is_admin')->willReturn($isadmin);
 400          $stub->method('is_moderator')->willReturn($ismoderator);
 401  
 402          $this->assertEquals($expected, $stub->does_current_user_count_towards_user_limit());
 403      }
 404  
 405      /**
 406       * Data provider for the does_current_user_count_towards_user_limit function.
 407       *
 408       * @return array
 409       */
 410      public function does_current_user_count_towards_user_limit_provider(): array {
 411          return [
 412              'Admin does not count' => [true, false, false],
 413              'Moderator does not count' => [false, true, false],
 414              'Other users do count' => [false, false, true],
 415          ];
 416      }
 417  
 418      /**
 419       * Ensure that the does_current_user_count_towards_user_limit function works as expectd.
 420       *
 421       * @dataProvider get_current_user_password_provider
 422       * @param bool $isadmin
 423       * @param bool $ismoderator
 424       * @param bool $expectedmodpassword
 425       * @covers ::get_current_user_password
 426       */
 427      public function test_get_current_user_password(bool $isadmin, bool $ismoderator, bool $expectedmodpassword): void {
 428          $stub = $this->getMockBuilder(instance::class)
 429              ->setMethods([
 430                  'is_admin',
 431                  'is_moderator',
 432                  'get_moderator_password',
 433                  'get_viewer_password',
 434              ])
 435              ->disableOriginalConstructor()
 436              ->getMock();
 437  
 438          $stub->method('is_admin')->willReturn($isadmin);
 439          $stub->method('is_moderator')->willReturn($ismoderator);
 440          $stub->method('get_moderator_password')->willReturn('Moderator Password');
 441          $stub->method('get_viewer_password')->willReturn('Viewer Password');
 442  
 443          if ($expectedmodpassword) {
 444              $this->assertEquals('Moderator Password', $stub->get_current_user_password());
 445          } else {
 446              $this->assertEquals('Viewer Password', $stub->get_current_user_password());
 447          }
 448      }
 449  
 450      /**
 451       * Data provider for the get_current_user_password function.
 452       *
 453       * @return array
 454       */
 455      public function get_current_user_password_provider(): array {
 456          return [
 457              'Admin is a moderator' => [true, false, true],
 458              'Moderator is a moderator' => [false, true, true],
 459              'Others are a viewer' => [false, false, false],
 460          ];
 461      }
 462  
 463      /**
 464       * Ensure that the get_current_user_role function works as expected.
 465       *
 466       * @dataProvider get_current_user_role_provider
 467       * @param bool $isadmin
 468       * @param bool $ismoderator
 469       * @param bool $expectedmodrole
 470       * @covers ::get_current_user_role
 471       */
 472      public function test_get_current_user_role(bool $isadmin, bool $ismoderator, bool $expectedmodrole): void {
 473          $stub = $this->getMockBuilder(instance::class)
 474              ->setMethods([
 475                  'is_admin',
 476                  'is_moderator',
 477              ])
 478              ->disableOriginalConstructor()
 479              ->getMock();
 480  
 481          $stub->method('is_admin')->willReturn($isadmin);
 482          $stub->method('is_moderator')->willReturn($ismoderator);
 483  
 484          if ($expectedmodrole) {
 485              $this->assertEquals('MODERATOR', $stub->get_current_user_role());
 486          } else {
 487              $this->assertEquals('VIEWER', $stub->get_current_user_role());
 488          }
 489      }
 490  
 491      /**
 492       * Data provider for the get_current_user_role function.
 493       *
 494       * @return array
 495       */
 496      public function get_current_user_role_provider(): array {
 497          return [
 498              'Admin is a moderator' => [true, false, true],
 499              'Moderator is a moderator' => [false, true, true],
 500              'Others are a viewer' => [false, false, false],
 501          ];
 502      }
 503  
 504      /**
 505       * Tests for the allow_recording_start_stop function.
 506       *
 507       * @dataProvider allow_recording_start_stop_provider
 508       * @param bool $isrecorded
 509       * @param bool $showbuttons
 510       * @param bool $expected
 511       * @covers ::allow_recording_start_stop
 512       */
 513      public function test_allow_recording_start_stop(
 514          bool $isrecorded,
 515          bool $showbuttons,
 516          bool $expected
 517      ): void {
 518          $stub = $this->getMockBuilder(instance::class)
 519              ->setMethods([
 520                  'is_recorded',
 521                  'should_show_recording_button',
 522              ])
 523              ->disableOriginalConstructor()
 524              ->getMock();
 525  
 526          $stub->method('is_recorded')->willReturn($isrecorded);
 527          $stub->method('should_show_recording_button')->willReturn($showbuttons);
 528  
 529          $this->assertEquals($expected, $stub->allow_recording_start_stop());
 530      }
 531  
 532      /**
 533       * Data provider for the allow_recording_start_stop function.
 534       *
 535       * @return array
 536       */
 537      public function allow_recording_start_stop_provider(): array {
 538          return [
 539              'Meeting is not recorded: No start/stop' => [false, false, false],
 540              'Meeting recorded, Buttons shown: Allow' => [true, true, true],
 541              'Meeting recorded, Buttons not shown: Deny' => [true, false, false],
 542          ];
 543      }
 544  
 545  
 546      /**
 547       * Test get user id (guest or current user)
 548       * @covers \mod_bigbluebuttonbn\instance::get_user_id
 549       */
 550      public function test_get_user_id(): void {
 551          $this->resetAfterTest();
 552          $this->setUser(null);
 553          ['record' => $record ] = $this->get_test_instance();
 554          $instance = instance::get_from_instanceid($record->id);
 555          $this->assertEquals(0, $instance->get_user_id());
 556          $user = $this->getDataGenerator()->create_user();
 557          $this->setUser($user);
 558          $this->assertEquals($user->id, $instance->get_user_id());
 559      }
 560  
 561      /**
 562       * Test guest access URL
 563       *
 564       * @covers ::get_guest_access_url
 565       */
 566      public function test_get_guest_access_url() {
 567          global $CFG;
 568          $this->resetAfterTest();
 569          ['record' => $record ] = $this->get_test_instance(['guestallowed' => true]);
 570          $CFG->bigbluebuttonbn['guestaccess_enabled'] = 1;
 571          $instance = instance::get_from_instanceid($record->id);
 572          $this->assertNotEmpty($instance->get_guest_access_url());
 573      }
 574  
 575      /**
 576       * Test guest allowed flag
 577       *
 578       * @covers ::is_guest_allowed
 579       */
 580      public function test_is_guest_allowed() {
 581          global $CFG;
 582          $this->resetAfterTest();
 583          ['record' => $record ] = $this->get_test_instance(['guestallowed' => true]);
 584          $CFG->bigbluebuttonbn['guestaccess_enabled'] = 1;
 585          $instance = instance::get_from_instanceid($record->id);
 586          $this->assertTrue($instance->is_guest_allowed());
 587          $CFG->bigbluebuttonbn['guestaccess_enabled'] = 0;
 588          $this->assertFalse($instance->is_guest_allowed());
 589      }
 590  
 591      /**
 592       * Test guest access password
 593       *
 594       * @covers ::get_guest_access_password
 595       */
 596      public function get_guest_access_password() {
 597          global $CFG;
 598          $this->resetAfterTest();
 599          ['record' => $record ] = $this->get_test_instance(['guestallowed' => true]);
 600          $CFG->bigbluebuttonbn['guestaccess_enabled'] = 1;
 601          $instance = instance::get_from_instanceid($record->id);
 602          $this->assertNotEmpty($instance->get_guest_access_password());
 603      }
 604  
 605  }