See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
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 use backup_controller; 21 use restore_controller; 22 use restore_dbops; 23 24 defined('MOODLE_INTERNAL') || die(); 25 26 global $CFG; 27 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); 28 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 29 require_once($CFG->libdir . '/completionlib.php'); 30 31 /** 32 * Asyncronhous restore tests. 33 * 34 * @package core_backup 35 * @copyright 2018 Matt Porritt <mattp@catalyst-au.net> 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class async_restore_test extends \advanced_testcase { 39 40 /** 41 * Tests the asynchronous backup. 42 */ 43 public function test_async_restore() { 44 global $CFG, $USER, $DB; 45 46 $this->resetAfterTest(true); 47 $this->setAdminUser(); 48 $CFG->enableavailability = true; 49 $CFG->enablecompletion = true; 50 51 // Create a course with some availability data set. 52 $generator = $this->getDataGenerator(); 53 $course = $generator->create_course( 54 array('format' => 'topics', 'numsections' => 3, 55 'enablecompletion' => COMPLETION_ENABLED), 56 array('createsections' => true)); 57 $forum = $generator->create_module('forum', array( 58 'course' => $course->id)); 59 $forum2 = $generator->create_module('forum', array( 60 'course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL)); 61 62 // We need a grade, easiest is to add an assignment. 63 $assignrow = $generator->create_module('assign', array( 64 'course' => $course->id)); 65 $assign = new \assign(\context_module::instance($assignrow->cmid), false, false); 66 $item = $assign->get_grade_item(); 67 68 // Make a test grouping as well. 69 $grouping = $generator->create_grouping(array('courseid' => $course->id, 70 'name' => 'Grouping!')); 71 72 $availability = '{"op":"|","show":false,"c":[' . 73 '{"type":"completion","cm":' . $forum2->cmid .',"e":1},' . 74 '{"type":"grade","id":' . $item->id . ',"min":4,"max":94},' . 75 '{"type":"grouping","id":' . $grouping->id . '}' . 76 ']}'; 77 $DB->set_field('course_modules', 'availability', $availability, array( 78 'id' => $forum->cmid)); 79 $DB->set_field('course_sections', 'availability', $availability, array( 80 'course' => $course->id, 'section' => 1)); 81 82 // Backup the course. 83 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, 84 backup::INTERACTIVE_YES, backup::MODE_GENERAL, $USER->id); 85 $bc->finish_ui(); 86 $backupid = $bc->get_backupid(); 87 $bc->execute_plan(); 88 $bc->destroy(); 89 90 // Get the backup file. 91 $coursecontext = \context_course::instance($course->id); 92 $fs = get_file_storage(); 93 $files = $fs->get_area_files($coursecontext->id, 'backup', 'course', false, 'id ASC'); 94 $backupfile = reset($files); 95 96 // Extract backup file. 97 $backupdir = "restore_" . uniqid(); 98 $path = $CFG->tempdir . DIRECTORY_SEPARATOR . "backup" . DIRECTORY_SEPARATOR . $backupdir; 99 100 $fp = get_file_packer('application/vnd.moodle.backup'); 101 $fp->extract_to_pathname($backupfile, $path); 102 103 // Create restore controller. 104 $newcourseid = restore_dbops::create_new_course( 105 $course->fullname, $course->shortname . '_2', $course->category); 106 $rc = new restore_controller($backupdir, $newcourseid, 107 backup::INTERACTIVE_NO, backup::MODE_ASYNC, $USER->id, 108 backup::TARGET_NEW_COURSE); 109 $this->assertTrue($rc->execute_precheck()); 110 $restoreid = $rc->get_restoreid(); 111 112 $prerestorerec = $DB->get_record('backup_controllers', array('backupid' => $restoreid)); 113 $prerestorerec->controller = ''; 114 115 $rc->destroy(); 116 117 // Create the adhoc task. 118 $asynctask = new \core\task\asynchronous_restore_task(); 119 $asynctask->set_blocking(false); 120 $asynctask->set_custom_data(array('backupid' => $restoreid)); 121 $asynctask->set_userid($USER->id); 122 \core\task\manager::queue_adhoc_task($asynctask); 123 124 // We are expecting trace output during this test. 125 $this->expectOutputRegex("/$restoreid/"); 126 127 // Execute adhoc task. 128 $now = time(); 129 $task = \core\task\manager::get_next_adhoc_task($now); 130 $this->assertInstanceOf('\\core\\task\\asynchronous_restore_task', $task); 131 $task->execute(); 132 \core\task\manager::adhoc_task_complete($task); 133 134 $postrestorerec = $DB->get_record('backup_controllers', array('backupid' => $restoreid)); 135 136 // Check backup was created successfully. 137 $this->assertEquals(backup::STATUS_FINISHED_OK, $postrestorerec->status); 138 $this->assertEquals(1.0, $postrestorerec->progress); 139 $this->assertEquals($USER->id, $postrestorerec->userid); 140 } 141 142 /** 143 * Tests the asynchronous restore will resolve in duplicate cases where the controller is already removed. 144 */ 145 public function test_async_restore_missing_controller() { 146 global $CFG, $USER, $DB; 147 148 $this->resetAfterTest(true); 149 $this->setAdminUser(); 150 $CFG->enableavailability = true; 151 $CFG->enablecompletion = true; 152 153 // Create a course with some availability data set. 154 $generator = $this->getDataGenerator(); 155 $course = $generator->create_course( 156 array('format' => 'topics', 'numsections' => 3, 157 'enablecompletion' => COMPLETION_ENABLED), 158 array('createsections' => true)); 159 $forum = $generator->create_module('forum', array( 160 'course' => $course->id)); 161 $forum2 = $generator->create_module('forum', array( 162 'course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL)); 163 164 // We need a grade, easiest is to add an assignment. 165 $assignrow = $generator->create_module('assign', array( 166 'course' => $course->id)); 167 $assign = new \assign(\context_module::instance($assignrow->cmid), false, false); 168 $item = $assign->get_grade_item(); 169 170 // Make a test grouping as well. 171 $grouping = $generator->create_grouping(array('courseid' => $course->id, 172 'name' => 'Grouping!')); 173 174 $availability = '{"op":"|","show":false,"c":[' . 175 '{"type":"completion","cm":' . $forum2->cmid .',"e":1},' . 176 '{"type":"grade","id":' . $item->id . ',"min":4,"max":94},' . 177 '{"type":"grouping","id":' . $grouping->id . '}' . 178 ']}'; 179 $DB->set_field('course_modules', 'availability', $availability, array( 180 'id' => $forum->cmid)); 181 $DB->set_field('course_sections', 'availability', $availability, array( 182 'course' => $course->id, 'section' => 1)); 183 184 // Backup the course. 185 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, 186 backup::INTERACTIVE_YES, backup::MODE_GENERAL, $USER->id); 187 $bc->finish_ui(); 188 $bc->execute_plan(); 189 $bc->destroy(); 190 191 // Get the backup file. 192 $coursecontext = \context_course::instance($course->id); 193 $fs = get_file_storage(); 194 $files = $fs->get_area_files($coursecontext->id, 'backup', 'course', false, 'id ASC'); 195 $backupfile = reset($files); 196 197 // Extract backup file. 198 $backupdir = "restore_" . uniqid(); 199 $path = $CFG->tempdir . DIRECTORY_SEPARATOR . "backup" . DIRECTORY_SEPARATOR . $backupdir; 200 201 $fp = get_file_packer('application/vnd.moodle.backup'); 202 $fp->extract_to_pathname($backupfile, $path); 203 204 // Create restore controller. 205 $newcourseid = restore_dbops::create_new_course( 206 $course->fullname, $course->shortname . '_2', $course->category); 207 $rc = new restore_controller($backupdir, $newcourseid, 208 backup::INTERACTIVE_NO, backup::MODE_ASYNC, $USER->id, 209 backup::TARGET_NEW_COURSE); 210 $restoreid = $rc->get_restoreid(); 211 $controllerid = $DB->get_field('backup_controllers', 'id', ['backupid' => $restoreid]); 212 213 // Now hack the record to remove the controller and set the status fields to complete. 214 // This emulates a duplicate run for an already finished controller. 215 $data = [ 216 'id' => $controllerid, 217 'controller' => '', 218 'progress' => 1.0, 219 'status' => backup::STATUS_FINISHED_OK 220 ]; 221 $DB->update_record('backup_controllers', $data); 222 $rc->destroy(); 223 224 // Create the adhoc task. 225 $asynctask = new \core\task\asynchronous_restore_task(); 226 $asynctask->set_blocking(false); 227 $asynctask->set_custom_data(['backupid' => $restoreid]); 228 \core\task\manager::queue_adhoc_task($asynctask); 229 230 // We are expecting a specific message output during this test. 231 $this->expectOutputRegex('/invalid controller/'); 232 233 // Execute adhoc task. 234 $now = time(); 235 $task = \core\task\manager::get_next_adhoc_task($now); 236 $this->assertInstanceOf('\\core\\task\\asynchronous_restore_task', $task); 237 $task->execute(); 238 \core\task\manager::adhoc_task_complete($task); 239 240 // Check the task record is removed. 241 $this->assertEquals(0, $DB->count_records('task_adhoc')); 242 243 // Now delete the record and confirm an entirely missing controller is handled. 244 $DB->delete_records('backup_controllers'); 245 246 // Create the adhoc task. 247 $asynctask = new \core\task\asynchronous_restore_task(); 248 $asynctask->set_blocking(false); 249 $asynctask->set_custom_data(['backupid' => $restoreid]); 250 \core\task\manager::queue_adhoc_task($asynctask); 251 252 // We are expecting a specific message output during this test. 253 $this->expectOutputRegex('/Unable to find restore controller/'); 254 255 // Execute adhoc task. 256 $now = time(); 257 $task = \core\task\manager::get_next_adhoc_task($now); 258 $this->assertInstanceOf('\\core\\task\\asynchronous_restore_task', $task); 259 $task->execute(); 260 \core\task\manager::adhoc_task_complete($task); 261 262 // Check the task record is removed. 263 $this->assertEquals(0, $DB->count_records('task_adhoc')); 264 } 265 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body