See Release Notes
Long Term Support Release
<?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. namespace mod_bigbluebuttonbn\task; use advanced_testcase; use core\task\manager; use mod_bigbluebuttonbn\instance; use mod_bigbluebuttonbn\logger; use mod_bigbluebuttonbn\recording; use mod_bigbluebuttonbn\test\testcase_helper_trait; /** * Class containing the scheduled task for lti module. * * @package mod_bigbluebuttonbn * @copyright 2019 onwards, Blindside Networks Inc * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @covers \mod_bigbluebuttonbn\task\upgrade_recordings_task */ class upgrade_recordings_task_test extends advanced_testcase { use testcase_helper_trait; /** * @var object $instance */ protected $instance = null; /** * @var array $groups all groups */ protected $groups = []; /** * Setup for test */ public function setUp(): void { parent::setUp(); $this->initialise_mock_server(); $this->resetAfterTest(); } /** * Upgrade task test */ public function test_upgrade_recordings_basic(): void { global $DB; $this->setup_basic_data(); upgrade_recordings_task::schedule_upgrade_per_meeting(false);; // The first run will lead to all of them being processed, and none left over. // A new job is always queued on a successful run. $this->runAdhocTasks(upgrade_recordings_task::class); $this->assertEquals(0, $DB->count_records('bigbluebuttonbn_logs', ['log' => logger::EVENT_CREATE])); $this->assertEquals(75, recording::count_records(['imported' => '0'])); // Old logs are kept but renamed. $this->assertEquals(75, $DB->count_records('bigbluebuttonbn_logs', ['log' => logger::EVENT_CREATE_MIGRATED])); $this->assertEquals(15, recording::count_records(['groupid' => $this->groups[0]->id, 'imported' => '0', 'status' => recording::RECORDING_STATUS_PROCESSED])); $this->assertEquals(15, recording::count_records(['groupid' => $this->groups[1]->id, 'imported' => '0', 'status' => recording::RECORDING_STATUS_PROCESSED])); $this->assertEquals(45, recording::count_records(['groupid' => 0, 'imported' => '0', 'status' => recording::RECORDING_STATUS_PROCESSED])); // The second run will lead to no change in the number of logs, but no further jobs will be queued. upgrade_recordings_task::schedule_upgrade_per_meeting(); $this->runAdhocTasks(upgrade_recordings_task::class); $this->assertEquals(75, recording::count_records()); // The first run will lead to all of them being processed, and none left over. // A new job is always queued on a successful run. $this->assertEmpty($DB->get_records_select( 'bigbluebuttonbn_logs', 'log = :logmatch AND ' . $DB->sql_like('meta', ':match'), [ 'logmatch' => 'Create', 'match' => '%true%' ] )); $this->runAdhocTasks(upgrade_recordings_task::class); $this->assertEmpty($DB->get_records_select( 'bigbluebuttonbn_logs', 'log = :logmatch AND ' . $DB->sql_like('meta', ':match'), [ 'logmatch' => 'Create', 'match' => '%true%' ] )); $this->assertEquals(0, $DB->count_records('bigbluebuttonbn_logs', ['log' => 'Create'])); // Ensure that logs match. $matchesarray = [ [ 'Executing .*', 'Fetching logs for conversion', "Creating new recording records", 'Migrated 30 recordings', ], [ 'Executing .*', 'Fetching logs for conversion', "Creating new recording records", 'Migrated 15 recordings', ], [ 'Executing .*', 'Fetching logs for conversion', "Creating new recording records", "Unable to find an activity for .*. This recording is headless", 'Migrated 15 recordings' ] ]; foreach ($matchesarray as $matches) { $this->expectOutputRegex('/' . implode('.*', $matches) . '/s'); } } /** * Upgrade task test */ public function test_upgrade_recordings_imported_basic(): void { global $DB; $this->setup_basic_data(true); upgrade_recordings_task::schedule_upgrade_per_meeting(true);; // The first run will lead to all of them being processed, and none left over. // A new job is always queued on a successful run. $this->runAdhocTasks(upgrade_recordings_task::class); $this->assertEquals(0, $DB->count_records('bigbluebuttonbn_logs', ['log' => logger::EVENT_IMPORT])); $this->assertEquals(75, $DB->count_records('bigbluebuttonbn_logs', ['log' => logger::EVENT_IMPORT_MIGRATED])); $this->assertEquals(75, recording::count_records(['imported' => '1'])); $this->assertEquals(15, recording::count_records(['groupid' => $this->groups[0]->id, 'imported' => '1', 'status' => recording::RECORDING_STATUS_PROCESSED])); $this->assertEquals(15, recording::count_records(['groupid' => $this->groups[1]->id, 'imported' => '1', 'status' => recording::RECORDING_STATUS_PROCESSED])); $this->assertEquals(45, recording::count_records(['groupid' => 0, 'imported' => '1', 'status' => recording::RECORDING_STATUS_PROCESSED])); // The second run will lead to no change in the number of logs, but no further jobs will be queued. upgrade_recordings_task::schedule_upgrade_per_meeting(); $this->runAdhocTasks(upgrade_recordings_task::class); $this->assertEquals(75, recording::count_records(['imported' => '1'])); // The first run will lead to all of them being processed, and none left over. // A new job is always queued on a successful run. $this->assertEmpty($DB->get_records_select( 'bigbluebuttonbn_logs', 'log = :logmatch', [ 'logmatch' => 'Import' ] )); // Ensure that logs match. $matchesarray = [ [ 'Executing .*', 'Fetching logs for conversion', "Creating new recording records", 'Migrated 30 recordings', ], [ 'Executing .*', 'Fetching logs for conversion', "Creating new recording records", 'Migrated 15 recordings', ], [ 'Executing .*', 'Fetching logs for conversion', "Creating new recording records", "Unable to find an activity for .*. This recording is headless", 'Migrated 15 recordings' ] ]; foreach ($matchesarray as $matches) { $this->expectOutputRegex('/' . implode('.*', $matches) . '/s'); } } /** * Upgrade recordings when we have missing recordings on the server * Basically, the recordings are imported and then we cannot the other because logs have been marked as migrated. */ public function test_upgrade_recordings_with_missing_recording_on_bbb_server(): void { global $DB; $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); [$teacher, $groups, $instance, $groupedinstance, $deletedinstance] = $this->setup_basic_course_and_meeting();< $this->create_legacy_log_entries($instance, $teacher->id, 5, false); < $this->create_legacy_log_entries($instance, $teacher->id, 5, false, false);> $this->create_log_entries($instance, $teacher->id, 5); > $this->create_log_entries($instance, $teacher->id, 5, false, false);$this->assertEquals(10, $DB->count_records('bigbluebuttonbn_logs', ['log' => 'Create'])); // Schedule the run. upgrade_recordings_task::schedule_upgrade_per_meeting(); $this->runAdhocTasks(upgrade_recordings_task::class); // At this point only 5 are created, the rest is still in the queue. $this->assertEquals(0, $DB->count_records('bigbluebuttonbn_logs', ['log' => 'Create'])); $this->assertEquals(5, recording::count_records()); // Now create 5 recordings on the server. // Schedule the run. upgrade_recordings_task::schedule_upgrade_per_meeting(); for ($index = 0; $index < 5; $index++) { $plugingenerator->create_recording([ 'bigbluebuttonbnid' => $instance->get_instance_id(), 'groupid' => $instance->get_group_id(), 'starttime' => time(), 'endtime' => time() + HOURSECS, ], true); // Create another recording on the server. } $this->assertEquals(0, $DB->count_records('task_adhoc', ['classname' => '\\' . upgrade_recordings_task::class])); $this->runAdhocTasks(upgrade_recordings_task::class); // Ensure that logs match. // Ensure that logs match. $matchesarray = [ [ 'Executing .*', 'Fetching logs for conversion', "Creating new recording records", 'Migrated 5 recordings', ], ]; foreach ($matchesarray as $matches) { $this->expectOutputRegex('/' . implode('.*', $matches) . '/s'); } } /** * Upgrade task test with more recordings on the server than in the log : we add all recording and should have * no more logs. */ public function test_upgrade_recordings_with_more_recordings_on_bbb_server(): void { global $DB; $generator = $this->getDataGenerator(); // Create a course with student and teacher, and two groups. $this->course = $generator->create_course(); $user = $this->getDataGenerator()->create_and_enrol($this->course); // Create an ungrouped activity. $activity = $generator->create_module('bigbluebuttonbn', [ 'course' => $this->course->id, ]); $this->instance = instance::get_from_instanceid($activity->id); // We create 5 recordings in the log but no recording instance on the server. $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); $plugingenerator->create_meeting([ 'instanceid' => $this->instance->get_instance_id(), 'groupid' => $this->instance->get_group_id(), ]);< $this->create_legacy_log_entries($this->instance, $user->id, 5, false);> $this->create_log_entries($this->instance, $user->id, 5);$plugingenerator->create_recording([ 'bigbluebuttonbnid' => $this->instance->get_instance_id(), 'groupid' => $this->instance->get_group_id(), 'starttime' => time(), 'endtime' => time() + HOURSECS, ], true); // Create another recording on the server. $this->assertEquals(5, $DB->count_records('bigbluebuttonbn_logs', ['log' => 'Create'])); upgrade_recordings_task::schedule_upgrade_per_meeting(); $this->runAdhocTasks(upgrade_recordings_task::class); $this->assertEquals(6, recording::count_records(['status' => recording::RECORDING_STATUS_PROCESSED])); $this->assertEquals(0, $DB->count_records('bigbluebuttonbn_logs', ['log' => 'Create'])); // Ensure that logs match. $matches = [ 'Executing .*', 'Fetching logs for conversion', "Creating new recording records", 'Migrated 6 recordings', ]; $this->expectOutputRegex('/' . implode('.*', $matches) . '/s'); } /** * Setup basic data for tests * * @param bool $importedrecording * @return void * @throws \coding_exception * @throws \moodle_exception */ protected function setup_basic_data($importedrecording = false) { global $DB; [$teacher, $groups, $instance, $groupedinstance, $deletedinstance] = $this->setup_basic_course_and_meeting();< $this->create_legacy_log_entries($instance, $teacher->id, 30, $importedrecording);> $this->create_log_entries($instance, $teacher->id, 30, $importedrecording);foreach ($groups as $group) { $groupinstance = instance::get_group_instance_from_instance($groupedinstance, $group->id);< $this->create_legacy_log_entries($groupinstance, $teacher->id, 15, $importedrecording);> $this->create_log_entries($groupinstance, $teacher->id, 15, $importedrecording);}< $this->create_legacy_log_entries($deletedinstance, $teacher->id, 15, $importedrecording);> $this->create_log_entries($deletedinstance, $teacher->id, 15, $importedrecording);course_delete_module($deletedinstance->get_cm_id()); // Truncate the recordings table to reflect what it would have looked like before this version. $DB->delete_records('bigbluebuttonbn_recordings'); $this->groups = $groups; $this->instance = $instance; } /** * Setup basic data for tests * * @return array * @throws \coding_exception * @throws \moodle_exception */ protected function setup_basic_course_and_meeting() { $generator = $this->getDataGenerator(); // Create a course with student and teacher, and two groups. $this->course = $generator->create_course(); $groups = []; $groups[] = $generator->create_group(['courseid' => $this->course->id]); $groups[] = $generator->create_group(['courseid' => $this->course->id]); $teacher = $generator->create_and_enrol($this->course, 'editingteacher'); $generator->create_and_enrol($this->course, 'student'); // Create a "normal" meeting. $instance = $this->create_meeting_for_logs(); // Create an grouped activity. $groupedinstance = $this->create_meeting_for_logs($groups); // Create an instance that will then be deleted. $deletedinstance = $this->create_meeting_for_logs(); // Create logs for an activity which no longer exists (because we deleted it). return [$teacher, $groups, $instance, $groupedinstance, $deletedinstance]; } /** * Create a meeting and return its instance * * @param array|null $groups * @return instance */ protected function create_meeting_for_logs(?array $groups = null) { $generator = $this->getDataGenerator(); $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); $params = [ 'course' => $this->course->id, ]; if (!empty($groups)) { $params['groupmode'] = SEPARATEGROUPS; } $activity = $generator->create_module('bigbluebuttonbn', $params); $instance = instance::get_from_instanceid($activity->id); if (!empty($groups)) { foreach ($groups as $group) { $groupinstance = instance::get_group_instance_from_instance($instance, $group->id); $plugingenerator->create_meeting([ 'instanceid' => $groupinstance->get_instance_id(), 'groupid' => $groupinstance->get_group_id(), ]); } } else { $plugingenerator->create_meeting([ 'instanceid' => $instance->get_instance_id(), 'groupid' => $instance->get_group_id(), ]); } return $instance; } }