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