| 1 | efrain | 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 core_backup;
 | 
        
           |  |  | 18 |   | 
        
           |  |  | 19 | use backup;
 | 
        
           |  |  | 20 |   | 
        
           |  |  | 21 | defined('MOODLE_INTERNAL') || die();
 | 
        
           |  |  | 22 |   | 
        
           |  |  | 23 | global $CFG;
 | 
        
           |  |  | 24 | require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
 | 
        
           |  |  | 25 | require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
 | 
        
           |  |  | 26 | require_once($CFG->libdir . '/completionlib.php');
 | 
        
           |  |  | 27 |   | 
        
           |  |  | 28 | /**
 | 
        
           |  |  | 29 |  * Course copy tests.
 | 
        
           |  |  | 30 |  *
 | 
        
           |  |  | 31 |  * @package    core_backup
 | 
        
           |  |  | 32 |  * @copyright  2020 onward The Moodle Users Association <https://moodleassociation.org/>
 | 
        
           |  |  | 33 |  * @author     Matt Porritt <mattp@catalyst-au.net>
 | 
        
           |  |  | 34 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 35 |  * @coversDefaultClass \copy_helper
 | 
        
           |  |  | 36 |  */
 | 
        
           |  |  | 37 | class copy_helper_test extends \advanced_testcase {
 | 
        
           |  |  | 38 |   | 
        
           |  |  | 39 |     /**
 | 
        
           |  |  | 40 |      *
 | 
        
           |  |  | 41 |      * @var \stdClass Course used for testing.
 | 
        
           |  |  | 42 |      */
 | 
        
           |  |  | 43 |     protected $course;
 | 
        
           |  |  | 44 |   | 
        
           |  |  | 45 |     /**
 | 
        
           |  |  | 46 |      *
 | 
        
           |  |  | 47 |      * @var int User used to perform backups.
 | 
        
           |  |  | 48 |      */
 | 
        
           |  |  | 49 |     protected $userid;
 | 
        
           |  |  | 50 |   | 
        
           |  |  | 51 |     /**
 | 
        
           |  |  | 52 |      *
 | 
        
           |  |  | 53 |      * @var array Ids of users in test course.
 | 
        
           |  |  | 54 |      */
 | 
        
           |  |  | 55 |     protected $courseusers;
 | 
        
           |  |  | 56 |   | 
        
           |  |  | 57 |     /**
 | 
        
           |  |  | 58 |      *
 | 
        
           |  |  | 59 |      * @var array Names of the created activities.
 | 
        
           |  |  | 60 |      */
 | 
        
           |  |  | 61 |     protected $activitynames;
 | 
        
           |  |  | 62 |   | 
        
           |  |  | 63 |     /**
 | 
        
           |  |  | 64 |      * Set up tasks for all tests.
 | 
        
           |  |  | 65 |      */
 | 
        
           |  |  | 66 |     protected function setUp(): void {
 | 
        
           |  |  | 67 |         global $DB, $CFG, $USER;
 | 
        
           |  |  | 68 |   | 
        
           |  |  | 69 |         $this->resetAfterTest(true);
 | 
        
           |  |  | 70 |   | 
        
           |  |  | 71 |         $CFG->enableavailability = true;
 | 
        
           |  |  | 72 |         $CFG->enablecompletion = true;
 | 
        
           |  |  | 73 |   | 
        
           |  |  | 74 |         // Create a course with some availability data set.
 | 
        
           |  |  | 75 |         $generator = $this->getDataGenerator();
 | 
        
           |  |  | 76 |         $course = $generator->create_course(
 | 
        
           |  |  | 77 |             array('format' => 'topics', 'numsections' => 3,
 | 
        
           |  |  | 78 |                 'enablecompletion' => COMPLETION_ENABLED),
 | 
        
           |  |  | 79 |             array('createsections' => true));
 | 
        
           |  |  | 80 |         $forum = $generator->create_module('forum', array(
 | 
        
           |  |  | 81 |             'course' => $course->id));
 | 
        
           |  |  | 82 |         $forum2 = $generator->create_module('forum', array(
 | 
        
           |  |  | 83 |             'course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL));
 | 
        
           |  |  | 84 |   | 
        
           |  |  | 85 |         // We need a grade, easiest is to add an assignment.
 | 
        
           |  |  | 86 |         $assignrow = $generator->create_module('assign', array(
 | 
        
           |  |  | 87 |             'course' => $course->id));
 | 
        
           |  |  | 88 |         $assign = new \assign(\context_module::instance($assignrow->cmid), false, false);
 | 
        
           |  |  | 89 |         $item = $assign->get_grade_item();
 | 
        
           |  |  | 90 |   | 
        
           |  |  | 91 |         // Make a test grouping as well.
 | 
        
           |  |  | 92 |         $grouping = $generator->create_grouping(array('courseid' => $course->id,
 | 
        
           |  |  | 93 |             'name' => 'Grouping!'));
 | 
        
           |  |  | 94 |   | 
        
           |  |  | 95 |         // Create some users.
 | 
        
           |  |  | 96 |         $user1 = $generator->create_user();
 | 
        
           |  |  | 97 |         $user2 = $generator->create_user();
 | 
        
           |  |  | 98 |         $user3 = $generator->create_user();
 | 
        
           |  |  | 99 |         $user4 = $generator->create_user();
 | 
        
           |  |  | 100 |         $this->courseusers = array(
 | 
        
           |  |  | 101 |             $user1->id, $user2->id, $user3->id, $user4->id
 | 
        
           |  |  | 102 |         );
 | 
        
           |  |  | 103 |   | 
        
           |  |  | 104 |         // Enrol users into the course.
 | 
        
           |  |  | 105 |         $generator->enrol_user($user1->id, $course->id, 'student');
 | 
        
           |  |  | 106 |         $generator->enrol_user($user2->id, $course->id, 'editingteacher');
 | 
        
           |  |  | 107 |         $generator->enrol_user($user3->id, $course->id, 'manager');
 | 
        
           |  |  | 108 |         $generator->enrol_user($user4->id, $course->id, 'editingteacher');
 | 
        
           |  |  | 109 |         $generator->enrol_user($user4->id, $course->id, 'manager');
 | 
        
           |  |  | 110 |   | 
        
           |  |  | 111 |         $availability = '{"op":"|","show":false,"c":[' .
 | 
        
           |  |  | 112 |             '{"type":"completion","cm":' . $forum2->cmid .',"e":1},' .
 | 
        
           |  |  | 113 |             '{"type":"grade","id":' . $item->id . ',"min":4,"max":94},' .
 | 
        
           |  |  | 114 |             '{"type":"grouping","id":' . $grouping->id . '}' .
 | 
        
           |  |  | 115 |             ']}';
 | 
        
           |  |  | 116 |         $DB->set_field('course_modules', 'availability', $availability, array(
 | 
        
           |  |  | 117 |             'id' => $forum->cmid));
 | 
        
           |  |  | 118 |         $DB->set_field('course_sections', 'availability', $availability, array(
 | 
        
           |  |  | 119 |             'course' => $course->id, 'section' => 1));
 | 
        
           |  |  | 120 |   | 
        
           |  |  | 121 |         // Add some user data to the course.
 | 
        
           |  |  | 122 |         $discussion = $generator->get_plugin_generator('mod_forum')->create_discussion(['course' => $course->id,
 | 
        
           |  |  | 123 |             'forum' => $forum->id, 'userid' => $user1->id, 'timemodified' => time(),
 | 
        
           |  |  | 124 |             'name' => 'Frog']);
 | 
        
           |  |  | 125 |         $generator->get_plugin_generator('mod_forum')->create_post(['discussion' => $discussion->id, 'userid' => $user1->id]);
 | 
        
           |  |  | 126 |   | 
        
           |  |  | 127 |         $this->course  = $course;
 | 
        
           |  |  | 128 |         $this->userid = $USER->id; // Admin.
 | 
        
           |  |  | 129 |         $this->activitynames = array(
 | 
        
           |  |  | 130 |             $forum->name,
 | 
        
           |  |  | 131 |             $forum2->name,
 | 
        
           |  |  | 132 |             $assignrow->name
 | 
        
           |  |  | 133 |         );
 | 
        
           |  |  | 134 |   | 
        
           |  |  | 135 |         // Set the user doing the backup to be a manager in the course.
 | 
        
           |  |  | 136 |         // By default Managers can restore courses AND users, teachers can only do users.
 | 
        
           |  |  | 137 |         $this->setUser($user3);
 | 
        
           |  |  | 138 |   | 
        
           |  |  | 139 |         // Disable all loggers.
 | 
        
           |  |  | 140 |         $CFG->backup_error_log_logger_level = backup::LOG_NONE;
 | 
        
           |  |  | 141 |         $CFG->backup_output_indented_logger_level = backup::LOG_NONE;
 | 
        
           |  |  | 142 |         $CFG->backup_file_logger_level = backup::LOG_NONE;
 | 
        
           |  |  | 143 |         $CFG->backup_database_logger_level = backup::LOG_NONE;
 | 
        
           |  |  | 144 |         $CFG->backup_file_logger_level_extra = backup::LOG_NONE;
 | 
        
           |  |  | 145 |     }
 | 
        
           |  |  | 146 |   | 
        
           |  |  | 147 |     /**
 | 
        
           |  |  | 148 |      * Test process form data with invalid data.
 | 
        
           |  |  | 149 |      *
 | 
        
           |  |  | 150 |      * @covers ::process_formdata
 | 
        
           |  |  | 151 |      */
 | 
        
           | 11 | efrain | 152 |     public function test_process_formdata_missing_fields(): void {
 | 
        
           | 1 | efrain | 153 |         $this->expectException(\moodle_exception::class);
 | 
        
           |  |  | 154 |         \copy_helper::process_formdata(new \stdClass);
 | 
        
           |  |  | 155 |     }
 | 
        
           |  |  | 156 |   | 
        
           |  |  | 157 |     /**
 | 
        
           |  |  | 158 |      * Test processing form data.
 | 
        
           |  |  | 159 |      *
 | 
        
           |  |  | 160 |      * @covers ::process_formdata
 | 
        
           |  |  | 161 |      */
 | 
        
           | 11 | efrain | 162 |     public function test_process_formdata(): void {
 | 
        
           | 1 | efrain | 163 |         $validformdata = [
 | 
        
           |  |  | 164 |             'courseid' => 1729,
 | 
        
           |  |  | 165 |             'fullname' => 'Taxicab Numbers',
 | 
        
           |  |  | 166 |             'shortname' => 'Taxi101',
 | 
        
           |  |  | 167 |             'category' => 2,
 | 
        
           |  |  | 168 |             'visible' => 1,
 | 
        
           |  |  | 169 |             'startdate' => 87539319,
 | 
        
           |  |  | 170 |             'enddate' => 6963472309248,
 | 
        
           |  |  | 171 |             'idnumber' => 1730,
 | 
        
           |  |  | 172 |             'userdata' => 1
 | 
        
           |  |  | 173 |         ];
 | 
        
           |  |  | 174 |   | 
        
           |  |  | 175 |         $roles = [
 | 
        
           |  |  | 176 |             'role_one' => 1,
 | 
        
           |  |  | 177 |             'role_two' => 2,
 | 
        
           |  |  | 178 |             'role_three' => 0
 | 
        
           |  |  | 179 |         ];
 | 
        
           |  |  | 180 |   | 
        
           |  |  | 181 |         $expected = (object)array_merge($validformdata, ['keptroles' => []]);
 | 
        
           |  |  | 182 |         $expected->keptroles = [1, 2];
 | 
        
           |  |  | 183 |         $processed = \copy_helper::process_formdata(
 | 
        
           |  |  | 184 |             (object)array_merge(
 | 
        
           |  |  | 185 |                 $validformdata,
 | 
        
           |  |  | 186 |                 $roles,
 | 
        
           |  |  | 187 |                 ['extra' => 'stuff', 'remove' => 'this'])
 | 
        
           |  |  | 188 |         );
 | 
        
           |  |  | 189 |   | 
        
           |  |  | 190 |         $this->assertEquals($expected, $processed);
 | 
        
           |  |  | 191 |     }
 | 
        
           |  |  | 192 |   | 
        
           |  |  | 193 |     /**
 | 
        
           |  |  | 194 |      * Test orphaned controller cleanup.
 | 
        
           |  |  | 195 |      *
 | 
        
           |  |  | 196 |      * @covers ::cleanup_orphaned_copy_controllers
 | 
        
           |  |  | 197 |      */
 | 
        
           | 11 | efrain | 198 |     public function test_cleanup_orphaned_copy_controllers(): void {
 | 
        
           | 1 | efrain | 199 |         global $DB;
 | 
        
           |  |  | 200 |   | 
        
           |  |  | 201 |         // Mock up the form data.
 | 
        
           |  |  | 202 |         $formdata = new \stdClass;
 | 
        
           |  |  | 203 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 204 |         $formdata->fullname = 'foo';
 | 
        
           |  |  | 205 |         $formdata->shortname = 'data1';
 | 
        
           |  |  | 206 |         $formdata->category = 1;
 | 
        
           |  |  | 207 |         $formdata->visible = 1;
 | 
        
           |  |  | 208 |         $formdata->startdate = 1582376400;
 | 
        
           |  |  | 209 |         $formdata->enddate = 0;
 | 
        
           |  |  | 210 |         $formdata->idnumber = 123;
 | 
        
           |  |  | 211 |         $formdata->userdata = 1;
 | 
        
           |  |  | 212 |         $formdata->role_1 = 1;
 | 
        
           |  |  | 213 |         $formdata->role_3 = 3;
 | 
        
           |  |  | 214 |         $formdata->role_5 = 5;
 | 
        
           |  |  | 215 |   | 
        
           |  |  | 216 |         $copies = [];
 | 
        
           |  |  | 217 |         for ($i = 0; $i < 5; $i++) {
 | 
        
           |  |  | 218 |             $formdata->shortname = 'data' . $i;
 | 
        
           |  |  | 219 |             $copies[] = \copy_helper::create_copy($formdata);
 | 
        
           |  |  | 220 |         }
 | 
        
           |  |  | 221 |   | 
        
           |  |  | 222 |         // Delete one of the restore controllers. Simulates a situation where copy creation
 | 
        
           |  |  | 223 |         // interrupted and the restore controller never gets created.
 | 
        
           |  |  | 224 |         $DB->delete_records('backup_controllers', ['backupid' => $copies[0]['restoreid']]);
 | 
        
           |  |  | 225 |   | 
        
           |  |  | 226 |         // Set a backup/restore controller pair to be in an intermediate state.
 | 
        
           |  |  | 227 |         \backup_controller::load_controller($copies[2]['backupid'])->set_status(backup::STATUS_FINISHED_OK);
 | 
        
           |  |  | 228 |   | 
        
           |  |  | 229 |         // Set a backup/restore controller pair to completed.
 | 
        
           |  |  | 230 |         \backup_controller::load_controller($copies[3]['backupid'])->set_status(backup::STATUS_FINISHED_OK);
 | 
        
           |  |  | 231 |         \restore_controller::load_controller($copies[3]['restoreid'])->set_status(backup::STATUS_FINISHED_OK);
 | 
        
           |  |  | 232 |   | 
        
           |  |  | 233 |         // Set a backup/restore controller pair to have a failed backup.
 | 
        
           |  |  | 234 |         \backup_controller::load_controller($copies[4]['backupid'])->set_status(backup::STATUS_FINISHED_ERR);
 | 
        
           |  |  | 235 |   | 
        
           |  |  | 236 |         // Create some backup/restore controllers that are unrelated to course copies.
 | 
        
           |  |  | 237 |         $bc = new \backup_controller(backup::TYPE_1COURSE, 1, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_ASYNC,
 | 
        
           |  |  | 238 |                 2, backup::RELEASESESSION_YES);
 | 
        
           |  |  | 239 |         $rc = new \restore_controller('restore-test-1729', 1, backup::INTERACTIVE_NO, backup::MODE_ASYNC, 1, 2);
 | 
        
           |  |  | 240 |         $rc->save_controller();
 | 
        
           |  |  | 241 |         $unrelatedvanillacontrollers = ['backupid' => $bc->get_backupid(), 'restoreid' => $rc->get_restoreid()];
 | 
        
           |  |  | 242 |   | 
        
           |  |  | 243 |         $bc = new \backup_controller(backup::TYPE_1COURSE, 1, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_ASYNC,
 | 
        
           |  |  | 244 |                 2, backup::RELEASESESSION_YES);
 | 
        
           |  |  | 245 |         $rc = new \restore_controller('restore-test-1729', 1, backup::INTERACTIVE_NO, backup::MODE_ASYNC, 1, 2);
 | 
        
           |  |  | 246 |         $bc->set_status(backup::STATUS_FINISHED_OK);
 | 
        
           |  |  | 247 |         $rc->set_status(backup::STATUS_FINISHED_OK);
 | 
        
           |  |  | 248 |         $unrelatedfinishedcontrollers = ['backupid' => $bc->get_backupid(), 'restoreid' => $rc->get_restoreid()];
 | 
        
           |  |  | 249 |   | 
        
           |  |  | 250 |         $bc = new \backup_controller(backup::TYPE_1COURSE, 1, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_ASYNC,
 | 
        
           |  |  | 251 |                 2, backup::RELEASESESSION_YES);
 | 
        
           |  |  | 252 |         $rc = new \restore_controller('restore-test-1729', 1, backup::INTERACTIVE_NO, backup::MODE_ASYNC, 1, 2);
 | 
        
           |  |  | 253 |         $bc->set_status(backup::STATUS_FINISHED_ERR);
 | 
        
           |  |  | 254 |         $rc->set_status(backup::STATUS_FINISHED_ERR);
 | 
        
           |  |  | 255 |         $unrelatedfailedcontrollers = ['backupid' => $bc->get_backupid(), 'restoreid' => $rc->get_restoreid()];
 | 
        
           |  |  | 256 |   | 
        
           |  |  | 257 |         // Clean up the backup_controllers table.
 | 
        
           |  |  | 258 |         $records = $DB->get_records('backup_controllers', null, '', 'id, backupid, status, operation, purpose, timecreated');
 | 
        
           |  |  | 259 |         \copy_helper::cleanup_orphaned_copy_controllers($records, 0);
 | 
        
           |  |  | 260 |   | 
        
           |  |  | 261 |         // Retrieve them again and check.
 | 
        
           |  |  | 262 |         $records = $DB->get_records('backup_controllers', null, '', 'backupid, status');
 | 
        
           |  |  | 263 |   | 
        
           |  |  | 264 |         // Verify the backup associated with the deleted restore is marked as failed.
 | 
        
           |  |  | 265 |         $this->assertEquals(backup::STATUS_FINISHED_ERR, $records[$copies[0]['backupid']]->status);
 | 
        
           |  |  | 266 |   | 
        
           |  |  | 267 |         // Verify other controllers remain untouched.
 | 
        
           |  |  | 268 |         $this->assertEquals(backup::STATUS_AWAITING, $records[$copies[1]['backupid']]->status);
 | 
        
           |  |  | 269 |         $this->assertEquals(backup::STATUS_REQUIRE_CONV, $records[$copies[1]['restoreid']]->status);
 | 
        
           |  |  | 270 |         $this->assertEquals(backup::STATUS_FINISHED_OK, $records[$copies[2]['backupid']]->status);
 | 
        
           |  |  | 271 |         $this->assertEquals(backup::STATUS_REQUIRE_CONV, $records[$copies[2]['restoreid']]->status);
 | 
        
           |  |  | 272 |         $this->assertEquals(backup::STATUS_FINISHED_OK, $records[$copies[3]['restoreid']]->status);
 | 
        
           |  |  | 273 |         $this->assertEquals(backup::STATUS_FINISHED_OK, $records[$copies[3]['backupid']]->status);
 | 
        
           |  |  | 274 |   | 
        
           |  |  | 275 |         // Verify that the restore associated with the failed backup is also marked as failed.
 | 
        
           |  |  | 276 |         $this->assertEquals(backup::STATUS_FINISHED_ERR, $records[$copies[4]['restoreid']]->status);
 | 
        
           |  |  | 277 |   | 
        
           |  |  | 278 |         // Verify that the unrelated controllers remain unchanged.
 | 
        
           |  |  | 279 |         $this->assertEquals(backup::STATUS_AWAITING, $records[$unrelatedvanillacontrollers['backupid']]->status);
 | 
        
           |  |  | 280 |         $this->assertEquals(backup::STATUS_REQUIRE_CONV, $records[$unrelatedvanillacontrollers['restoreid']]->status);
 | 
        
           |  |  | 281 |         $this->assertEquals(backup::STATUS_FINISHED_OK, $records[$unrelatedfinishedcontrollers['backupid']]->status);
 | 
        
           |  |  | 282 |         $this->assertEquals(backup::STATUS_FINISHED_OK, $records[$unrelatedfinishedcontrollers['restoreid']]->status);
 | 
        
           |  |  | 283 |         $this->assertEquals(backup::STATUS_FINISHED_ERR, $records[$unrelatedfailedcontrollers['backupid']]->status);
 | 
        
           |  |  | 284 |         $this->assertEquals(backup::STATUS_FINISHED_ERR, $records[$unrelatedfailedcontrollers['restoreid']]->status);
 | 
        
           |  |  | 285 |     }
 | 
        
           |  |  | 286 |   | 
        
           |  |  | 287 |     /**
 | 
        
           |  |  | 288 |      * Test creating a course copy.
 | 
        
           |  |  | 289 |      *
 | 
        
           |  |  | 290 |      * @covers ::create_copy
 | 
        
           |  |  | 291 |      */
 | 
        
           | 11 | efrain | 292 |     public function test_create_copy(): void {
 | 
        
           | 1 | efrain | 293 |   | 
        
           |  |  | 294 |         // Mock up the form data.
 | 
        
           |  |  | 295 |         $formdata = new \stdClass;
 | 
        
           |  |  | 296 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 297 |         $formdata->fullname = 'foo';
 | 
        
           |  |  | 298 |         $formdata->shortname = 'bar';
 | 
        
           |  |  | 299 |         $formdata->category = 1;
 | 
        
           |  |  | 300 |         $formdata->visible = 1;
 | 
        
           |  |  | 301 |         $formdata->startdate = 1582376400;
 | 
        
           |  |  | 302 |         $formdata->enddate = 0;
 | 
        
           |  |  | 303 |         $formdata->idnumber = 123;
 | 
        
           |  |  | 304 |         $formdata->userdata = 1;
 | 
        
           |  |  | 305 |         $formdata->role_1 = 1;
 | 
        
           |  |  | 306 |         $formdata->role_3 = 3;
 | 
        
           |  |  | 307 |         $formdata->role_5 = 5;
 | 
        
           |  |  | 308 |   | 
        
           |  |  | 309 |         $copydata = \copy_helper::process_formdata($formdata);
 | 
        
           |  |  | 310 |         $result = \copy_helper::create_copy($copydata);
 | 
        
           |  |  | 311 |   | 
        
           |  |  | 312 |         // Load the controllers, to extract the data we need.
 | 
        
           |  |  | 313 |         $bc = \backup_controller::load_controller($result['backupid']);
 | 
        
           |  |  | 314 |         $rc = \restore_controller::load_controller($result['restoreid']);
 | 
        
           |  |  | 315 |   | 
        
           |  |  | 316 |         // Check the backup controller.
 | 
        
           |  |  | 317 |         $this->assertEquals(backup::MODE_COPY, $bc->get_mode());
 | 
        
           |  |  | 318 |         $this->assertEquals($this->course->id, $bc->get_courseid());
 | 
        
           |  |  | 319 |         $this->assertEquals(backup::TYPE_1COURSE, $bc->get_type());
 | 
        
           |  |  | 320 |   | 
        
           |  |  | 321 |         // Check the restore controller.
 | 
        
           |  |  | 322 |         $newcourseid = $rc->get_courseid();
 | 
        
           |  |  | 323 |         $newcourse = get_course($newcourseid);
 | 
        
           |  |  | 324 |   | 
        
           |  |  | 325 |         $this->assertEquals(get_string('copyingcourse', 'backup'), $newcourse->fullname);
 | 
        
           |  |  | 326 |         $this->assertEquals(get_string('copyingcourseshortname', 'backup'), $newcourse->shortname);
 | 
        
           |  |  | 327 |         $this->assertEquals(backup::MODE_COPY, $rc->get_mode());
 | 
        
           |  |  | 328 |         $this->assertEquals($newcourseid, $rc->get_courseid());
 | 
        
           |  |  | 329 |   | 
        
           |  |  | 330 |         // Check the created ad-hoc task.
 | 
        
           |  |  | 331 |         $now = time();
 | 
        
           |  |  | 332 |         $task = \core\task\manager::get_next_adhoc_task($now);
 | 
        
           |  |  | 333 |   | 
        
           |  |  | 334 |         $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
 | 
        
           |  |  | 335 |         $this->assertEquals($result, (array)$task->get_custom_data());
 | 
        
           |  |  | 336 |   | 
        
           |  |  | 337 |         \core\task\manager::adhoc_task_complete($task);
 | 
        
           |  |  | 338 |     }
 | 
        
           |  |  | 339 |   | 
        
           |  |  | 340 |     /**
 | 
        
           |  |  | 341 |      * Test getting the current copies.
 | 
        
           |  |  | 342 |      *
 | 
        
           |  |  | 343 |      * @covers ::get_copies
 | 
        
           |  |  | 344 |      */
 | 
        
           | 11 | efrain | 345 |     public function test_get_copies(): void {
 | 
        
           | 1 | efrain | 346 |         global $USER;
 | 
        
           |  |  | 347 |   | 
        
           |  |  | 348 |         // Mock up the form data.
 | 
        
           |  |  | 349 |         $formdata = new \stdClass;
 | 
        
           |  |  | 350 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 351 |         $formdata->fullname = 'foo';
 | 
        
           |  |  | 352 |         $formdata->shortname = 'bar';
 | 
        
           |  |  | 353 |         $formdata->category = 1;
 | 
        
           |  |  | 354 |         $formdata->visible = 1;
 | 
        
           |  |  | 355 |         $formdata->startdate = 1582376400;
 | 
        
           |  |  | 356 |         $formdata->enddate = 0;
 | 
        
           |  |  | 357 |         $formdata->idnumber = '';
 | 
        
           |  |  | 358 |         $formdata->userdata = 1;
 | 
        
           |  |  | 359 |         $formdata->role_1 = 1;
 | 
        
           |  |  | 360 |         $formdata->role_3 = 3;
 | 
        
           |  |  | 361 |         $formdata->role_5 = 5;
 | 
        
           |  |  | 362 |   | 
        
           |  |  | 363 |         $formdata2 = clone($formdata);
 | 
        
           |  |  | 364 |         $formdata2->shortname = 'tree';
 | 
        
           |  |  | 365 |   | 
        
           |  |  | 366 |         // Create some copies.
 | 
        
           |  |  | 367 |         $copydata = \copy_helper::process_formdata($formdata);
 | 
        
           |  |  | 368 |         $result = \copy_helper::create_copy($copydata);
 | 
        
           |  |  | 369 |   | 
        
           |  |  | 370 |         // Backup, awaiting.
 | 
        
           |  |  | 371 |         $copies = \copy_helper::get_copies($USER->id);
 | 
        
           |  |  | 372 |         $this->assertEquals($result['backupid'], $copies[0]->backupid);
 | 
        
           |  |  | 373 |         $this->assertEquals($result['restoreid'], $copies[0]->restoreid);
 | 
        
           |  |  | 374 |         $this->assertEquals(\backup::STATUS_AWAITING, $copies[0]->status);
 | 
        
           |  |  | 375 |         $this->assertEquals(\backup::OPERATION_BACKUP, $copies[0]->operation);
 | 
        
           |  |  | 376 |   | 
        
           |  |  | 377 |         $bc = \backup_controller::load_controller($result['backupid']);
 | 
        
           |  |  | 378 |   | 
        
           |  |  | 379 |         // Backup, in progress.
 | 
        
           |  |  | 380 |         $bc->set_status(\backup::STATUS_EXECUTING);
 | 
        
           |  |  | 381 |         $copies = \copy_helper::get_copies($USER->id);
 | 
        
           |  |  | 382 |         $this->assertEquals($result['backupid'], $copies[0]->backupid);
 | 
        
           |  |  | 383 |         $this->assertEquals($result['restoreid'], $copies[0]->restoreid);
 | 
        
           |  |  | 384 |         $this->assertEquals(\backup::STATUS_EXECUTING, $copies[0]->status);
 | 
        
           |  |  | 385 |         $this->assertEquals(\backup::OPERATION_BACKUP, $copies[0]->operation);
 | 
        
           |  |  | 386 |   | 
        
           |  |  | 387 |         // Restore, ready to process.
 | 
        
           |  |  | 388 |         $bc->set_status(\backup::STATUS_FINISHED_OK);
 | 
        
           |  |  | 389 |         $copies = \copy_helper::get_copies($USER->id);
 | 
        
           |  |  | 390 |         $this->assertEquals(null, $copies[0]->backupid);
 | 
        
           |  |  | 391 |         $this->assertEquals($result['restoreid'], $copies[0]->restoreid);
 | 
        
           |  |  | 392 |         $this->assertEquals(\backup::STATUS_REQUIRE_CONV, $copies[0]->status);
 | 
        
           |  |  | 393 |         $this->assertEquals(\backup::OPERATION_RESTORE, $copies[0]->operation);
 | 
        
           |  |  | 394 |   | 
        
           |  |  | 395 |         // No records.
 | 
        
           |  |  | 396 |         $bc->set_status(\backup::STATUS_FINISHED_ERR);
 | 
        
           |  |  | 397 |         $copies = \copy_helper::get_copies($USER->id);
 | 
        
           |  |  | 398 |         $this->assertEmpty($copies);
 | 
        
           |  |  | 399 |   | 
        
           |  |  | 400 |         $copydata2 = \copy_helper::process_formdata($formdata2);
 | 
        
           |  |  | 401 |         $result2 = \copy_helper::create_copy($copydata2);
 | 
        
           |  |  | 402 |         // Set the second copy to be complete.
 | 
        
           |  |  | 403 |         $bc = \backup_controller::load_controller($result2['backupid']);
 | 
        
           |  |  | 404 |         $bc->set_status(\backup::STATUS_FINISHED_OK);
 | 
        
           |  |  | 405 |         // Set the restore to be finished.
 | 
        
           |  |  | 406 |         $rc = \backup_controller::load_controller($result2['restoreid']);
 | 
        
           |  |  | 407 |         $rc->set_status(\backup::STATUS_FINISHED_OK);
 | 
        
           |  |  | 408 |   | 
        
           |  |  | 409 |         // No records.
 | 
        
           |  |  | 410 |         $copies = \copy_helper::get_copies($USER->id);
 | 
        
           |  |  | 411 |         $this->assertEmpty($copies);
 | 
        
           |  |  | 412 |     }
 | 
        
           |  |  | 413 |   | 
        
           |  |  | 414 |     /**
 | 
        
           |  |  | 415 |      * Test getting the current copies when they are in an invalid state.
 | 
        
           |  |  | 416 |      *
 | 
        
           |  |  | 417 |      * @covers ::get_copies
 | 
        
           |  |  | 418 |      */
 | 
        
           | 11 | efrain | 419 |     public function test_get_copies_invalid_state(): void {
 | 
        
           | 1 | efrain | 420 |         global $DB, $USER;
 | 
        
           |  |  | 421 |   | 
        
           |  |  | 422 |         // Mock up the form data.
 | 
        
           |  |  | 423 |         $formdata = new \stdClass;
 | 
        
           |  |  | 424 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 425 |         $formdata->fullname = 'foo';
 | 
        
           |  |  | 426 |         $formdata->shortname = 'bar';
 | 
        
           |  |  | 427 |         $formdata->category = 1;
 | 
        
           |  |  | 428 |         $formdata->visible = 1;
 | 
        
           |  |  | 429 |         $formdata->startdate = 1582376400;
 | 
        
           |  |  | 430 |         $formdata->enddate = 0;
 | 
        
           |  |  | 431 |         $formdata->idnumber = '';
 | 
        
           |  |  | 432 |         $formdata->userdata = 1;
 | 
        
           |  |  | 433 |         $formdata->role_1 = 1;
 | 
        
           |  |  | 434 |         $formdata->role_3 = 3;
 | 
        
           |  |  | 435 |         $formdata->role_5 = 5;
 | 
        
           |  |  | 436 |   | 
        
           |  |  | 437 |         $formdata2 = clone ($formdata);
 | 
        
           |  |  | 438 |         $formdata2->shortname = 'tree';
 | 
        
           |  |  | 439 |   | 
        
           |  |  | 440 |         // Create some copies.
 | 
        
           |  |  | 441 |         $copydata = \copy_helper::process_formdata($formdata);
 | 
        
           |  |  | 442 |         $result = \copy_helper::create_copy($copydata);
 | 
        
           |  |  | 443 |         $copydata2 = \copy_helper::process_formdata($formdata2);
 | 
        
           |  |  | 444 |         $result2 = \copy_helper::create_copy($copydata2);
 | 
        
           |  |  | 445 |   | 
        
           |  |  | 446 |         $copies = \copy_helper::get_copies($USER->id);
 | 
        
           |  |  | 447 |   | 
        
           |  |  | 448 |         // Verify get_copies gives back both backup controllers.
 | 
        
           |  |  | 449 |         $this->assertEqualsCanonicalizing([$result['backupid'], $result2['backupid']], array_column($copies, 'backupid'));
 | 
        
           |  |  | 450 |   | 
        
           |  |  | 451 |         // Set one of the backup controllers to failed, this should cause it to not be present.
 | 
        
           |  |  | 452 |         \backup_controller::load_controller($result['backupid'])->set_status(backup::STATUS_FINISHED_ERR);
 | 
        
           |  |  | 453 |         $copies = \copy_helper::get_copies($USER->id);
 | 
        
           |  |  | 454 |   | 
        
           |  |  | 455 |         // Verify there is only one backup listed, and that it is not the failed one.
 | 
        
           |  |  | 456 |         $this->assertEqualsCanonicalizing([$result2['backupid']], array_column($copies, 'backupid'));
 | 
        
           |  |  | 457 |   | 
        
           |  |  | 458 |         // Set the controller back to awaiting.
 | 
        
           |  |  | 459 |         \backup_controller::load_controller($result['backupid'])->set_status(backup::STATUS_AWAITING);
 | 
        
           |  |  | 460 |         $copies = \copy_helper::get_copies($USER->id);
 | 
        
           |  |  | 461 |   | 
        
           |  |  | 462 |         // Verify both backup controllers are back.
 | 
        
           |  |  | 463 |         $this->assertEqualsCanonicalizing([$result['backupid'], $result2['backupid']], array_column($copies, 'backupid'));
 | 
        
           |  |  | 464 |   | 
        
           |  |  | 465 |         // Delete the restore controller for one of the copies, this should cause it to not be present.
 | 
        
           |  |  | 466 |         $DB->delete_records('backup_controllers', ['backupid' => $result['restoreid']]);
 | 
        
           |  |  | 467 |         $copies = \copy_helper::get_copies($USER->id);
 | 
        
           |  |  | 468 |   | 
        
           |  |  | 469 |         // Verify there is only one backup listed, and that it is not the failed one.
 | 
        
           |  |  | 470 |         $this->assertEqualsCanonicalizing([$result2['backupid']], array_column($copies, 'backupid'));
 | 
        
           |  |  | 471 |     }
 | 
        
           |  |  | 472 |   | 
        
           |  |  | 473 |     /**
 | 
        
           |  |  | 474 |      * Test getting the current copies for specific course.
 | 
        
           |  |  | 475 |      *
 | 
        
           |  |  | 476 |      * @covers ::get_copies
 | 
        
           |  |  | 477 |      */
 | 
        
           | 11 | efrain | 478 |     public function test_get_copies_course(): void {
 | 
        
           | 1 | efrain | 479 |         global $USER;
 | 
        
           |  |  | 480 |   | 
        
           |  |  | 481 |         // Mock up the form data.
 | 
        
           |  |  | 482 |         $formdata = new \stdClass;
 | 
        
           |  |  | 483 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 484 |         $formdata->fullname = 'foo';
 | 
        
           |  |  | 485 |         $formdata->shortname = 'bar';
 | 
        
           |  |  | 486 |         $formdata->category = 1;
 | 
        
           |  |  | 487 |         $formdata->visible = 1;
 | 
        
           |  |  | 488 |         $formdata->startdate = 1582376400;
 | 
        
           |  |  | 489 |         $formdata->enddate = 0;
 | 
        
           |  |  | 490 |         $formdata->idnumber = '';
 | 
        
           |  |  | 491 |         $formdata->userdata = 1;
 | 
        
           |  |  | 492 |         $formdata->role_1 = 1;
 | 
        
           |  |  | 493 |         $formdata->role_3 = 3;
 | 
        
           |  |  | 494 |         $formdata->role_5 = 5;
 | 
        
           |  |  | 495 |   | 
        
           |  |  | 496 |         // Create some copies.
 | 
        
           |  |  | 497 |         $copydata = \copy_helper::process_formdata($formdata);
 | 
        
           |  |  | 498 |         \copy_helper::create_copy($copydata);
 | 
        
           |  |  | 499 |   | 
        
           |  |  | 500 |         // No copies match this course id.
 | 
        
           |  |  | 501 |         $copies = \copy_helper::get_copies($USER->id, ($this->course->id + 1));
 | 
        
           |  |  | 502 |         $this->assertEmpty($copies);
 | 
        
           |  |  | 503 |     }
 | 
        
           |  |  | 504 |   | 
        
           |  |  | 505 |     /**
 | 
        
           |  |  | 506 |      * Test getting the current copies if course has been deleted.
 | 
        
           |  |  | 507 |      *
 | 
        
           |  |  | 508 |      * @covers ::get_copies
 | 
        
           |  |  | 509 |      */
 | 
        
           | 11 | efrain | 510 |     public function test_get_copies_course_deleted(): void {
 | 
        
           | 1 | efrain | 511 |         global $USER;
 | 
        
           |  |  | 512 |   | 
        
           |  |  | 513 |         // Mock up the form data.
 | 
        
           |  |  | 514 |         $formdata = new \stdClass;
 | 
        
           |  |  | 515 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 516 |         $formdata->fullname = 'foo';
 | 
        
           |  |  | 517 |         $formdata->shortname = 'bar';
 | 
        
           |  |  | 518 |         $formdata->category = 1;
 | 
        
           |  |  | 519 |         $formdata->visible = 1;
 | 
        
           |  |  | 520 |         $formdata->startdate = 1582376400;
 | 
        
           |  |  | 521 |         $formdata->enddate = 0;
 | 
        
           |  |  | 522 |         $formdata->idnumber = '';
 | 
        
           |  |  | 523 |         $formdata->userdata = 1;
 | 
        
           |  |  | 524 |         $formdata->role_1 = 1;
 | 
        
           |  |  | 525 |         $formdata->role_3 = 3;
 | 
        
           |  |  | 526 |         $formdata->role_5 = 5;
 | 
        
           |  |  | 527 |   | 
        
           |  |  | 528 |         // Create some copies.
 | 
        
           |  |  | 529 |         $copydata = \copy_helper::process_formdata($formdata);
 | 
        
           |  |  | 530 |         \copy_helper::create_copy($copydata);
 | 
        
           |  |  | 531 |   | 
        
           |  |  | 532 |         delete_course($this->course->id, false);
 | 
        
           |  |  | 533 |   | 
        
           |  |  | 534 |         // No copies match this course id as it has been deleted.
 | 
        
           |  |  | 535 |         $copies = \copy_helper::get_copies($USER->id, ($this->course->id));
 | 
        
           |  |  | 536 |         $this->assertEmpty($copies);
 | 
        
           |  |  | 537 |     }
 | 
        
           |  |  | 538 |   | 
        
           |  |  | 539 |     /**
 | 
        
           |  |  | 540 |      * Test course copy.
 | 
        
           |  |  | 541 |      */
 | 
        
           | 11 | efrain | 542 |     public function test_course_copy(): void {
 | 
        
           | 1 | efrain | 543 |         global $DB;
 | 
        
           |  |  | 544 |   | 
        
           |  |  | 545 |         // Mock up the form data.
 | 
        
           |  |  | 546 |         $formdata = new \stdClass;
 | 
        
           |  |  | 547 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 548 |         $formdata->fullname = 'copy course';
 | 
        
           |  |  | 549 |         $formdata->shortname = 'copy course short';
 | 
        
           |  |  | 550 |         $formdata->category = 1;
 | 
        
           |  |  | 551 |         $formdata->visible = 0;
 | 
        
           |  |  | 552 |         $formdata->startdate = 1582376400;
 | 
        
           |  |  | 553 |         $formdata->enddate = 1582386400;
 | 
        
           |  |  | 554 |         $formdata->idnumber = 123;
 | 
        
           |  |  | 555 |         $formdata->userdata = 1;
 | 
        
           |  |  | 556 |         $formdata->role_1 = 1;
 | 
        
           |  |  | 557 |         $formdata->role_3 = 3;
 | 
        
           |  |  | 558 |         $formdata->role_5 = 5;
 | 
        
           |  |  | 559 |   | 
        
           |  |  | 560 |         // Create the course copy records and associated ad-hoc task.
 | 
        
           |  |  | 561 |         $copydata = \copy_helper::process_formdata($formdata);
 | 
        
           |  |  | 562 |         $copyids = \copy_helper::create_copy($copydata);
 | 
        
           |  |  | 563 |   | 
        
           |  |  | 564 |         $courseid = $this->course->id;
 | 
        
           |  |  | 565 |   | 
        
           |  |  | 566 |         // We are expecting trace output during this test.
 | 
        
           |  |  | 567 |         $this->expectOutputRegex("/$courseid/");
 | 
        
           |  |  | 568 |   | 
        
           |  |  | 569 |         // Execute adhoc task.
 | 
        
           |  |  | 570 |         $now = time();
 | 
        
           |  |  | 571 |         $task = \core\task\manager::get_next_adhoc_task($now);
 | 
        
           |  |  | 572 |         $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
 | 
        
           |  |  | 573 |         $task->execute();
 | 
        
           |  |  | 574 |         \core\task\manager::adhoc_task_complete($task);
 | 
        
           |  |  | 575 |   | 
        
           |  |  | 576 |         $postbackuprec = $DB->get_record('backup_controllers', array('backupid' => $copyids['backupid']));
 | 
        
           |  |  | 577 |         $postrestorerec = $DB->get_record('backup_controllers', array('backupid' => $copyids['restoreid']));
 | 
        
           |  |  | 578 |   | 
        
           |  |  | 579 |         // Check backup was completed successfully.
 | 
        
           |  |  | 580 |         $this->assertEquals(backup::STATUS_FINISHED_OK, $postbackuprec->status);
 | 
        
           |  |  | 581 |         $this->assertEquals(1.0, $postbackuprec->progress);
 | 
        
           |  |  | 582 |   | 
        
           |  |  | 583 |         // Check restore was completed successfully.
 | 
        
           |  |  | 584 |         $this->assertEquals(backup::STATUS_FINISHED_OK, $postrestorerec->status);
 | 
        
           |  |  | 585 |         $this->assertEquals(1.0, $postrestorerec->progress);
 | 
        
           |  |  | 586 |   | 
        
           |  |  | 587 |         // Check the restored course itself.
 | 
        
           |  |  | 588 |         $coursecontext = \context_course::instance($postrestorerec->itemid);
 | 
        
           |  |  | 589 |         $users = get_enrolled_users($coursecontext);
 | 
        
           |  |  | 590 |   | 
        
           |  |  | 591 |         $modinfo = get_fast_modinfo($postrestorerec->itemid);
 | 
        
           |  |  | 592 |         $forums = $modinfo->get_instances_of('forum');
 | 
        
           |  |  | 593 |         $forum = reset($forums);
 | 
        
           |  |  | 594 |         $discussions = forum_get_discussions($forum);
 | 
        
           |  |  | 595 |         $course = $modinfo->get_course();
 | 
        
           |  |  | 596 |   | 
        
           |  |  | 597 |         $this->assertEquals($formdata->startdate, $course->startdate);
 | 
        
           |  |  | 598 |         $this->assertEquals($formdata->enddate, $course->enddate);
 | 
        
           |  |  | 599 |         $this->assertEquals('copy course', $course->fullname);
 | 
        
           |  |  | 600 |         $this->assertEquals('copy course short',  $course->shortname);
 | 
        
           |  |  | 601 |         $this->assertEquals(0,  $course->visible);
 | 
        
           |  |  | 602 |         $this->assertEquals(123,  $course->idnumber);
 | 
        
           |  |  | 603 |   | 
        
           |  |  | 604 |         foreach ($modinfo->get_cms() as $cm) {
 | 
        
           |  |  | 605 |             $this->assertContains($cm->get_formatted_name(), $this->activitynames);
 | 
        
           |  |  | 606 |         }
 | 
        
           |  |  | 607 |   | 
        
           |  |  | 608 |         foreach ($this->courseusers as $user) {
 | 
        
           |  |  | 609 |             $this->assertEquals($user, $users[$user]->id);
 | 
        
           |  |  | 610 |         }
 | 
        
           |  |  | 611 |   | 
        
           |  |  | 612 |         $this->assertEquals(count($this->courseusers), count($users));
 | 
        
           |  |  | 613 |         $this->assertEquals(2, count($discussions));
 | 
        
           |  |  | 614 |     }
 | 
        
           |  |  | 615 |   | 
        
           |  |  | 616 |     /**
 | 
        
           |  |  | 617 |      * Test course copy, not including any users (or data).
 | 
        
           |  |  | 618 |      */
 | 
        
           | 11 | efrain | 619 |     public function test_course_copy_no_users(): void {
 | 
        
           | 1 | efrain | 620 |         global $DB;
 | 
        
           |  |  | 621 |   | 
        
           |  |  | 622 |         // Mock up the form data.
 | 
        
           |  |  | 623 |         $formdata = new \stdClass;
 | 
        
           |  |  | 624 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 625 |         $formdata->fullname = 'copy course';
 | 
        
           |  |  | 626 |         $formdata->shortname = 'copy course short';
 | 
        
           |  |  | 627 |         $formdata->category = 1;
 | 
        
           |  |  | 628 |         $formdata->visible = 0;
 | 
        
           |  |  | 629 |         $formdata->startdate = 1582376400;
 | 
        
           |  |  | 630 |         $formdata->enddate = 1582386400;
 | 
        
           |  |  | 631 |         $formdata->idnumber = 123;
 | 
        
           |  |  | 632 |         $formdata->userdata = 1;
 | 
        
           |  |  | 633 |         $formdata->role_1 = 0;
 | 
        
           |  |  | 634 |         $formdata->role_3 = 0;
 | 
        
           |  |  | 635 |         $formdata->role_5 = 0;
 | 
        
           |  |  | 636 |   | 
        
           |  |  | 637 |         // Create the course copy records and associated ad-hoc task.
 | 
        
           |  |  | 638 |         $copydata = \copy_helper::process_formdata($formdata);
 | 
        
           |  |  | 639 |         $copyids = \copy_helper::create_copy($copydata);
 | 
        
           |  |  | 640 |   | 
        
           |  |  | 641 |         $courseid = $this->course->id;
 | 
        
           |  |  | 642 |   | 
        
           |  |  | 643 |         // We are expecting trace output during this test.
 | 
        
           |  |  | 644 |         $this->expectOutputRegex("/$courseid/");
 | 
        
           |  |  | 645 |   | 
        
           |  |  | 646 |         // Execute adhoc task.
 | 
        
           |  |  | 647 |         $now = time();
 | 
        
           |  |  | 648 |         $task = \core\task\manager::get_next_adhoc_task($now);
 | 
        
           |  |  | 649 |         $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
 | 
        
           |  |  | 650 |         $task->execute();
 | 
        
           |  |  | 651 |         \core\task\manager::adhoc_task_complete($task);
 | 
        
           |  |  | 652 |   | 
        
           |  |  | 653 |         $postrestorerec = $DB->get_record('backup_controllers', array('backupid' => $copyids['restoreid']));
 | 
        
           |  |  | 654 |   | 
        
           |  |  | 655 |         // Check the restored course itself.
 | 
        
           |  |  | 656 |         $coursecontext = \context_course::instance($postrestorerec->itemid);
 | 
        
           |  |  | 657 |         $users = get_enrolled_users($coursecontext);
 | 
        
           |  |  | 658 |   | 
        
           |  |  | 659 |         $modinfo = get_fast_modinfo($postrestorerec->itemid);
 | 
        
           |  |  | 660 |         $forums = $modinfo->get_instances_of('forum');
 | 
        
           |  |  | 661 |         $forum = reset($forums);
 | 
        
           |  |  | 662 |         $discussions = forum_get_discussions($forum);
 | 
        
           |  |  | 663 |         $course = $modinfo->get_course();
 | 
        
           |  |  | 664 |   | 
        
           |  |  | 665 |         $this->assertEquals($formdata->startdate, $course->startdate);
 | 
        
           |  |  | 666 |         $this->assertEquals($formdata->enddate, $course->enddate);
 | 
        
           |  |  | 667 |         $this->assertEquals('copy course', $course->fullname);
 | 
        
           |  |  | 668 |         $this->assertEquals('copy course short',  $course->shortname);
 | 
        
           |  |  | 669 |         $this->assertEquals(0,  $course->visible);
 | 
        
           |  |  | 670 |         $this->assertEquals(123,  $course->idnumber);
 | 
        
           |  |  | 671 |   | 
        
           |  |  | 672 |         foreach ($modinfo->get_cms() as $cm) {
 | 
        
           |  |  | 673 |             $this->assertContains($cm->get_formatted_name(), $this->activitynames);
 | 
        
           |  |  | 674 |         }
 | 
        
           |  |  | 675 |   | 
        
           |  |  | 676 |         // Should be no discussions as the user that made them wasn't included.
 | 
        
           |  |  | 677 |         $this->assertEquals(0, count($discussions));
 | 
        
           |  |  | 678 |   | 
        
           |  |  | 679 |         // There should only be one user in the new course, and that's the user who did the copy.
 | 
        
           |  |  | 680 |         $this->assertEquals(1, count($users));
 | 
        
           |  |  | 681 |         $this->assertEquals($this->courseusers[2], $users[$this->courseusers[2]]->id);
 | 
        
           |  |  | 682 |   | 
        
           |  |  | 683 |     }
 | 
        
           |  |  | 684 |   | 
        
           |  |  | 685 |     /**
 | 
        
           |  |  | 686 |      * Test course copy, including students and their data.
 | 
        
           |  |  | 687 |      */
 | 
        
           | 11 | efrain | 688 |     public function test_course_copy_students_data(): void {
 | 
        
           | 1 | efrain | 689 |         global $DB;
 | 
        
           |  |  | 690 |   | 
        
           |  |  | 691 |         // Mock up the form data.
 | 
        
           |  |  | 692 |         $formdata = new \stdClass;
 | 
        
           |  |  | 693 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 694 |         $formdata->fullname = 'copy course';
 | 
        
           |  |  | 695 |         $formdata->shortname = 'copy course short';
 | 
        
           |  |  | 696 |         $formdata->category = 1;
 | 
        
           |  |  | 697 |         $formdata->visible = 0;
 | 
        
           |  |  | 698 |         $formdata->startdate = 1582376400;
 | 
        
           |  |  | 699 |         $formdata->enddate = 1582386400;
 | 
        
           |  |  | 700 |         $formdata->idnumber = 123;
 | 
        
           |  |  | 701 |         $formdata->userdata = 1;
 | 
        
           |  |  | 702 |         $formdata->role_1 = 0;
 | 
        
           |  |  | 703 |         $formdata->role_3 = 0;
 | 
        
           |  |  | 704 |         $formdata->role_5 = 5;
 | 
        
           |  |  | 705 |   | 
        
           |  |  | 706 |         // Create the course copy records and associated ad-hoc task.
 | 
        
           |  |  | 707 |         $copydata = \copy_helper::process_formdata($formdata);
 | 
        
           |  |  | 708 |         $copyids = \copy_helper::create_copy($copydata);
 | 
        
           |  |  | 709 |   | 
        
           |  |  | 710 |         $courseid = $this->course->id;
 | 
        
           |  |  | 711 |   | 
        
           |  |  | 712 |         // We are expecting trace output during this test.
 | 
        
           |  |  | 713 |         $this->expectOutputRegex("/$courseid/");
 | 
        
           |  |  | 714 |   | 
        
           |  |  | 715 |         // Execute adhoc task.
 | 
        
           |  |  | 716 |         $now = time();
 | 
        
           |  |  | 717 |         $task = \core\task\manager::get_next_adhoc_task($now);
 | 
        
           |  |  | 718 |         $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
 | 
        
           |  |  | 719 |         $task->execute();
 | 
        
           |  |  | 720 |         \core\task\manager::adhoc_task_complete($task);
 | 
        
           |  |  | 721 |   | 
        
           |  |  | 722 |         $postrestorerec = $DB->get_record('backup_controllers', array('backupid' => $copyids['restoreid']));
 | 
        
           |  |  | 723 |   | 
        
           |  |  | 724 |         // Check the restored course itself.
 | 
        
           |  |  | 725 |         $coursecontext = \context_course::instance($postrestorerec->itemid);
 | 
        
           |  |  | 726 |         $users = get_enrolled_users($coursecontext);
 | 
        
           |  |  | 727 |   | 
        
           |  |  | 728 |         $modinfo = get_fast_modinfo($postrestorerec->itemid);
 | 
        
           |  |  | 729 |         $forums = $modinfo->get_instances_of('forum');
 | 
        
           |  |  | 730 |         $forum = reset($forums);
 | 
        
           |  |  | 731 |         $discussions = forum_get_discussions($forum);
 | 
        
           |  |  | 732 |         $course = $modinfo->get_course();
 | 
        
           |  |  | 733 |   | 
        
           |  |  | 734 |         $this->assertEquals($formdata->startdate, $course->startdate);
 | 
        
           |  |  | 735 |         $this->assertEquals($formdata->enddate, $course->enddate);
 | 
        
           |  |  | 736 |         $this->assertEquals('copy course', $course->fullname);
 | 
        
           |  |  | 737 |         $this->assertEquals('copy course short',  $course->shortname);
 | 
        
           |  |  | 738 |         $this->assertEquals(0,  $course->visible);
 | 
        
           |  |  | 739 |         $this->assertEquals(123,  $course->idnumber);
 | 
        
           |  |  | 740 |   | 
        
           |  |  | 741 |         foreach ($modinfo->get_cms() as $cm) {
 | 
        
           |  |  | 742 |             $this->assertContains($cm->get_formatted_name(), $this->activitynames);
 | 
        
           |  |  | 743 |         }
 | 
        
           |  |  | 744 |   | 
        
           |  |  | 745 |         // Should be no discussions as the user that made them wasn't included.
 | 
        
           |  |  | 746 |         $this->assertEquals(2, count($discussions));
 | 
        
           |  |  | 747 |   | 
        
           |  |  | 748 |         // There should only be two users in the new course. The copier and one student.
 | 
        
           |  |  | 749 |         $this->assertEquals(2, count($users));
 | 
        
           |  |  | 750 |         $this->assertEquals($this->courseusers[2], $users[$this->courseusers[2]]->id);
 | 
        
           |  |  | 751 |         $this->assertEquals($this->courseusers[0], $users[$this->courseusers[0]]->id);
 | 
        
           |  |  | 752 |     }
 | 
        
           |  |  | 753 |   | 
        
           |  |  | 754 |     /**
 | 
        
           |  |  | 755 |      * Test course copy, not including any users (or data).
 | 
        
           |  |  | 756 |      */
 | 
        
           | 11 | efrain | 757 |     public function test_course_copy_no_data(): void {
 | 
        
           | 1 | efrain | 758 |         global $DB;
 | 
        
           |  |  | 759 |   | 
        
           |  |  | 760 |         // Mock up the form data.
 | 
        
           |  |  | 761 |         $formdata = new \stdClass;
 | 
        
           |  |  | 762 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 763 |         $formdata->fullname = 'copy course';
 | 
        
           |  |  | 764 |         $formdata->shortname = 'copy course short';
 | 
        
           |  |  | 765 |         $formdata->category = 1;
 | 
        
           |  |  | 766 |         $formdata->visible = 0;
 | 
        
           |  |  | 767 |         $formdata->startdate = 1582376400;
 | 
        
           |  |  | 768 |         $formdata->enddate = 1582386400;
 | 
        
           |  |  | 769 |         $formdata->idnumber = 123;
 | 
        
           |  |  | 770 |         $formdata->userdata = 0;
 | 
        
           |  |  | 771 |         $formdata->role_1 = 1;
 | 
        
           |  |  | 772 |         $formdata->role_3 = 3;
 | 
        
           |  |  | 773 |         $formdata->role_5 = 5;
 | 
        
           |  |  | 774 |   | 
        
           |  |  | 775 |         // Create the course copy records and associated ad-hoc task.
 | 
        
           |  |  | 776 |         $copydata = \copy_helper::process_formdata($formdata);
 | 
        
           |  |  | 777 |         $copyids = \copy_helper::create_copy($copydata);
 | 
        
           |  |  | 778 |   | 
        
           |  |  | 779 |         $courseid = $this->course->id;
 | 
        
           |  |  | 780 |   | 
        
           |  |  | 781 |         // We are expecting trace output during this test.
 | 
        
           |  |  | 782 |         $this->expectOutputRegex("/$courseid/");
 | 
        
           |  |  | 783 |   | 
        
           |  |  | 784 |         // Execute adhoc task.
 | 
        
           |  |  | 785 |         $now = time();
 | 
        
           |  |  | 786 |         $task = \core\task\manager::get_next_adhoc_task($now);
 | 
        
           |  |  | 787 |         $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
 | 
        
           |  |  | 788 |         $task->execute();
 | 
        
           |  |  | 789 |         \core\task\manager::adhoc_task_complete($task);
 | 
        
           |  |  | 790 |   | 
        
           |  |  | 791 |         $postrestorerec = $DB->get_record('backup_controllers', array('backupid' => $copyids['restoreid']));
 | 
        
           |  |  | 792 |   | 
        
           |  |  | 793 |         // Check the restored course itself.
 | 
        
           |  |  | 794 |         $coursecontext = \context_course::instance($postrestorerec->itemid);
 | 
        
           |  |  | 795 |         $users = get_enrolled_users($coursecontext);
 | 
        
           |  |  | 796 |   | 
        
           |  |  | 797 |         get_fast_modinfo($postrestorerec->itemid, 0, true);
 | 
        
           |  |  | 798 |         $modinfo = get_fast_modinfo($postrestorerec->itemid);
 | 
        
           |  |  | 799 |         $forums = $modinfo->get_instances_of('forum');
 | 
        
           |  |  | 800 |         $forum = reset($forums);
 | 
        
           |  |  | 801 |         $discussions = forum_get_discussions($forum);
 | 
        
           |  |  | 802 |         $course = $modinfo->get_course();
 | 
        
           |  |  | 803 |   | 
        
           |  |  | 804 |         $this->assertEquals($formdata->startdate, $course->startdate);
 | 
        
           |  |  | 805 |         $this->assertEquals($formdata->enddate, $course->enddate);
 | 
        
           |  |  | 806 |         $this->assertEquals('copy course', $course->fullname);
 | 
        
           |  |  | 807 |         $this->assertEquals('copy course short',  $course->shortname);
 | 
        
           |  |  | 808 |         $this->assertEquals(0,  $course->visible);
 | 
        
           |  |  | 809 |         $this->assertEquals(123,  $course->idnumber);
 | 
        
           |  |  | 810 |   | 
        
           |  |  | 811 |         foreach ($modinfo->get_cms() as $cm) {
 | 
        
           |  |  | 812 |             $this->assertContains($cm->get_formatted_name(), $this->activitynames);
 | 
        
           |  |  | 813 |         }
 | 
        
           |  |  | 814 |   | 
        
           |  |  | 815 |         // Should be no discussions as the user data wasn't included.
 | 
        
           |  |  | 816 |         $this->assertEquals(0, count($discussions));
 | 
        
           |  |  | 817 |   | 
        
           |  |  | 818 |         // There should only be all users in the new course.
 | 
        
           |  |  | 819 |         $this->assertEquals(count($this->courseusers), count($users));
 | 
        
           |  |  | 820 |     }
 | 
        
           |  |  | 821 |   | 
        
           |  |  | 822 |     /**
 | 
        
           |  |  | 823 |      * Test instantiation with incomplete formdata.
 | 
        
           |  |  | 824 |      */
 | 
        
           | 11 | efrain | 825 |     public function test_malformed_instantiation(): void {
 | 
        
           | 1 | efrain | 826 |         // Mock up the form data, missing things so we get an exception.
 | 
        
           |  |  | 827 |         $formdata = new \stdClass;
 | 
        
           |  |  | 828 |         $formdata->courseid = $this->course->id;
 | 
        
           |  |  | 829 |         $formdata->fullname = 'copy course';
 | 
        
           |  |  | 830 |         $formdata->shortname = 'copy course short';
 | 
        
           |  |  | 831 |         $formdata->category = 1;
 | 
        
           |  |  | 832 |   | 
        
           |  |  | 833 |         // Expect and exception as form data is incomplete.
 | 
        
           |  |  | 834 |         $this->expectException(\moodle_exception::class);
 | 
        
           |  |  | 835 |         $copydata = \copy_helper::process_formdata($formdata);
 | 
        
           |  |  | 836 |         \copy_helper::create_copy($copydata);
 | 
        
           |  |  | 837 |     }
 | 
        
           |  |  | 838 | }
 |