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 401 and 402] [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  /**
  18   * BBB Library tests class trait.
  19   *
  20   * @package   mod_bigbluebuttonbn
  21   * @copyright 2018 - present, Blindside Networks Inc
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   * @author    Laurent David (laurent@call-learning.fr)
  24   */
  25  namespace mod_bigbluebuttonbn\test;
  26  
  27  use context_module;
  28  use mod_bigbluebuttonbn\instance;
  29  use mod_bigbluebuttonbn\local\proxy\recording_proxy;
  30  use mod_bigbluebuttonbn\meeting;
  31  use stdClass;
  32  use testing_data_generator;
  33  use core\plugininfo\mod;
  34  
  35  trait testcase_helper_trait {
  36      /** @var testing_data_generator|null */
  37      protected $generator = null;
  38  
  39      /** @var object|null */
  40      protected $course = null;
  41  
  42      /**
  43       * Convenience function to create an instance of a bigbluebuttonactivty.
  44       *
  45       * @param stdClass|null $course course to add the module to
  46       * @param array $params Array of parameters to pass to the generator
  47       * @param array $options Array of options to pass to the generator
  48       * @return array($context, $cm, $instance) Testable wrapper around the assign class.
  49       */
  50      protected function create_instance(?stdClass $course = null, array $params = [], array $options = []): array {
  51          // Prior to creating the instance, make sure that the BigBlueButton module is enabled.
  52          $modules = \core_plugin_manager::instance()->get_plugins_of_type('mod');
  53          if (!$modules['bigbluebuttonbn']->is_enabled()) {
  54              mod::enable_plugin('bigbluebuttonbn', true);
  55          }
  56  
  57          if (!$course) {
  58              $course = $this->get_course();
  59          }
  60          $params['course'] = $course->id;
  61          $options['visible'] = 1;
  62          $instance = $this->getDataGenerator()->create_module('bigbluebuttonbn', $params, $options);
  63          list($course, $cm) = get_course_and_cm_from_instance($instance, 'bigbluebuttonbn');
  64          $context = context_module::instance($cm->id);
  65  
  66          return [$context, $cm, $instance];
  67      }
  68  
  69      /**
  70       * Get the matching form data
  71       *
  72       * @param stdClass $bbactivity the current bigbluebutton activity
  73       * @param stdClass|null $course the course or null (taken from $this->get_course() if null)
  74       * @return mixed
  75       */
  76      protected function get_form_data_from_instance(stdClass $bbactivity, ?stdClass $course = null): object {
  77          global $USER;
  78  
  79          if (!$course) {
  80              $course = $this->get_course();
  81          }
  82          $this->setAdminUser();
  83          $bbactivitycm = get_coursemodule_from_instance('bigbluebuttonbn', $bbactivity->id);
  84          list($cm, $context, $module, $data, $cw) = get_moduleinfo_data($bbactivitycm, $course);
  85          $this->setUser($USER);
  86          return $data;
  87      }
  88  
  89      /**
  90       * Get or create course if it does not exist
  91       *
  92       * @return stdClass|null
  93       */
  94      protected function get_course(): stdClass {
  95          if (!$this->course) {
  96              $this->course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
  97          }
  98          return $this->course;
  99      }
 100  
 101      /**
 102       * Generate a course, several students and several groups
 103       *
 104       * @param stdClass $courserecord
 105       * @param int $numstudents
 106       * @param int $numteachers
 107       * @param int $groupsnum
 108       * @return array
 109       */
 110      protected function setup_course_students_teachers(stdClass $courserecord, int $numstudents, int $numteachers,
 111          int $groupsnum): array {
 112          global $DB;
 113          $generator = $this->getDataGenerator();
 114          $course = $generator->create_course($courserecord);
 115          $groups = [];
 116          for ($i = 0; $i < $groupsnum; $i++) {
 117              $groups[] = $generator->create_group(['courseid' => $course->id]);
 118          }
 119          $generator->create_group(['courseid' => $course->id]);
 120          $generator->create_group(['courseid' => $course->id]);
 121  
 122          $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
 123  
 124          $students = [];
 125          for ($i = 0; $i < $numstudents; $i++) {
 126              $student = $generator->create_user();
 127              $generator->enrol_user($student->id, $course->id, $roleids['student']);
 128              $groupid = $groups[$i % $groupsnum]->id;
 129              groups_add_member($groupid, $student->id);
 130              $students[] = $student;
 131          }
 132  
 133          $teachers = [];
 134          for ($i = 0; $i < $numteachers; $i++) {
 135              $teacher = $generator->create_user();
 136              $generator->enrol_user($teacher->id, $course->id, $roleids['teacher']);
 137              $groupid = $groups[$i % $groupsnum]->id;
 138              groups_add_member($groupid, $teacher->id);
 139              $teachers[] = $teacher;
 140          }
 141          $bbactivity = $generator->create_module(
 142              'bigbluebuttonbn',
 143              ['course' => $course->id],
 144              ['visible' => true]);
 145  
 146          get_fast_modinfo(0, 0, true);
 147          return [$course, $groups, $students, $teachers, $bbactivity, $roleids];
 148      }
 149  
 150      /**
 151       * This test requires mock server to be present.
 152       */
 153      protected function initialise_mock_server(): void {
 154          if (!defined('TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER')) {
 155              $this->markTestSkipped(
 156                  'The TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER constant must be defined to run mod_bigbluebuttonbn tests'
 157              );
 158          }
 159          try {
 160              $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn')->reset_mock();
 161          } catch (\moodle_exception $e) {
 162              $this->markTestSkipped(
 163                  'Cannot connect to the mock server for this test. Make sure that TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER points
 164                  to an active Mock server'
 165              );
 166          }
 167      }
 168  
 169      /**
 170       * Create an return an array of recordings
 171       *
 172       * @param instance $instance
 173       * @param array $recordingdata array of recording information
 174       * @param array $additionalmeetingdata
 175       * @return array
 176       */
 177      protected function create_recordings_for_instance(instance $instance, array $recordingdata = [],
 178          $additionalmeetingdata = []): array {
 179          $recordings = [];
 180          $bbbgenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn');
 181          // Create the meetings on the mock server, so like this we can find the recordings.
 182          $meeting = new meeting($instance);
 183          $meeting->update_cache(); // The meeting has just been created but we need to force fetch info from the server.
 184          if (!$meeting->is_running()) {
 185              $additionalmeetingdata = array_merge([
 186                  'instanceid' => $instance->get_instance_id(),
 187                  'groupid' => $instance->get_group_id()
 188              ], $additionalmeetingdata);
 189              $bbbgenerator->create_meeting($additionalmeetingdata);
 190          }
 191          foreach ($recordingdata as $rindex => $data) {
 192              $recordings[] = $bbbgenerator->create_recording(
 193                  array_merge([
 194                      'bigbluebuttonbnid' => $instance->get_instance_id(),
 195                      'groupid' => $instance->get_group_id()
 196                  ], $data)
 197              );
 198          }
 199          return $recordings;
 200      }
 201  
 202      /**
 203       * Create an activity which includes a set of recordings.
 204       *
 205       * @param stdClass $course
 206       * @param int $type
 207       * @param array $recordingdata array of recording information
 208       * @param int $groupid
 209       * @return array
 210       */
 211      protected function create_activity_with_recordings(stdClass $course, int $type, array $recordingdata, int $groupid = 0): array {
 212          $generator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn');
 213  
 214          $activity = $generator->create_instance([
 215              'course' => $course->id,
 216              'type' => $type
 217          ]);
 218  
 219          $instance = instance::get_from_instanceid($activity->id);
 220          $instance->set_group_id($groupid);
 221          $recordings = $this->create_recordings_for_instance($instance, $recordingdata);
 222          return [
 223              'course' => $course,
 224              'activity' => $activity,
 225              'recordings' => $recordings,
 226          ];
 227      }
 228  
 229      /**
 230       * Create a course, users and recording from dataset given in an array form
 231       *
 232       * @param array $dataset
 233       * @return mixed
 234       */
 235      protected function create_from_dataset(array $dataset) {
 236          list('type' => $type, 'recordingsdata' => $recordingsdata, 'groups' => $groups,
 237              'users' => $users) = $dataset;
 238          $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn');
 239  
 240          $coursedata = empty($groups) ? [] : ['groupmodeforce' => true, 'groupmode' => $dataset['coursemode'] ?? VISIBLEGROUPS];
 241          $this->course = $this->getDataGenerator()->create_course($coursedata);
 242  
 243          foreach ($users as $userdata) {
 244              $this->getDataGenerator()->create_and_enrol($this->course, $userdata['role'], ['username' => $userdata['username']]);
 245          }
 246  
 247          if ($groups) {
 248              foreach ($groups as $groupname => $students) {
 249                  $group = $this->getDataGenerator()->create_group(['name' => $groupname, 'courseid' => $this->course->id]);
 250                  foreach ($students as $username) {
 251                      $user = \core_user::get_user_by_username($username);
 252                      $this->getDataGenerator()->create_group_member(['userid' => $user->id, 'groupid' => $group->id]);
 253                  }
 254              }
 255          }
 256          $instancesettings = [
 257              'course' => $this->course->id,
 258              'type' => $type,
 259              'name' => 'Example',
 260          ];
 261          if (!empty($dataset['additionalsettings'])) {
 262              $instancesettings = array_merge($instancesettings, $dataset['additionalsettings']);
 263          }
 264          $activity = $plugingenerator->create_instance($instancesettings);
 265          $instance = instance::get_from_instanceid($activity->id);
 266          foreach ($recordingsdata as $groupname => $recordings) {
 267              if ($groups) {
 268                  $groupid = groups_get_group_by_name($this->course->id, $groupname);
 269                  $instance->set_group_id($groupid);
 270              }
 271              $this->create_recordings_for_instance($instance, $recordings);
 272          }
 273          return $activity->id;
 274      }
 275  
 276      /**
 277       * Create the legacy log entries for this task.
 278       *
 279       * @param instance $instance
 280       * @param int $userid
 281       * @param int $count
 282       * @param bool $importedrecordings
 283       * @param bool $withremoterecordings create recording on the mock server ?
 284       * @return array
 285       */
 286      protected function create_legacy_log_entries(
 287          instance $instance,
 288          int $userid,
 289          int $count = 30,
 290          bool $importedrecordings = false,
 291          bool $withremoterecordings = true
 292      ): array {
 293          // Create log entries for each (30 for the ungrouped, 30 for the grouped).
 294          $baselogdata = [
 295              'courseid' => $instance->get_course_id(),
 296              'userid' => $userid,
 297              'log' => $importedrecordings ? 'Import' : 'Create',
 298              'meta' => json_encode(['record' => true]),
 299              'imported' => $importedrecordings,
 300          ];
 301          $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn');
 302          for ($i = 0; $i < $count; $i++) {
 303              if ($withremoterecordings) {
 304                  // Create a recording.
 305                  $starttime = time() - random_int(HOURSECS, WEEKSECS);
 306                  $recording = $plugingenerator->create_recording([
 307                          'bigbluebuttonbnid' => $instance->get_instance_id(),
 308                          'groupid' => $instance->get_group_id(),
 309                          'starttime' => $starttime,
 310                          'endtime' => $starttime + HOURSECS,
 311                  ], true); // Create them on the server only.
 312  
 313                  $baselogdata['meetingid'] = $instance->get_meeting_id();
 314                  if ($importedrecordings) {
 315                      // Fetch the data.
 316                      $data = recording_proxy::fetch_recordings([$recording->recordingid]);
 317                      $data = end($data);
 318                      if ($data) {
 319                          $metaonly = array_filter($data, function($key) {
 320                              return strstr($key, 'meta_');
 321                          }, ARRAY_FILTER_USE_KEY);
 322                      } else {
 323                          $data = [];
 324                      }
 325                      $baselogdata['meta'] = json_encode(array_merge([
 326                              'recording' => array_diff_key($data, $metaonly),
 327                      ], $metaonly));
 328  
 329                  } else {
 330                      $baselogdata['meta'] = json_encode((object) ['record' => true]);
 331                  }
 332              }
 333              // Insert the legacy log entry.
 334              $logs[] = $plugingenerator->create_log(array_merge($baselogdata, [
 335                  'bigbluebuttonbnid' => $instance->get_instance_id(),
 336                  'timecreated' => time() - random_int(HOURSECS, WEEKSECS) + (HOURSECS * $i),
 337              ]));
 338          }
 339  
 340          return $logs;
 341      }
 342  }