Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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
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_custom_data(array('backupid' => $restoreid));
120
        $asynctask->set_userid($USER->id);
121
        \core\task\manager::queue_adhoc_task($asynctask);
122
 
123
        // We are expecting trace output during this test.
124
        $this->expectOutputRegex("/$restoreid/");
125
 
126
        // Execute adhoc task.
127
        $now = time();
128
        $task = \core\task\manager::get_next_adhoc_task($now);
129
        $this->assertInstanceOf('\\core\\task\\asynchronous_restore_task', $task);
130
        $task->execute();
131
        \core\task\manager::adhoc_task_complete($task);
132
 
133
        $postrestorerec = $DB->get_record('backup_controllers', array('backupid' => $restoreid));
134
 
135
        // Check backup was created successfully.
136
        $this->assertEquals(backup::STATUS_FINISHED_OK, $postrestorerec->status);
137
        $this->assertEquals(1.0, $postrestorerec->progress);
138
        $this->assertEquals($USER->id, $postrestorerec->userid);
139
    }
140
 
141
    /**
142
     * Tests the asynchronous restore will resolve in duplicate cases where the controller is already removed.
143
     */
144
    public function test_async_restore_missing_controller() {
145
        global $CFG, $USER, $DB;
146
 
147
        $this->resetAfterTest(true);
148
        $this->setAdminUser();
149
        $CFG->enableavailability = true;
150
        $CFG->enablecompletion = true;
151
 
152
        // Create a course with some availability data set.
153
        $generator = $this->getDataGenerator();
154
        $course = $generator->create_course(
155
                array('format' => 'topics', 'numsections' => 3,
156
                        'enablecompletion' => COMPLETION_ENABLED),
157
                array('createsections' => true));
158
        $forum = $generator->create_module('forum', array(
159
                'course' => $course->id));
160
        $forum2 = $generator->create_module('forum', array(
161
                'course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL));
162
 
163
        // We need a grade, easiest is to add an assignment.
164
        $assignrow = $generator->create_module('assign', array(
165
                'course' => $course->id));
166
        $assign = new \assign(\context_module::instance($assignrow->cmid), false, false);
167
        $item = $assign->get_grade_item();
168
 
169
        // Make a test grouping as well.
170
        $grouping = $generator->create_grouping(array('courseid' => $course->id,
171
                'name' => 'Grouping!'));
172
 
173
        $availability = '{"op":"|","show":false,"c":[' .
174
                '{"type":"completion","cm":' . $forum2->cmid .',"e":1},' .
175
                '{"type":"grade","id":' . $item->id . ',"min":4,"max":94},' .
176
                '{"type":"grouping","id":' . $grouping->id . '}' .
177
                ']}';
178
        $DB->set_field('course_modules', 'availability', $availability, array(
179
                'id' => $forum->cmid));
180
        $DB->set_field('course_sections', 'availability', $availability, array(
181
                'course' => $course->id, 'section' => 1));
182
 
183
        // Backup the course.
184
        $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
185
                backup::INTERACTIVE_YES, backup::MODE_GENERAL, $USER->id);
186
        $bc->finish_ui();
187
        $bc->execute_plan();
188
        $bc->destroy();
189
 
190
        // Get the backup file.
191
        $coursecontext = \context_course::instance($course->id);
192
        $fs = get_file_storage();
193
        $files = $fs->get_area_files($coursecontext->id, 'backup', 'course', false, 'id ASC');
194
        $backupfile = reset($files);
195
 
196
        // Extract backup file.
197
        $backupdir = "restore_" . uniqid();
198
        $path = $CFG->tempdir . DIRECTORY_SEPARATOR . "backup" . DIRECTORY_SEPARATOR . $backupdir;
199
 
200
        $fp = get_file_packer('application/vnd.moodle.backup');
201
        $fp->extract_to_pathname($backupfile, $path);
202
 
203
        // Create restore controller.
204
        $newcourseid = restore_dbops::create_new_course(
205
                $course->fullname, $course->shortname . '_2', $course->category);
206
        $rc = new restore_controller($backupdir, $newcourseid,
207
                backup::INTERACTIVE_NO, backup::MODE_ASYNC, $USER->id,
208
                backup::TARGET_NEW_COURSE);
209
        $restoreid = $rc->get_restoreid();
210
        $controllerid = $DB->get_field('backup_controllers', 'id', ['backupid' => $restoreid]);
211
 
212
        // Now hack the record to remove the controller and set the status fields to complete.
213
        // This emulates a duplicate run for an already finished controller.
214
        $data = [
215
            'id' => $controllerid,
216
            'controller' => '',
217
            'progress' => 1.0,
218
            'status' => backup::STATUS_FINISHED_OK
219
        ];
220
        $DB->update_record('backup_controllers', $data);
221
        $rc->destroy();
222
 
223
        // Create the adhoc task.
224
        $asynctask = new \core\task\asynchronous_restore_task();
225
        $asynctask->set_custom_data(['backupid' => $restoreid]);
226
        \core\task\manager::queue_adhoc_task($asynctask);
227
 
228
        // We are expecting a specific message output during this test.
229
        $this->expectOutputRegex('/invalid controller/');
230
 
231
        // Execute adhoc task.
232
        $now = time();
233
        $task = \core\task\manager::get_next_adhoc_task($now);
234
        $this->assertInstanceOf('\\core\\task\\asynchronous_restore_task', $task);
235
        $task->execute();
236
        \core\task\manager::adhoc_task_complete($task);
237
 
238
        // Check the task record is removed.
239
        $this->assertEquals(0, $DB->count_records('task_adhoc'));
240
 
241
        // Now delete the record and confirm an entirely missing controller is handled.
242
        $DB->delete_records('backup_controllers');
243
 
244
        // Create the adhoc task.
245
        $asynctask = new \core\task\asynchronous_restore_task();
246
        $asynctask->set_custom_data(['backupid' => $restoreid]);
247
        \core\task\manager::queue_adhoc_task($asynctask);
248
 
249
        // We are expecting a specific message output during this test.
250
        $this->expectOutputRegex('/Unable to find restore controller/');
251
 
252
        // Execute adhoc task.
253
        $now = time();
254
        $task = \core\task\manager::get_next_adhoc_task($now);
255
        $this->assertInstanceOf('\\core\\task\\asynchronous_restore_task', $task);
256
        $task->execute();
257
        \core\task\manager::adhoc_task_complete($task);
258
 
259
        // Check the task record is removed.
260
        $this->assertEquals(0, $DB->count_records('task_adhoc'));
261
    }
262
}