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/**
18 * @package   core_backup
19 * @category  phpunit
20 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
21 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22 */
23
24defined('MOODLE_INTERNAL') || die();
25
26// Include all the needed stuff
27global $CFG;
28require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
29require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
30
31/*
32 * controller tests (all)
33 */
34class core_backup_controller_testcase extends advanced_testcase {
35
36    protected $moduleid;  // course_modules id used for testing
37    protected $sectionid; // course_sections id used for testing
38    protected $courseid;  // course id used for testing
39    protected $userid;    // user used if for testing
40
41    protected function setUp() {
42        global $DB, $CFG;
43
44        $this->resetAfterTest(true);
45
46        $course = $this->getDataGenerator()->create_course();
47        $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id), array('section'=>3));
48        $coursemodule = $DB->get_record('course_modules', array('id'=>$page->cmid));
49
50        $this->moduleid  = $coursemodule->id;
51        $this->sectionid = $DB->get_field("course_sections", 'id', array("section"=>$coursemodule->section, "course"=>$course->id));
52        $this->courseid  = $coursemodule->course;
53        $this->userid = 2; // admin
54
55        // Disable all loggers
56        $CFG->backup_error_log_logger_level = backup::LOG_NONE;
57        $CFG->backup_output_indented_logger_level = backup::LOG_NONE;
58        $CFG->backup_file_logger_level = backup::LOG_NONE;
59        $CFG->backup_database_logger_level = backup::LOG_NONE;
60        $CFG->backup_file_logger_level_extra = backup::LOG_NONE;
61    }
62
63    /**
64     * Test set copy method.
65     */
66    public function test_base_controller_set_copy() {
67        $this->expectException(\backup_controller_exception::class);
68        $copy = new \stdClass();
69
70        // Set up controller as a non-copy operation.
71        $bc = new \backup_controller(backup::TYPE_1COURSE, $this->courseid, backup::FORMAT_MOODLE,
72            backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid, backup::RELEASESESSION_YES);
73
74        $bc->set_copy($copy);
75    }
76
77    /*
78     * test base_setting class
79     */
80    public function test_backup_controller() {
81        // Instantiate non interactive backup_controller
82        $bc = new mock_backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
83            backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid);
84        $this->assertTrue($bc instanceof backup_controller);
85        $this->assertEquals($bc->get_status(), backup::STATUS_AWAITING);
86        // Instantiate interactive backup_controller
87        $bc = new mock_backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
88            backup::INTERACTIVE_YES, backup::MODE_GENERAL, $this->userid);
89        $this->assertTrue($bc instanceof backup_controller);
90        $this->assertEquals($bc->get_status(), backup::STATUS_SETTING_UI);
91        $this->assertEquals(strlen($bc->get_backupid()), 32); // is one md5
92
93        // Save and load one backup controller to check everything is in place
94        $bc = new mock_backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
95            backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid);
96        $recid = $bc->save_controller();
97        $newbc = mock_backup_controller::load_controller($bc->get_backupid());
98        $this->assertTrue($newbc instanceof backup_controller); // This means checksum and load worked ok
99    }
100
101    public function test_backup_controller_include_files() {
102        // A MODE_GENERAL controller - this should include files
103        $bc = new mock_backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
104            backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid);
105        $this->assertEquals($bc->get_include_files(), 1);
106
107
108        // The MODE_IMPORT and MODE_SAMESITE should not include files in the backup.
109        // A MODE_IMPORT controller
110        $bc = new mock_backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
111            backup::INTERACTIVE_NO, backup::MODE_IMPORT, $this->userid);
112        $this->assertEquals($bc->get_include_files(), 0);
113
114        // A MODE_SAMESITE controller
115        $bc = new mock_backup_controller(backup::TYPE_1COURSE, $this->courseid, backup::FORMAT_MOODLE,
116            backup::INTERACTIVE_NO, backup::MODE_IMPORT, $this->userid);
117        $this->assertEquals($bc->get_include_files(), 0);
118    }
119
120    /**
121     * Test set kept roles method.
122     */
123    public function test_backup_controller_set_kept_roles() {
124        $this->expectException(\backup_controller_exception::class);
125
126        // Set up controller as a non-copy operation.
127        $bc = new \backup_controller(backup::TYPE_1COURSE, $this->courseid, backup::FORMAT_MOODLE,
128            backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid, backup::RELEASESESSION_YES);
129
130        $bc->set_kept_roles(array(1, 3, 5));
131    }
132
133    /**
134     * Tests the restore_controller.
135     */
136    public function test_restore_controller_is_executing() {
137        global $CFG;
138
139        // Make a backup.
140        make_backup_temp_directory('');
141        $bc = new backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
142            backup::INTERACTIVE_NO, backup::MODE_IMPORT, $this->userid);
143        $backupid = $bc->get_backupid();
144        $bc->execute_plan();
145        $bc->destroy();
146
147        // The progress class will get called during restore, so we can use that
148        // to check the executing flag is true.
149        $progress = new core_backup_progress_restore_is_executing();
150
151        // Set up restore.
152        $rc = new restore_controller($backupid, $this->courseid,
153                backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $this->userid,
154                backup::TARGET_EXISTING_ADDING);
155        $this->assertTrue($rc->execute_precheck());
156
157        // Check restore is NOT executing.
158        $this->assertFalse(restore_controller::is_executing());
159
160        // Execute restore.
161        $rc->set_progress($progress);
162        $rc->execute_plan();
163
164        // Check restore is NOT executing afterward either.
165        $this->assertFalse(restore_controller::is_executing());
166        $rc->destroy();
167
168        // During restore, check that executing was true.
169        $this->assertTrue(count($progress->executing) > 0);
170        $alltrue = true;
171        foreach ($progress->executing as $executing) {
172            if (!$executing) {
173                $alltrue = false;
174                break;
175            }
176        }
177        $this->assertTrue($alltrue);
178    }
179
180    /**
181     * Test prepare copy method.
182     */
183    public function test_restore_controller_prepare_copy() {
184        $this->expectException(\restore_controller_exception::class);
185
186        global $CFG;
187
188        // Make a backup.
189        make_backup_temp_directory('');
190        $bc = new backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
191            backup::INTERACTIVE_NO, backup::MODE_IMPORT, $this->userid);
192        $backupid = $bc->get_backupid();
193        $bc->execute_plan();
194        $bc->destroy();
195
196        // Set up restore.
197        $rc = new restore_controller($backupid, $this->courseid,
198            backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $this->userid,
199            backup::TARGET_EXISTING_ADDING);
200        $rc->prepare_copy();
201    }
202
203    /**
204     * Test restore of deadlock causing backup.
205     */
206    public function test_restore_of_deadlock_causing_backup() {
207        global $USER, $CFG;
208        $this->preventResetByRollback();
209
210        $foldername = 'deadlock';
211        $fp = get_file_packer('application/vnd.moodle.backup');
212        $tempdir = make_backup_temp_directory($foldername);
213        $files = $fp->extract_to_pathname($CFG->dirroot . '/backup/controller/tests/fixtures/deadlock.mbz', $tempdir);
214
215        $this->setAdminUser();
216        $controller = new restore_controller(
217            'deadlock',
218            $this->courseid,
219            backup::INTERACTIVE_NO,
220            backup::MODE_GENERAL,
221            $USER->id,
222            backup::TARGET_NEW_COURSE
223        );
224        $this->assertTrue($controller->execute_precheck());
225        $controller->execute_plan();
226        $controller->destroy();
227    }
228}
229
230
231/**
232 * Progress class that records the result of restore_controller::is_executing calls.
233 *
234 * @package core_backup
235 * @copyright 2014 The Open University
236 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
237 */
238class core_backup_progress_restore_is_executing extends \core\progress\base {
239    /** @var array Array of results from calling function */
240    public $executing = array();
241
242    public function update_progress() {
243        $this->executing[] = restore_controller::is_executing();
244    }
245}
246
247
248/*
249 * helper extended @backup_controller class that makes some methods public for testing
250 */
251class mock_backup_controller extends backup_controller {
252
253    public function save_controller($includeobj = true, $cleanobj = false) {
254        parent::save_controller($includeobj, $cleanobj);
255    }
256}
257