See Release Notes
Long Term Support Release
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 namespace mod_bigbluebuttonbn\task; 18 19 use advanced_testcase; 20 use core\task\manager; 21 use mod_bigbluebuttonbn\instance; 22 use mod_bigbluebuttonbn\logger; 23 use mod_bigbluebuttonbn\recording; 24 use mod_bigbluebuttonbn\test\testcase_helper_trait; 25 26 /** 27 * Class containing the scheduled task for lti module. 28 * 29 * @package mod_bigbluebuttonbn 30 * @copyright 2019 onwards, Blindside Networks Inc 31 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 32 * @covers \mod_bigbluebuttonbn\task\upgrade_recordings_task 33 */ 34 class upgrade_recordings_task_test extends advanced_testcase { 35 36 use testcase_helper_trait; 37 38 /** 39 * @var object $instance 40 */ 41 protected $instance = null; 42 43 /** 44 * @var array $groups all groups 45 */ 46 protected $groups = []; 47 48 /** 49 * Setup for test 50 */ 51 public function setUp(): void { 52 parent::setUp(); 53 $this->initialise_mock_server(); 54 $this->resetAfterTest(); 55 } 56 57 /** 58 * Upgrade task test 59 */ 60 public function test_upgrade_recordings_basic(): void { 61 global $DB; 62 $this->setup_basic_data(); 63 upgrade_recordings_task::schedule_upgrade_per_meeting(false);; 64 // The first run will lead to all of them being processed, and none left over. 65 // A new job is always queued on a successful run. 66 $this->runAdhocTasks(upgrade_recordings_task::class); 67 $this->assertEquals(0, $DB->count_records('bigbluebuttonbn_logs', ['log' => logger::EVENT_CREATE])); 68 $this->assertEquals(75, recording::count_records(['imported' => '0'])); 69 // Old logs are kept but renamed. 70 $this->assertEquals(75, $DB->count_records('bigbluebuttonbn_logs', ['log' => logger::EVENT_CREATE_MIGRATED])); 71 $this->assertEquals(15, recording::count_records(['groupid' => $this->groups[0]->id, 'imported' => '0', 72 'status' => recording::RECORDING_STATUS_PROCESSED])); 73 $this->assertEquals(15, recording::count_records(['groupid' => $this->groups[1]->id, 'imported' => '0', 74 'status' => recording::RECORDING_STATUS_PROCESSED])); 75 $this->assertEquals(45, 76 recording::count_records(['groupid' => 0, 'imported' => '0', 'status' => recording::RECORDING_STATUS_PROCESSED])); 77 78 // The second run will lead to no change in the number of logs, but no further jobs will be queued. 79 upgrade_recordings_task::schedule_upgrade_per_meeting(); 80 $this->runAdhocTasks(upgrade_recordings_task::class); 81 $this->assertEquals(75, recording::count_records()); 82 // The first run will lead to all of them being processed, and none left over. 83 // A new job is always queued on a successful run. 84 $this->assertEmpty($DB->get_records_select( 85 'bigbluebuttonbn_logs', 86 'log = :logmatch AND ' . $DB->sql_like('meta', ':match'), 87 [ 88 'logmatch' => 'Create', 89 'match' => '%true%' 90 ] 91 )); 92 $this->runAdhocTasks(upgrade_recordings_task::class); 93 $this->assertEmpty($DB->get_records_select( 94 'bigbluebuttonbn_logs', 95 'log = :logmatch AND ' . $DB->sql_like('meta', ':match'), 96 [ 97 'logmatch' => 'Create', 98 'match' => '%true%' 99 ] 100 )); 101 $this->assertEquals(0, $DB->count_records('bigbluebuttonbn_logs', ['log' => 'Create'])); 102 // Ensure that logs match. 103 $matchesarray = [ 104 [ 105 'Executing .*', 106 'Fetching logs for conversion', 107 "Creating new recording records", 108 'Migrated 30 recordings', 109 ], 110 [ 111 'Executing .*', 112 'Fetching logs for conversion', 113 "Creating new recording records", 114 'Migrated 15 recordings', 115 ], 116 [ 117 'Executing .*', 118 'Fetching logs for conversion', 119 "Creating new recording records", 120 "Unable to find an activity for .*. This recording is headless", 121 'Migrated 15 recordings' 122 ] 123 ]; 124 foreach ($matchesarray as $matches) { 125 $this->expectOutputRegex('/' . implode('.*', $matches) . '/s'); 126 } 127 } 128 129 /** 130 * Upgrade task test 131 */ 132 public function test_upgrade_recordings_imported_basic(): void { 133 global $DB; 134 $this->setup_basic_data(true); 135 upgrade_recordings_task::schedule_upgrade_per_meeting(true);; 136 // The first run will lead to all of them being processed, and none left over. 137 // A new job is always queued on a successful run. 138 $this->runAdhocTasks(upgrade_recordings_task::class); 139 140 $this->assertEquals(0, $DB->count_records('bigbluebuttonbn_logs', ['log' => logger::EVENT_IMPORT])); 141 $this->assertEquals(75, $DB->count_records('bigbluebuttonbn_logs', ['log' => logger::EVENT_IMPORT_MIGRATED])); 142 $this->assertEquals(75, recording::count_records(['imported' => '1'])); 143 144 $this->assertEquals(15, recording::count_records(['groupid' => $this->groups[0]->id, 'imported' => '1', 145 'status' => recording::RECORDING_STATUS_PROCESSED])); 146 $this->assertEquals(15, recording::count_records(['groupid' => $this->groups[1]->id, 'imported' => '1', 147 'status' => recording::RECORDING_STATUS_PROCESSED])); 148 $this->assertEquals(45, 149 recording::count_records(['groupid' => 0, 'imported' => '1', 'status' => recording::RECORDING_STATUS_PROCESSED])); 150 151 // The second run will lead to no change in the number of logs, but no further jobs will be queued. 152 upgrade_recordings_task::schedule_upgrade_per_meeting(); 153 $this->runAdhocTasks(upgrade_recordings_task::class); 154 $this->assertEquals(75, recording::count_records(['imported' => '1'])); 155 // The first run will lead to all of them being processed, and none left over. 156 // A new job is always queued on a successful run. 157 $this->assertEmpty($DB->get_records_select( 158 'bigbluebuttonbn_logs', 159 'log = :logmatch', 160 [ 161 'logmatch' => 'Import' 162 ] 163 )); 164 // Ensure that logs match. 165 $matchesarray = [ 166 [ 167 'Executing .*', 168 'Fetching logs for conversion', 169 "Creating new recording records", 170 'Migrated 30 recordings', 171 ], 172 [ 173 'Executing .*', 174 'Fetching logs for conversion', 175 "Creating new recording records", 176 'Migrated 15 recordings', 177 ], 178 [ 179 'Executing .*', 180 'Fetching logs for conversion', 181 "Creating new recording records", 182 "Unable to find an activity for .*. This recording is headless", 183 'Migrated 15 recordings' 184 ] 185 ]; 186 foreach ($matchesarray as $matches) { 187 $this->expectOutputRegex('/' . implode('.*', $matches) . '/s'); 188 } 189 } 190 191 /** 192 * Upgrade recordings when we have missing recordings on the server 193 * Basically, the recordings are imported and then we cannot the other because logs have been marked as migrated. 194 */ 195 public function test_upgrade_recordings_with_missing_recording_on_bbb_server(): void { 196 global $DB; 197 $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); 198 [$teacher, $groups, $instance, $groupedinstance, $deletedinstance] = $this->setup_basic_course_and_meeting(); 199 200 $this->create_legacy_log_entries($instance, $teacher->id, 5, false); 201 $this->create_legacy_log_entries($instance, $teacher->id, 5, false, false); 202 $this->assertEquals(10, $DB->count_records('bigbluebuttonbn_logs', ['log' => 'Create'])); 203 204 // Schedule the run. 205 upgrade_recordings_task::schedule_upgrade_per_meeting(); 206 $this->runAdhocTasks(upgrade_recordings_task::class); 207 // At this point only 5 are created, the rest is still in the queue. 208 $this->assertEquals(0, $DB->count_records('bigbluebuttonbn_logs', ['log' => 'Create'])); 209 $this->assertEquals(5, recording::count_records()); 210 // Now create 5 recordings on the server. 211 // Schedule the run. 212 upgrade_recordings_task::schedule_upgrade_per_meeting(); 213 for ($index = 0; $index < 5; $index++) { 214 $plugingenerator->create_recording([ 215 'bigbluebuttonbnid' => $instance->get_instance_id(), 216 'groupid' => $instance->get_group_id(), 217 'starttime' => time(), 218 'endtime' => time() + HOURSECS, 219 ], true); // Create another recording on the server. 220 } 221 $this->assertEquals(0, $DB->count_records('task_adhoc', ['classname' => '\\' . upgrade_recordings_task::class])); 222 $this->runAdhocTasks(upgrade_recordings_task::class); 223 // Ensure that logs match. 224 225 // Ensure that logs match. 226 $matchesarray = [ 227 [ 228 'Executing .*', 229 'Fetching logs for conversion', 230 "Creating new recording records", 231 'Migrated 5 recordings', 232 ], 233 ]; 234 foreach ($matchesarray as $matches) { 235 $this->expectOutputRegex('/' . implode('.*', $matches) . '/s'); 236 } 237 } 238 239 /** 240 * Upgrade task test with more recordings on the server than in the log : we add all recording and should have 241 * no more logs. 242 */ 243 public function test_upgrade_recordings_with_more_recordings_on_bbb_server(): void { 244 global $DB; 245 $generator = $this->getDataGenerator(); 246 // Create a course with student and teacher, and two groups. 247 $this->course = $generator->create_course(); 248 $user = $this->getDataGenerator()->create_and_enrol($this->course); 249 // Create an ungrouped activity. 250 $activity = $generator->create_module('bigbluebuttonbn', [ 251 'course' => $this->course->id, 252 ]); 253 $this->instance = instance::get_from_instanceid($activity->id); 254 // We create 5 recordings in the log but no recording instance on the server. 255 $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); 256 $plugingenerator->create_meeting([ 257 'instanceid' => $this->instance->get_instance_id(), 258 'groupid' => $this->instance->get_group_id(), 259 ]); 260 $this->create_legacy_log_entries($this->instance, $user->id, 5, false); 261 $plugingenerator->create_recording([ 262 'bigbluebuttonbnid' => $this->instance->get_instance_id(), 263 'groupid' => $this->instance->get_group_id(), 264 'starttime' => time(), 265 'endtime' => time() + HOURSECS, 266 ], true); // Create another recording on the server. 267 268 $this->assertEquals(5, $DB->count_records('bigbluebuttonbn_logs', ['log' => 'Create'])); 269 upgrade_recordings_task::schedule_upgrade_per_meeting(); 270 $this->runAdhocTasks(upgrade_recordings_task::class); 271 $this->assertEquals(6, recording::count_records(['status' => recording::RECORDING_STATUS_PROCESSED])); 272 $this->assertEquals(0, $DB->count_records('bigbluebuttonbn_logs', ['log' => 'Create'])); 273 // Ensure that logs match. 274 $matches = [ 275 'Executing .*', 276 'Fetching logs for conversion', 277 "Creating new recording records", 278 'Migrated 6 recordings', 279 ]; 280 $this->expectOutputRegex('/' . implode('.*', $matches) . '/s'); 281 } 282 283 /** 284 * Setup basic data for tests 285 * 286 * @param bool $importedrecording 287 * @return void 288 * @throws \coding_exception 289 * @throws \moodle_exception 290 */ 291 protected function setup_basic_data($importedrecording = false) { 292 global $DB; 293 [$teacher, $groups, $instance, $groupedinstance, $deletedinstance] = $this->setup_basic_course_and_meeting(); 294 295 $this->create_legacy_log_entries($instance, $teacher->id, 30, $importedrecording); 296 foreach ($groups as $group) { 297 $groupinstance = instance::get_group_instance_from_instance($groupedinstance, $group->id); 298 $this->create_legacy_log_entries($groupinstance, $teacher->id, 15, $importedrecording); 299 } 300 $this->create_legacy_log_entries($deletedinstance, $teacher->id, 15, $importedrecording); 301 course_delete_module($deletedinstance->get_cm_id()); 302 // Truncate the recordings table to reflect what it would have looked like before this version. 303 $DB->delete_records('bigbluebuttonbn_recordings'); 304 $this->groups = $groups; 305 $this->instance = $instance; 306 } 307 308 /** 309 * Setup basic data for tests 310 * 311 * @return array 312 * @throws \coding_exception 313 * @throws \moodle_exception 314 */ 315 protected function setup_basic_course_and_meeting() { 316 $generator = $this->getDataGenerator(); 317 // Create a course with student and teacher, and two groups. 318 $this->course = $generator->create_course(); 319 $groups = []; 320 $groups[] = $generator->create_group(['courseid' => $this->course->id]); 321 $groups[] = $generator->create_group(['courseid' => $this->course->id]); 322 323 $teacher = $generator->create_and_enrol($this->course, 'editingteacher'); 324 $generator->create_and_enrol($this->course, 'student'); 325 326 // Create a "normal" meeting. 327 $instance = $this->create_meeting_for_logs(); 328 // Create an grouped activity. 329 $groupedinstance = $this->create_meeting_for_logs($groups); 330 // Create an instance that will then be deleted. 331 $deletedinstance = $this->create_meeting_for_logs(); 332 // Create logs for an activity which no longer exists (because we deleted it). 333 return [$teacher, $groups, $instance, $groupedinstance, $deletedinstance]; 334 } 335 336 /** 337 * Create a meeting and return its instance 338 * 339 * @param array|null $groups 340 * @return instance 341 */ 342 protected function create_meeting_for_logs(?array $groups = null) { 343 $generator = $this->getDataGenerator(); 344 $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); 345 $params = [ 346 'course' => $this->course->id, 347 ]; 348 if (!empty($groups)) { 349 $params['groupmode'] = SEPARATEGROUPS; 350 } 351 $activity = $generator->create_module('bigbluebuttonbn', $params); 352 $instance = instance::get_from_instanceid($activity->id); 353 if (!empty($groups)) { 354 foreach ($groups as $group) { 355 $groupinstance = instance::get_group_instance_from_instance($instance, $group->id); 356 $plugingenerator->create_meeting([ 357 'instanceid' => $groupinstance->get_instance_id(), 358 'groupid' => $groupinstance->get_group_id(), 359 ]); 360 } 361 } else { 362 $plugingenerator->create_meeting([ 363 'instanceid' => $instance->get_instance_id(), 364 'groupid' => $instance->get_group_id(), 365 ]); 366 } 367 return $instance; 368 } 369 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body