See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]
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 tool_recyclebin; 18 19 /** 20 * Recycle bin course tests. 21 * 22 * @package tool_recyclebin 23 * @copyright 2015 University of Kent 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 class course_bin_test extends \advanced_testcase { 27 28 /** 29 * @var \stdClass $course 30 */ 31 protected $course; 32 33 /** 34 * @var stdClass the quiz record 35 */ 36 protected $quiz; 37 38 /** 39 * Setup for each test. 40 */ 41 protected function setUp(): void { 42 $this->resetAfterTest(true); 43 $this->setAdminUser(); 44 45 // We want the course bin to be enabled. 46 set_config('coursebinenable', 1, 'tool_recyclebin'); 47 48 $this->course = $this->getDataGenerator()->create_course(); 49 $this->quiz = $this->getDataGenerator()->get_plugin_generator('mod_quiz')->create_instance(array( 50 'course' => $this->course->id, 'grade' => 100.0, 'sumgrades' => 1 51 )); 52 } 53 54 /** 55 * Check that our hook is called when an activity is deleted. 56 */ 57 public function test_pre_course_module_delete_hook() { 58 global $DB; 59 60 // Should have nothing in the recycle bin. 61 $this->assertEquals(0, $DB->count_records('tool_recyclebin_course')); 62 63 // Delete the course module. 64 course_delete_module($this->quiz->cmid); 65 66 // Now, run the course module deletion adhoc task. 67 \phpunit_util::run_all_adhoc_tasks(); 68 69 // Check the course module is now in the recycle bin. 70 $this->assertEquals(1, $DB->count_records('tool_recyclebin_course')); 71 72 // Try with the API. 73 $recyclebin = new \tool_recyclebin\course_bin($this->course->id); 74 $this->assertEquals(1, count($recyclebin->get_items())); 75 } 76 77 /** 78 * Test that we can restore recycle bin items. 79 */ 80 public function test_restore() { 81 global $DB; 82 83 $startcount = $DB->count_records('course_modules'); 84 85 // Delete the course module. 86 course_delete_module($this->quiz->cmid); 87 88 // Try restoring. 89 $recyclebin = new \tool_recyclebin\course_bin($this->course->id); 90 foreach ($recyclebin->get_items() as $item) { 91 $recyclebin->restore_item($item); 92 } 93 94 // Check that it was restored and removed from the recycle bin. 95 $this->assertEquals($startcount, $DB->count_records('course_modules')); 96 $this->assertEquals(0, count($recyclebin->get_items())); 97 } 98 99 /** 100 * Test that we can delete recycle bin items. 101 */ 102 public function test_delete() { 103 global $DB; 104 105 $startcount = $DB->count_records('course_modules'); 106 107 // Delete the course module. 108 course_delete_module($this->quiz->cmid); 109 110 // Now, run the course module deletion adhoc task. 111 \phpunit_util::run_all_adhoc_tasks(); 112 113 // Try purging. 114 $recyclebin = new \tool_recyclebin\course_bin($this->course->id); 115 foreach ($recyclebin->get_items() as $item) { 116 $recyclebin->delete_item($item); 117 } 118 119 // Item was deleted, so no course module was restored. 120 $this->assertEquals($startcount - 1, $DB->count_records('course_modules')); 121 $this->assertEquals(0, count($recyclebin->get_items())); 122 } 123 124 /** 125 * Test the cleanup task. 126 */ 127 public function test_cleanup_task() { 128 global $DB; 129 130 set_config('coursebinexpiry', WEEKSECS, 'tool_recyclebin'); 131 132 // Delete the quiz. 133 course_delete_module($this->quiz->cmid); 134 135 // Now, run the course module deletion adhoc task. 136 \phpunit_util::run_all_adhoc_tasks(); 137 138 // Set deleted date to the distant past. 139 $recyclebin = new \tool_recyclebin\course_bin($this->course->id); 140 foreach ($recyclebin->get_items() as $item) { 141 $item->timecreated = time() - WEEKSECS; 142 $DB->update_record('tool_recyclebin_course', $item); 143 } 144 145 // Create another module we are going to delete, but not alter the time it was placed in the recycle bin. 146 $book = $this->getDataGenerator()->get_plugin_generator('mod_book')->create_instance(array( 147 'course' => $this->course->id)); 148 149 course_delete_module($book->cmid); 150 151 // Now, run the course module deletion adhoc task. 152 \phpunit_util::run_all_adhoc_tasks(); 153 154 // Should have 2 items now. 155 $this->assertEquals(2, count($recyclebin->get_items())); 156 157 // Execute cleanup task. 158 $this->expectOutputRegex("/\[tool_recyclebin\] Deleting item '\d+' from the course recycle bin/"); 159 $task = new \tool_recyclebin\task\cleanup_course_bin(); 160 $task->execute(); 161 162 // Should only have the book as it was not due to be deleted. 163 $items = $recyclebin->get_items(); 164 $this->assertEquals(1, count($items)); 165 $deletedbook = reset($items); 166 $this->assertEquals($book->name, $deletedbook->name); 167 } 168 169 /** 170 * Provider for test_coursemodule_restore_with_userdata() and test_coursemodule_restore_without_userdata() 171 * 172 * Used to verify that recycle bin is immune to various settings. Provides plugin, name, value for 173 * direct usage with set_config() 174 */ 175 public function recycle_bin_settings_provider() { 176 return [ 177 'backup/backup_auto_storage moodle' => [[ 178 (object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 0], 179 ]], 180 181 'backup/backup_auto_storage external' => [[ 182 (object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 1], 183 (object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true], 184 ]], 185 186 'backup/backup_auto_storage mixed' => [[ 187 (object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 2], 188 (object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true], 189 ]], 190 191 'restore/restore_general_users moodle' => [[ 192 (object)['plugin' => 'restore', 'name' => 'restore_general_users', 'value' => 0], 193 (object)['plugin' => 'restore', 'name' => 'restore_general_groups', 'value' => 0], 194 ]], 195 ]; 196 } 197 198 /** 199 * Tests that user data is restored when module is restored. 200 * 201 * @dataProvider recycle_bin_settings_provider 202 * @param array $settings array of plugin, name, value stdClass(). 203 */ 204 public function test_coursemodule_restore_with_userdata($settings) { 205 // Force configuration changes from provider. 206 foreach ($settings as $setting) { 207 // Need to create a directory for backup_auto_destination. 208 if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) { 209 $setting->value = make_request_directory(); 210 } 211 set_config($setting->name, $setting->value, $setting->plugin); 212 } 213 214 $student = $this->getDataGenerator()->create_and_enrol($this->course, 'student'); 215 $this->setUser($student); 216 217 set_config('backup_auto_users', true, 'backup'); 218 $this->create_quiz_attempt($this->quiz, $student); 219 220 // Delete quiz. 221 $cm = get_coursemodule_from_instance('quiz', $this->quiz->id); 222 course_delete_module($cm->id); 223 \phpunit_util::run_all_adhoc_tasks(); 224 $quizzes = get_coursemodules_in_course('quiz', $this->course->id); 225 $this->assertEquals(0, count($quizzes)); 226 227 // Restore quiz. 228 $recyclebin = new \tool_recyclebin\course_bin($this->course->id); 229 foreach ($recyclebin->get_items() as $item) { 230 $recyclebin->restore_item($item); 231 } 232 $quizzes = get_coursemodules_in_course('quiz', $this->course->id); 233 $this->assertEquals(1, count($quizzes)); 234 $cm = array_pop($quizzes); 235 236 // Check if user quiz attempt data is restored. 237 $attempts = quiz_get_user_attempts($cm->instance, $student->id); 238 $this->assertEquals(1, count($attempts)); 239 $attempt = array_pop($attempts); 240 $attemptobj = \quiz_attempt::create($attempt->id); 241 $this->assertEquals($student->id, $attemptobj->get_userid()); 242 $this->assertEquals(true, $attemptobj->is_finished()); 243 } 244 245 /** 246 * Test that the activity is NOT stored in bin when 247 * in Automated backup setup settings "backup_auto_activities" is disabled. 248 * 249 * @dataProvider recycle_bin_settings_provider 250 * @covers ::store_item 251 */ 252 public function test_coursemodule_restore_with_activity_setting_disabled() { 253 254 // Set the configuration to not include activities in the automated backup. 255 set_config('backup_auto_activities', false, 'backup'); 256 257 // Delete the course module. 258 course_delete_module($this->quiz->cmid); 259 260 // Now, run the course module deletion adhoc task. 261 \phpunit_util::run_all_adhoc_tasks(); 262 263 // Check there is no items in the recycle bin. 264 $recyclebin = new \tool_recyclebin\course_bin($this->course->id); 265 $this->assertEquals(0, count($recyclebin->get_items())); 266 } 267 268 /** 269 * Tests that user data is not restored when module is restored. 270 * 271 * @dataProvider recycle_bin_settings_provider 272 * @param array $settings array of plugin, name, value stdClass(). 273 */ 274 public function test_coursemodule_restore_without_userdata($settings) { 275 // Force configuration changes from provider. 276 foreach ($settings as $setting) { 277 // Need to create a directory for backup_auto_destination. 278 if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) { 279 $setting->value = make_request_directory(); 280 } 281 set_config($setting->name, $setting->value, $setting->plugin); 282 } 283 284 $student = $this->getDataGenerator()->create_and_enrol($this->course, 'student'); 285 $this->setUser($student); 286 287 set_config('backup_auto_users', false, 'backup'); 288 $this->create_quiz_attempt($this->quiz, $student); 289 290 // Delete quiz. 291 $cm = get_coursemodule_from_instance('quiz', $this->quiz->id); 292 course_delete_module($cm->id); 293 \phpunit_util::run_all_adhoc_tasks(); 294 $quizzes = get_coursemodules_in_course('quiz', $this->course->id); 295 $this->assertEquals(0, count($quizzes)); 296 297 // Restore quiz. 298 $recyclebin = new \tool_recyclebin\course_bin($this->course->id); 299 foreach ($recyclebin->get_items() as $item) { 300 $recyclebin->restore_item($item); 301 } 302 $quizzes = get_coursemodules_in_course('quiz', $this->course->id); 303 $this->assertEquals(1, count($quizzes)); 304 $cm = array_pop($quizzes); 305 306 // Check if user quiz attempt data is restored. 307 $attempts = quiz_get_user_attempts($cm->instance, $student->id); 308 $this->assertEquals(0, count($attempts)); 309 } 310 311 /** 312 * Add a question to quiz and create a quiz attempt. 313 * @param \stdClass $quiz Quiz 314 * @param \stdClass $student User 315 * @throws coding_exception 316 * @throws moodle_exception 317 */ 318 private function create_quiz_attempt($quiz, $student) { 319 // Add Question. 320 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 321 $cat = $questiongenerator->create_question_category(); 322 $numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id)); 323 quiz_add_quiz_question($numq->id, $quiz); 324 325 // Create quiz attempt. 326 $quizobj = \quiz::create($quiz->id, $student->id); 327 $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); 328 $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); 329 $timenow = time(); 330 $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $student->id); 331 quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow); 332 quiz_attempt_save_started($quizobj, $quba, $attempt); 333 $attemptobj = \quiz_attempt::create($attempt->id); 334 $tosubmit = array(1 => array('answer' => '0')); 335 $attemptobj->process_submitted_actions($timenow, false, $tosubmit); 336 $attemptobj = \quiz_attempt::create($attempt->id); 337 $attemptobj->process_finish($timenow, false); 338 } 339 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body