Proyectos de Subversion Moodle

Rev

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