weekly release 2.6dev
[moodle.git] / course / tests / courselib_test.php
CommitLineData
354b214c
PS
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 * Course related unit tests
19 *
20 * @package core
21 * @category phpunit
22 * @copyright 2012 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
f70bfb84
FM
28global $CFG;
29require_once($CFG->dirroot.'/course/lib.php');
354b214c 30
8252b7c2 31class core_course_courselib_testcase extends advanced_testcase {
354b214c 32
dd5d933f 33 /**
7bf4f6e9
JM
34 * Set forum specific test values for calling create_module().
35 *
36 * @param object $moduleinfo - the moduleinfo to add some specific values - passed in reference.
dd5d933f 37 */
7bf4f6e9
JM
38 private function forum_create_set_values(&$moduleinfo) {
39 // Completion specific to forum - optional.
40 $moduleinfo->completionposts = 3;
41 $moduleinfo->completiondiscussions = 1;
42 $moduleinfo->completionreplies = 2;
dd5d933f 43
7cb0ea2c 44 // Specific values to the Forum module.
dd5d933f
JM
45 $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE;
46 $moduleinfo->type = 'single';
7bf4f6e9
JM
47 $moduleinfo->trackingtype = FORUM_TRACKING_ON;
48 $moduleinfo->maxbytes = 10240;
49 $moduleinfo->maxattachments = 2;
50
7cb0ea2c 51 // Post threshold for blocking - specific to forum.
7bf4f6e9
JM
52 $moduleinfo->blockperiod = 60*60*24;
53 $moduleinfo->blockafter = 10;
54 $moduleinfo->warnafter = 5;
55 }
dd5d933f 56
7bf4f6e9
JM
57 /**
58 * Execute test asserts on the saved DB data by create_module($forum).
59 *
60 * @param object $moduleinfo - the specific forum values that were used to create a forum.
61 * @param object $dbmodinstance - the DB values of the created forum.
62 */
63 private function forum_create_run_asserts($moduleinfo, $dbmodinstance) {
64 // Compare values specific to forums.
65 $this->assertEquals($moduleinfo->forcesubscribe, $dbmodinstance->forcesubscribe);
66 $this->assertEquals($moduleinfo->type, $dbmodinstance->type);
67 $this->assertEquals($moduleinfo->assessed, $dbmodinstance->assessed);
68 $this->assertEquals($moduleinfo->completionposts, $dbmodinstance->completionposts);
69 $this->assertEquals($moduleinfo->completiondiscussions, $dbmodinstance->completiondiscussions);
70 $this->assertEquals($moduleinfo->completionreplies, $dbmodinstance->completionreplies);
71 $this->assertEquals($moduleinfo->scale, $dbmodinstance->scale);
72 $this->assertEquals($moduleinfo->assesstimestart, $dbmodinstance->assesstimestart);
73 $this->assertEquals($moduleinfo->assesstimefinish, $dbmodinstance->assesstimefinish);
74 $this->assertEquals($moduleinfo->rsstype, $dbmodinstance->rsstype);
75 $this->assertEquals($moduleinfo->rssarticles, $dbmodinstance->rssarticles);
76 $this->assertEquals($moduleinfo->trackingtype, $dbmodinstance->trackingtype);
77 $this->assertEquals($moduleinfo->maxbytes, $dbmodinstance->maxbytes);
78 $this->assertEquals($moduleinfo->maxattachments, $dbmodinstance->maxattachments);
79 $this->assertEquals($moduleinfo->blockperiod, $dbmodinstance->blockperiod);
80 $this->assertEquals($moduleinfo->blockafter, $dbmodinstance->blockafter);
81 $this->assertEquals($moduleinfo->warnafter, $dbmodinstance->warnafter);
82 }
dd5d933f 83
7bf4f6e9
JM
84 /**
85 * Set assign module specific test values for calling create_module().
86 *
87 * @param object $moduleinfo - the moduleinfo to add some specific values - passed in reference.
88 */
89 private function assign_create_set_values(&$moduleinfo) {
7cb0ea2c 90 // Specific values to the Assign module.
dd5d933f
JM
91 $moduleinfo->alwaysshowdescription = true;
92 $moduleinfo->submissiondrafts = true;
93 $moduleinfo->requiresubmissionstatement = true;
94 $moduleinfo->sendnotifications = true;
95 $moduleinfo->sendlatenotifications = true;
7bf4f6e9
JM
96 $moduleinfo->duedate = time() + (7 * 24 * 3600);
97 $moduleinfo->cutoffdate = time() + (7 * 24 * 3600);
98 $moduleinfo->allowsubmissionsfromdate = time();
dd5d933f
JM
99 $moduleinfo->teamsubmission = true;
100 $moduleinfo->requireallteammemberssubmit = true;
101 $moduleinfo->teamsubmissiongroupingid = true;
102 $moduleinfo->blindmarking = true;
7f198356
DW
103 $moduleinfo->markingworkflow = true;
104 $moduleinfo->markingallocation = true;
dd5d933f
JM
105 $moduleinfo->assignsubmission_onlinetext_enabled = true;
106 $moduleinfo->assignsubmission_file_enabled = true;
107 $moduleinfo->assignsubmission_file_maxfiles = 1;
108 $moduleinfo->assignsubmission_file_maxsizebytes = 1000000;
109 $moduleinfo->assignsubmission_comments_enabled = true;
110 $moduleinfo->assignfeedback_comments_enabled = true;
111 $moduleinfo->assignfeedback_offline_enabled = true;
112 $moduleinfo->assignfeedback_file_enabled = true;
113
7cb0ea2c 114 // Advanced grading.
7bf4f6e9
JM
115 $gradingmethods = grading_manager::available_methods();
116 $moduleinfo->advancedgradingmethod_submissions = current(array_keys($gradingmethods));
117 }
dd5d933f 118
7bf4f6e9
JM
119 /**
120 * Execute test asserts on the saved DB data by create_module($assign).
121 *
122 * @param object $moduleinfo - the specific assign module values that were used to create an assign module.
123 * @param object $dbmodinstance - the DB values of the created assign module.
124 */
125 private function assign_create_run_asserts($moduleinfo, $dbmodinstance) {
126 global $DB;
127
128 $this->assertEquals($moduleinfo->alwaysshowdescription, $dbmodinstance->alwaysshowdescription);
129 $this->assertEquals($moduleinfo->submissiondrafts, $dbmodinstance->submissiondrafts);
130 $this->assertEquals($moduleinfo->requiresubmissionstatement, $dbmodinstance->requiresubmissionstatement);
131 $this->assertEquals($moduleinfo->sendnotifications, $dbmodinstance->sendnotifications);
132 $this->assertEquals($moduleinfo->duedate, $dbmodinstance->duedate);
133 $this->assertEquals($moduleinfo->cutoffdate, $dbmodinstance->cutoffdate);
134 $this->assertEquals($moduleinfo->allowsubmissionsfromdate, $dbmodinstance->allowsubmissionsfromdate);
135 $this->assertEquals($moduleinfo->teamsubmission, $dbmodinstance->teamsubmission);
136 $this->assertEquals($moduleinfo->requireallteammemberssubmit, $dbmodinstance->requireallteammemberssubmit);
137 $this->assertEquals($moduleinfo->teamsubmissiongroupingid, $dbmodinstance->teamsubmissiongroupingid);
138 $this->assertEquals($moduleinfo->blindmarking, $dbmodinstance->blindmarking);
7f198356
DW
139 $this->assertEquals($moduleinfo->markingworkflow, $dbmodinstance->markingworkflow);
140 $this->assertEquals($moduleinfo->markingallocation, $dbmodinstance->markingallocation);
7bf4f6e9
JM
141 // The goal not being to fully test assign_add_instance() we'll stop here for the assign tests - to avoid too many DB queries.
142
7cb0ea2c 143 // Advanced grading.
7bf4f6e9
JM
144 $contextmodule = context_module::instance($dbmodinstance->id);
145 $advancedgradingmethod = $DB->get_record('grading_areas',
146 array('contextid' => $contextmodule->id,
147 'activemethod' => $moduleinfo->advancedgradingmethod_submissions));
148 $this->assertEquals($moduleinfo->advancedgradingmethod_submissions, $advancedgradingmethod);
149 }
150
151 /**
152 * Run some asserts test for a specific module for the function create_module().
153 *
154 * The function has been created (and is called) for $this->test_create_module().
155 * Note that the call to MODULE_create_set_values and MODULE_create_run_asserts are done after the common set values/run asserts.
156 * So if you want, you can overwrite the default values/asserts in the respective functions.
157 * @param string $modulename Name of the module ('forum', 'assign', 'book'...).
158 */
159 private function create_specific_module_test($modulename) {
160 global $DB, $CFG;
161
162 $this->resetAfterTest(true);
163
164 $this->setAdminUser();
165
166 // Warnings: you'll need to change this line if ever you come to test a module not following Moodle standard.
167 require_once($CFG->dirroot.'/mod/'. $modulename .'/lib.php');
168
169 // Enable avaibility.
170 // If not enabled all conditional fields will be ignored.
171 set_config('enableavailability', 1);
172
173 // Enable course completion.
174 // If not enabled all completion settings will be ignored.
175 set_config('enablecompletion', COMPLETION_ENABLED);
176
177 // Enable forum RSS feeds.
178 set_config('enablerssfeeds', 1);
179 set_config('forum_enablerssfeeds', 1);
180
181 $course = $this->getDataGenerator()->create_course(array('numsections'=>1, 'enablecompletion' => COMPLETION_ENABLED),
182 array('createsections'=>true));
183
184 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
dd5d933f 185
7cb0ea2c 186 // Create assign module instance for test.
7bf4f6e9
JM
187 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
188 $params['course'] = $course->id;
189 $instance = $generator->create_instance($params);
190 $assigncm = get_coursemodule_from_instance('assign', $instance->id);
dd5d933f 191
7bf4f6e9
JM
192 // Module test values.
193 $moduleinfo = new stdClass();
dd5d933f 194
7bf4f6e9
JM
195 // Always mandatory generic values to any module.
196 $moduleinfo->modulename = $modulename;
197 $moduleinfo->section = 1; // This is the section number in the course. Not the section id in the database.
198 $moduleinfo->course = $course->id;
199 $moduleinfo->groupingid = $grouping->id;
200 $moduleinfo->groupmembersonly = 0;
201 $moduleinfo->visible = true;
202
203 // Sometimes optional generic values for some modules.
204 $moduleinfo->name = 'My test module';
205 $moduleinfo->showdescription = 1; // standard boolean
206 require_once($CFG->libdir . '/gradelib.php');
207 $gradecats = grade_get_categories_menu($moduleinfo->course, false);
208 $gradecatid = current(array_keys($gradecats)); // Retrieve the first key of $gradecats
209 $moduleinfo->gradecat = $gradecatid;
210 $moduleinfo->groupmode = VISIBLEGROUPS;
211 $moduleinfo->cmidnumber = 'idnumber_XXX';
212
213 // Completion common to all module.
214 $moduleinfo->completion = COMPLETION_TRACKING_AUTOMATIC;
215 $moduleinfo->completionview = COMPLETION_VIEW_REQUIRED;
7cb0ea2c 216 $moduleinfo->completiongradeitemnumber = 1;
7bf4f6e9
JM
217 $moduleinfo->completionexpected = time() + (7 * 24 * 3600);
218
219 // Conditional activity.
220 $moduleinfo->availablefrom = time();
221 $moduleinfo->availableuntil = time() + (7 * 24 * 3600);
222 $moduleinfo->showavailability = CONDITION_STUDENTVIEW_SHOW;
223 $coursegradeitem = grade_item::fetch_course_item($moduleinfo->course); //the activity will become available only when the user reach some grade into the course itself.
224 $moduleinfo->conditiongradegroup = array(array('conditiongradeitemid' => $coursegradeitem->id, 'conditiongrademin' => 10, 'conditiongrademax' => 80));
225 $moduleinfo->conditionfieldgroup = array(array('conditionfield' => 'email', 'conditionfieldoperator' => OP_CONTAINS, 'conditionfieldvalue' => '@'));
226 $moduleinfo->conditioncompletiongroup = array(array('conditionsourcecmid' => $assigncm->id, 'conditionrequiredcompletion' => COMPLETION_COMPLETE)); // "conditionsourcecmid == 0" => none
227
228 // Grading and Advanced grading.
229 require_once($CFG->dirroot . '/rating/lib.php');
230 $moduleinfo->assessed = RATING_AGGREGATE_AVERAGE;
231 $moduleinfo->scale = 10; // Note: it could be minus (for specific course scale). It is a signed number.
232 $moduleinfo->assesstimestart = time();
233 $moduleinfo->assesstimefinish = time() + (7 * 24 * 3600);
234
7cb0ea2c 235 // RSS.
7bf4f6e9
JM
236 $moduleinfo->rsstype = 2;
237 $moduleinfo->rssarticles = 10;
238
239 // Optional intro editor (depends of module).
7bf4f6e9
JM
240 $draftid_editor = 0;
241 file_prepare_draft_area($draftid_editor, null, null, null, null);
242 $moduleinfo->introeditor = array('text' => 'This is a module', 'format' => FORMAT_HTML, 'itemid' => $draftid_editor);
dd5d933f 243
7bf4f6e9
JM
244 // Following is the advanced grading method area called 'submissions' for the 'assign' module.
245 if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) {
246 $moduleinfo->grade = 100;
247 }
7cb0ea2c 248
7bf4f6e9
JM
249 // Plagiarism form values.
250 // No plagiarism plugin installed by default. Use this space to make your own test.
251
252 // Values specific to the module.
253 $modulesetvalues = $modulename.'_create_set_values';
254 $this->$modulesetvalues($moduleinfo);
255
256 // Create the module.
257 $result = create_module($moduleinfo);
258
259 // Retrieve the module info.
260 $dbmodinstance = $DB->get_record($moduleinfo->modulename, array('id' => $result->instance));
261 $dbcm = get_coursemodule_from_instance($moduleinfo->modulename, $result->instance);
262 // We passed the course section number to create_courses but $dbcm contain the section id.
263 // We need to retrieve the db course section number.
264 $section = $DB->get_record('course_sections', array('course' => $dbcm->course, 'id' => $dbcm->section));
7cb0ea2c 265 // Retrieve the grade item.
7bf4f6e9
JM
266 $gradeitem = $DB->get_record('grade_items', array('courseid' => $moduleinfo->course,
267 'iteminstance' => $dbmodinstance->id, 'itemmodule' => $moduleinfo->modulename));
268
269 // Compare the values common to all module instances.
270 $this->assertEquals($moduleinfo->modulename, $dbcm->modname);
271 $this->assertEquals($moduleinfo->section, $section->section);
272 $this->assertEquals($moduleinfo->course, $dbcm->course);
273 $this->assertEquals($moduleinfo->groupingid, $dbcm->groupingid);
274 $this->assertEquals($moduleinfo->groupmembersonly, $dbcm->groupmembersonly);
275 $this->assertEquals($moduleinfo->visible, $dbcm->visible);
276 $this->assertEquals($moduleinfo->completion, $dbcm->completion);
277 $this->assertEquals($moduleinfo->completionview, $dbcm->completionview);
278 $this->assertEquals($moduleinfo->completiongradeitemnumber, $dbcm->completiongradeitemnumber);
279 $this->assertEquals($moduleinfo->completionexpected, $dbcm->completionexpected);
280 $this->assertEquals($moduleinfo->availablefrom, $dbcm->availablefrom);
281 $this->assertEquals($moduleinfo->availableuntil, $dbcm->availableuntil);
282 $this->assertEquals($moduleinfo->showavailability, $dbcm->showavailability);
283 $this->assertEquals($moduleinfo->showdescription, $dbcm->showdescription);
284 $this->assertEquals($moduleinfo->groupmode, $dbcm->groupmode);
285 $this->assertEquals($moduleinfo->cmidnumber, $dbcm->idnumber);
286 $this->assertEquals($moduleinfo->gradecat, $gradeitem->categoryid);
287
288 // Optional grade testing.
289 if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) {
290 $this->assertEquals($moduleinfo->grade, $dbmodinstance->grade);
291 }
292
293 // Some optional (but quite common) to some module.
294 $this->assertEquals($moduleinfo->name, $dbmodinstance->name);
295 $this->assertEquals($moduleinfo->intro, $dbmodinstance->intro);
296 $this->assertEquals($moduleinfo->introformat, $dbmodinstance->introformat);
297
298 // Common values when conditional activity is enabled.
299 foreach ($moduleinfo->conditionfieldgroup as $fieldgroup) {
300 $isfieldgroupsaved = $DB->count_records('course_modules_avail_fields', array('coursemoduleid' => $dbcm->id,
301 'userfield' => $fieldgroup['conditionfield'], 'operator' => $fieldgroup['conditionfieldoperator'],
302 'value' => $fieldgroup['conditionfieldvalue']));
303 $this->assertEquals(1, $isfieldgroupsaved);
304 }
305 foreach ($moduleinfo->conditiongradegroup as $gradegroup) {
306 $isgradegroupsaved = $DB->count_records('course_modules_availability', array('coursemoduleid' => $dbcm->id,
307 'grademin' => $gradegroup['conditiongrademin'], 'grademax' => $gradegroup['conditiongrademax'],
308 'gradeitemid' => $gradegroup['conditiongradeitemid']));
309 $this->assertEquals(1, $isgradegroupsaved);
310 }
311 foreach ($moduleinfo->conditioncompletiongroup as $completiongroup) {
312 $iscompletiongroupsaved = $DB->count_records('course_modules_availability', array('coursemoduleid' => $dbcm->id,
313 'sourcecmid' => $completiongroup['conditionsourcecmid'], 'requiredcompletion' => $completiongroup['conditionrequiredcompletion']));
314 $this->assertEquals(1, $iscompletiongroupsaved);
315 }
316
7cb0ea2c 317 // Test specific to the module.
7bf4f6e9
JM
318 $modulerunasserts = $modulename.'_create_run_asserts';
319 $this->$modulerunasserts($moduleinfo, $dbmodinstance);
b9b994c7 320 return $moduleinfo;
7bf4f6e9 321 }
dd5d933f 322
7bf4f6e9
JM
323 /**
324 * Test create_module() for multiple modules defined in the $modules array (first declaration of the function).
325 */
326 public function test_create_module() {
327 // Add the module name you want to test here.
7cb0ea2c 328 // Create the match MODULENAME_create_set_values() and MODULENAME_create_run_asserts().
7bf4f6e9
JM
329 $modules = array('forum', 'assign');
330 // Run all tests.
331 foreach ($modules as $modulename) {
332 $this->create_specific_module_test($modulename);
333 }
dd5d933f
JM
334 }
335
336 /**
7bf4f6e9 337 * Test update_module() for multiple modules defined in the $modules array (first declaration of the function).
dd5d933f
JM
338 */
339 public function test_update_module() {
7bf4f6e9 340 // Add the module name you want to test here.
7cb0ea2c 341 // Create the match MODULENAME_update_set_values() and MODULENAME_update_run_asserts().
7bf4f6e9
JM
342 $modules = array('forum');
343 // Run all tests.
344 foreach ($modules as $modulename) {
345 $this->update_specific_module_test($modulename);
346 }
347 }
348
349 /**
350 * Set forum specific test values for calling update_module().
351 *
352 * @param object $moduleinfo - the moduleinfo to add some specific values - passed in reference.
353 */
354 private function forum_update_set_values(&$moduleinfo) {
355 // Completion specific to forum - optional.
356 $moduleinfo->completionposts = 3;
357 $moduleinfo->completiondiscussions = 1;
358 $moduleinfo->completionreplies = 2;
359
7cb0ea2c 360 // Specific values to the Forum module.
7bf4f6e9
JM
361 $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE;
362 $moduleinfo->type = 'single';
363 $moduleinfo->trackingtype = FORUM_TRACKING_ON;
364 $moduleinfo->maxbytes = 10240;
365 $moduleinfo->maxattachments = 2;
366
7cb0ea2c 367 // Post threshold for blocking - specific to forum.
7bf4f6e9
JM
368 $moduleinfo->blockperiod = 60*60*24;
369 $moduleinfo->blockafter = 10;
370 $moduleinfo->warnafter = 5;
371 }
372
373 /**
374 * Execute test asserts on the saved DB data by update_module($forum).
375 *
376 * @param object $moduleinfo - the specific forum values that were used to update a forum.
377 * @param object $dbmodinstance - the DB values of the updated forum.
378 */
379 private function forum_update_run_asserts($moduleinfo, $dbmodinstance) {
380 // Compare values specific to forums.
381 $this->assertEquals($moduleinfo->forcesubscribe, $dbmodinstance->forcesubscribe);
382 $this->assertEquals($moduleinfo->type, $dbmodinstance->type);
383 $this->assertEquals($moduleinfo->assessed, $dbmodinstance->assessed);
384 $this->assertEquals($moduleinfo->completionposts, $dbmodinstance->completionposts);
385 $this->assertEquals($moduleinfo->completiondiscussions, $dbmodinstance->completiondiscussions);
386 $this->assertEquals($moduleinfo->completionreplies, $dbmodinstance->completionreplies);
387 $this->assertEquals($moduleinfo->scale, $dbmodinstance->scale);
388 $this->assertEquals($moduleinfo->assesstimestart, $dbmodinstance->assesstimestart);
389 $this->assertEquals($moduleinfo->assesstimefinish, $dbmodinstance->assesstimefinish);
390 $this->assertEquals($moduleinfo->rsstype, $dbmodinstance->rsstype);
391 $this->assertEquals($moduleinfo->rssarticles, $dbmodinstance->rssarticles);
392 $this->assertEquals($moduleinfo->trackingtype, $dbmodinstance->trackingtype);
393 $this->assertEquals($moduleinfo->maxbytes, $dbmodinstance->maxbytes);
394 $this->assertEquals($moduleinfo->maxattachments, $dbmodinstance->maxattachments);
395 $this->assertEquals($moduleinfo->blockperiod, $dbmodinstance->blockperiod);
396 $this->assertEquals($moduleinfo->blockafter, $dbmodinstance->blockafter);
397 $this->assertEquals($moduleinfo->warnafter, $dbmodinstance->warnafter);
398 }
399
400
401
402 /**
403 * Test a specific type of module.
404 *
405 * @param string $modulename - the module name to test
406 */
407 private function update_specific_module_test($modulename) {
dd5d933f
JM
408 global $DB, $CFG;
409
410 $this->resetAfterTest(true);
411
412 $this->setAdminUser();
413
7bf4f6e9
JM
414 // Warnings: you'll need to change this line if ever you come to test a module not following Moodle standard.
415 require_once($CFG->dirroot.'/mod/'. $modulename .'/lib.php');
dd5d933f 416
7bf4f6e9
JM
417 // Enable avaibility.
418 // If not enabled all conditional fields will be ignored.
419 set_config('enableavailability', 1);
420
421 // Enable course completion.
422 // If not enabled all completion settings will be ignored.
423 set_config('enablecompletion', COMPLETION_ENABLED);
424
425 // Enable forum RSS feeds.
426 set_config('enablerssfeeds', 1);
427 set_config('forum_enablerssfeeds', 1);
428
429 $course = $this->getDataGenerator()->create_course(array('numsections'=>1, 'enablecompletion' => COMPLETION_ENABLED),
dd5d933f
JM
430 array('createsections'=>true));
431
432 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
433
7cb0ea2c 434 // Create assign module instance for testing gradeitem.
7bf4f6e9
JM
435 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
436 $params['course'] = $course->id;
437 $instance = $generator->create_instance($params);
438 $assigncm = get_coursemodule_from_instance('assign', $instance->id);
dd5d933f 439
7cb0ea2c 440 // Create the test forum to update.
7bf4f6e9
JM
441 $initvalues = new stdClass();
442 $initvalues->introformat = FORMAT_HTML;
443 $initvalues->course = $course->id;
444 $forum = self::getDataGenerator()->create_module('forum', $initvalues);
dd5d933f 445
7cb0ea2c 446 // Retrieve course module.
7bf4f6e9 447 $cm = get_coursemodule_from_instance('forum', $forum->id);
dd5d933f 448
7bf4f6e9 449 // Module test values.
dd5d933f
JM
450 $moduleinfo = new stdClass();
451
7bf4f6e9 452 // Always mandatory generic values to any module.
dd5d933f 453 $moduleinfo->coursemodule = $cm->id;
7bf4f6e9
JM
454 $moduleinfo->modulename = $modulename;
455 $moduleinfo->course = $course->id;
456 $moduleinfo->groupingid = $grouping->id;
457 $moduleinfo->groupmembersonly = 0;
458 $moduleinfo->visible = true;
459
460 // Sometimes optional generic values for some modules.
461 $moduleinfo->name = 'My test module';
462 $moduleinfo->showdescription = 1; // standard boolean
463 require_once($CFG->libdir . '/gradelib.php');
464 $gradecats = grade_get_categories_menu($moduleinfo->course, false);
465 $gradecatid = current(array_keys($gradecats)); // Retrieve the first key of $gradecats
466 $moduleinfo->gradecat = $gradecatid;
467 $moduleinfo->groupmode = VISIBLEGROUPS;
468 $moduleinfo->cmidnumber = 'idnumber_XXX';
469
470 // Completion common to all module.
471 $moduleinfo->completion = COMPLETION_TRACKING_AUTOMATIC;
472 $moduleinfo->completionview = COMPLETION_VIEW_REQUIRED;
7cb0ea2c 473 $moduleinfo->completiongradeitemnumber = 1;
7bf4f6e9 474 $moduleinfo->completionexpected = time() + (7 * 24 * 3600);
69a0e65c 475 $moduleinfo->completionunlocked = 1;
7bf4f6e9
JM
476
477 // Conditional activity.
478 $moduleinfo->availablefrom = time();
479 $moduleinfo->availableuntil = time() + (7 * 24 * 3600);
480 $moduleinfo->showavailability = CONDITION_STUDENTVIEW_SHOW;
481 $coursegradeitem = grade_item::fetch_course_item($moduleinfo->course); //the activity will become available only when the user reach some grade into the course itself.
482 $moduleinfo->conditiongradegroup = array(array('conditiongradeitemid' => $coursegradeitem->id, 'conditiongrademin' => 10, 'conditiongrademax' => 80));
483 $moduleinfo->conditionfieldgroup = array(array('conditionfield' => 'email', 'conditionfieldoperator' => OP_CONTAINS, 'conditionfieldvalue' => '@'));
484 $moduleinfo->conditioncompletiongroup = array(array('conditionsourcecmid' => $assigncm->id, 'conditionrequiredcompletion' => COMPLETION_COMPLETE)); // "conditionsourcecmid == 0" => none
485
486 // Grading and Advanced grading.
487 require_once($CFG->dirroot . '/rating/lib.php');
488 $moduleinfo->assessed = RATING_AGGREGATE_AVERAGE;
489 $moduleinfo->scale = 10; // Note: it could be minus (for specific course scale). It is a signed number.
490 $moduleinfo->assesstimestart = time();
491 $moduleinfo->assesstimefinish = time() + (7 * 24 * 3600);
492
7cb0ea2c 493 // RSS.
7bf4f6e9
JM
494 $moduleinfo->rsstype = 2;
495 $moduleinfo->rssarticles = 10;
496
497 // Optional intro editor (depends of module).
dd5d933f
JM
498 $draftid_editor = 0;
499 file_prepare_draft_area($draftid_editor, null, null, null, null);
7bf4f6e9 500 $moduleinfo->introeditor = array('text' => 'This is a module', 'format' => FORMAT_HTML, 'itemid' => $draftid_editor);
dd5d933f 501
7bf4f6e9
JM
502 // Following is the advanced grading method area called 'submissions' for the 'assign' module.
503 if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) {
504 $moduleinfo->grade = 100;
505 }
506 // Plagiarism form values.
dd5d933f
JM
507 // No plagiarism plugin installed by default. Use this space to make your own test.
508
7bf4f6e9
JM
509 // Values specific to the module.
510 $modulesetvalues = $modulename.'_update_set_values';
511 $this->$modulesetvalues($moduleinfo);
512
513 // Create the module.
514 $result = update_module($moduleinfo);
515
516 // Retrieve the module info.
517 $dbmodinstance = $DB->get_record($moduleinfo->modulename, array('id' => $result->instance));
518 $dbcm = get_coursemodule_from_instance($moduleinfo->modulename, $result->instance);
7cb0ea2c 519 // Retrieve the grade item.
7bf4f6e9
JM
520 $gradeitem = $DB->get_record('grade_items', array('courseid' => $moduleinfo->course,
521 'iteminstance' => $dbmodinstance->id, 'itemmodule' => $moduleinfo->modulename));
522
523 // Compare the values common to all module instances.
524 $this->assertEquals($moduleinfo->modulename, $dbcm->modname);
525 $this->assertEquals($moduleinfo->course, $dbcm->course);
526 $this->assertEquals($moduleinfo->groupingid, $dbcm->groupingid);
527 $this->assertEquals($moduleinfo->groupmembersonly, $dbcm->groupmembersonly);
528 $this->assertEquals($moduleinfo->visible, $dbcm->visible);
529 $this->assertEquals($moduleinfo->completion, $dbcm->completion);
530 $this->assertEquals($moduleinfo->completionview, $dbcm->completionview);
531 $this->assertEquals($moduleinfo->completiongradeitemnumber, $dbcm->completiongradeitemnumber);
532 $this->assertEquals($moduleinfo->completionexpected, $dbcm->completionexpected);
533 $this->assertEquals($moduleinfo->availablefrom, $dbcm->availablefrom);
534 $this->assertEquals($moduleinfo->availableuntil, $dbcm->availableuntil);
535 $this->assertEquals($moduleinfo->showavailability, $dbcm->showavailability);
536 $this->assertEquals($moduleinfo->showdescription, $dbcm->showdescription);
537 $this->assertEquals($moduleinfo->groupmode, $dbcm->groupmode);
538 $this->assertEquals($moduleinfo->cmidnumber, $dbcm->idnumber);
539 $this->assertEquals($moduleinfo->gradecat, $gradeitem->categoryid);
540
541 // Optional grade testing.
542 if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) {
543 $this->assertEquals($moduleinfo->grade, $dbmodinstance->grade);
544 }
545
546 // Some optional (but quite common) to some module.
547 $this->assertEquals($moduleinfo->name, $dbmodinstance->name);
548 $this->assertEquals($moduleinfo->intro, $dbmodinstance->intro);
549 $this->assertEquals($moduleinfo->introformat, $dbmodinstance->introformat);
550
551 // Common values when conditional activity is enabled.
552 foreach ($moduleinfo->conditionfieldgroup as $fieldgroup) {
553 $isfieldgroupsaved = $DB->count_records('course_modules_avail_fields', array('coursemoduleid' => $dbcm->id,
554 'userfield' => $fieldgroup['conditionfield'], 'operator' => $fieldgroup['conditionfieldoperator'],
555 'value' => $fieldgroup['conditionfieldvalue']));
556 $this->assertEquals(1, $isfieldgroupsaved);
557 }
558 foreach ($moduleinfo->conditiongradegroup as $gradegroup) {
559 $isgradegroupsaved = $DB->count_records('course_modules_availability', array('coursemoduleid' => $dbcm->id,
560 'grademin' => $gradegroup['conditiongrademin'], 'grademax' => $gradegroup['conditiongrademax'],
561 'gradeitemid' => $gradegroup['conditiongradeitemid']));
562 $this->assertEquals(1, $isgradegroupsaved);
563 }
564 foreach ($moduleinfo->conditioncompletiongroup as $completiongroup) {
565 $iscompletiongroupsaved = $DB->count_records('course_modules_availability', array('coursemoduleid' => $dbcm->id,
566 'sourcecmid' => $completiongroup['conditionsourcecmid'], 'requiredcompletion' => $completiongroup['conditionrequiredcompletion']));
567 $this->assertEquals(1, $iscompletiongroupsaved);
568 }
569
7cb0ea2c 570 // Test specific to the module.
7bf4f6e9
JM
571 $modulerunasserts = $modulename.'_update_run_asserts';
572 $this->$modulerunasserts($moduleinfo, $dbmodinstance);
b9b994c7 573 return $moduleinfo;
7bf4f6e9 574 }
dd5d933f
JM
575
576
f70bfb84
FM
577 public function test_create_course() {
578 global $DB;
579 $this->resetAfterTest(true);
580 $defaultcategory = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
581
582 $course = new stdClass();
583 $course->fullname = 'Apu loves Unit Təsts';
584 $course->shortname = 'Spread the lŭve';
585 $course->idnumber = '123';
586 $course->summary = 'Awesome!';
587 $course->summaryformat = FORMAT_PLAIN;
588 $course->format = 'topics';
589 $course->newsitems = 0;
590 $course->numsections = 5;
591 $course->category = $defaultcategory;
97d5e39c 592 $original = (array) $course;
f70bfb84
FM
593
594 $created = create_course($course);
595 $context = context_course::instance($created->id);
596
597 // Compare original and created.
f70bfb84
FM
598 $this->assertEquals($original, array_intersect_key((array) $created, $original));
599
600 // Ensure default section is created.
601 $sectioncreated = $DB->record_exists('course_sections', array('course' => $created->id, 'section' => 0));
602 $this->assertTrue($sectioncreated);
603
604 // Ensure blocks have been associated to the course.
605 $blockcount = $DB->count_records('block_instances', array('parentcontextid' => $context->id));
606 $this->assertGreaterThan(0, $blockcount);
9930e426
CF
607
608 // Ensure that the shortname isn't duplicated.
609 try {
610 $created = create_course($course);
2793260f 611 $this->fail('Exception expected');
9930e426 612 } catch (moodle_exception $e) {
2793260f 613 $this->assertSame(get_string('shortnametaken', 'error', $course->shortname), $e->getMessage());
9930e426 614 }
9930e426
CF
615
616 // Ensure that the idnumber isn't duplicated.
617 $course->shortname .= '1';
618 try {
619 $created = create_course($course);
2793260f 620 $this->fail('Exception expected');
9930e426 621 } catch (moodle_exception $e) {
2793260f 622 $this->assertSame(get_string('courseidnumbertaken', 'error', $course->idnumber), $e->getMessage());
9930e426 623 }
f70bfb84
FM
624 }
625
384c3510
MG
626 public function test_create_course_with_generator() {
627 global $DB;
628 $this->resetAfterTest(true);
629 $course = $this->getDataGenerator()->create_course();
630
631 // Ensure default section is created.
632 $sectioncreated = $DB->record_exists('course_sections', array('course' => $course->id, 'section' => 0));
633 $this->assertTrue($sectioncreated);
634 }
635
636 public function test_create_course_sections() {
637 global $DB;
638 $this->resetAfterTest(true);
639
640 $course = $this->getDataGenerator()->create_course(
641 array('shortname' => 'GrowingCourse',
642 'fullname' => 'Growing Course',
643 'numsections' => 5),
644 array('createsections' => true));
645
4a3fb71c 646 // Ensure all 6 (0-5) sections were created and course content cache works properly
384c3510
MG
647 $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all());
648 $this->assertEquals(range(0, $course->numsections), $sectionscreated);
649
650 // this will do nothing, section already exists
651 $this->assertFalse(course_create_sections_if_missing($course, $course->numsections));
652
653 // this will create new section
654 $this->assertTrue(course_create_sections_if_missing($course, $course->numsections + 1));
655
656 // Ensure all 7 (0-6) sections were created and modinfo/sectioninfo cache works properly
657 $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all());
658 $this->assertEquals(range(0, $course->numsections + 1), $sectionscreated);
659 }
660
5536a561
FD
661 public function test_update_course() {
662 global $DB;
663
664 $this->resetAfterTest();
c3bf6181
MN
665
666 $defaultcategory = $DB->get_field_select('course_categories', 'MIN(id)', 'parent = 0');
5536a561
FD
667
668 $course = new stdClass();
669 $course->fullname = 'Apu loves Unit Təsts';
670 $course->shortname = 'test1';
671 $course->idnumber = '1';
672 $course->summary = 'Awesome!';
673 $course->summaryformat = FORMAT_PLAIN;
674 $course->format = 'topics';
675 $course->newsitems = 0;
676 $course->numsections = 5;
677 $course->category = $defaultcategory;
5536a561
FD
678
679 $created = create_course($course);
680 // Ensure the checks only work on idnumber/shortname that are not already ours.
c3bf6181 681 update_course($created);
5536a561
FD
682
683 $course->shortname = 'test2';
684 $course->idnumber = '2';
685
686 $created2 = create_course($course);
687
688 // Test duplicate idnumber.
689 $created2->idnumber = '1';
690 try {
691 update_course($created2);
692 $this->fail('Expected exception when trying to update a course with duplicate idnumber');
693 } catch (moodle_exception $e) {
694 $this->assertEquals(get_string('courseidnumbertaken', 'error', $created2->idnumber), $e->getMessage());
695 }
696
697 // Test duplicate shortname.
698 $created2->idnumber = '2';
699 $created2->shortname = 'test1';
5536a561
FD
700 try {
701 update_course($created2);
702 $this->fail('Expected exception when trying to update a course with a duplicate shortname');
703 } catch (moodle_exception $e) {
704 $this->assertEquals(get_string('shortnametaken', 'error', $created2->shortname), $e->getMessage());
705 }
706 }
707
5523c344 708 public function test_course_add_cm_to_section() {
709 global $DB;
710 $this->resetAfterTest(true);
711
712 // Create course with 1 section.
713 $course = $this->getDataGenerator()->create_course(
714 array('shortname' => 'GrowingCourse',
715 'fullname' => 'Growing Course',
716 'numsections' => 1),
717 array('createsections' => true));
718
719 // Trash modinfo.
720 rebuild_course_cache($course->id, true);
721
722 // Create some cms for testing.
723 $cmids = array();
724 for ($i=0; $i<4; $i++) {
725 $cmids[$i] = $DB->insert_record('course_modules', array('course' => $course->id));
726 }
727
728 // Add it to section that exists.
729 course_add_cm_to_section($course, $cmids[0], 1);
730
731 // Check it got added to sequence.
732 $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1));
733 $this->assertEquals($cmids[0], $sequence);
734
735 // Add a second, this time using courseid variant of parameters.
4a3fb71c 736 $coursecacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
5523c344 737 course_add_cm_to_section($course->id, $cmids[1], 1);
738 $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1));
739 $this->assertEquals($cmids[0] . ',' . $cmids[1], $sequence);
740
4a3fb71c
MG
741 // Check that modinfo cache was reset but not rebuilt (important for performance if calling repeatedly).
742 $this->assertGreaterThan($coursecacherev, $DB->get_field('course', 'cacherev', array('id' => $course->id)));
743 $this->assertEmpty(cache::make('core', 'coursemodinfo')->get($course->id));
5523c344 744
745 // Add one to section that doesn't exist (this might rebuild modinfo).
746 course_add_cm_to_section($course, $cmids[2], 2);
747 $this->assertEquals(3, $DB->count_records('course_sections', array('course' => $course->id)));
748 $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 2));
749 $this->assertEquals($cmids[2], $sequence);
750
751 // Add using the 'before' option.
752 course_add_cm_to_section($course, $cmids[3], 2, $cmids[2]);
753 $this->assertEquals(3, $DB->count_records('course_sections', array('course' => $course->id)));
754 $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 2));
755 $this->assertEquals($cmids[3] . ',' . $cmids[2], $sequence);
756 }
757
354b214c
PS
758 public function test_reorder_sections() {
759 global $DB;
760 $this->resetAfterTest(true);
761
762 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
763 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
764 $oldsections = array();
765 $sections = array();
3a222db2 766 foreach ($DB->get_records('course_sections', array('course'=>$course->id), 'id') as $section) {
354b214c
PS
767 $oldsections[$section->section] = $section->id;
768 $sections[$section->id] = $section->section;
769 }
770 ksort($oldsections);
771
772 $neworder = reorder_sections($sections, 2, 4);
773 $neworder = array_keys($neworder);
774 $this->assertEquals($oldsections[0], $neworder[0]);
775 $this->assertEquals($oldsections[1], $neworder[1]);
776 $this->assertEquals($oldsections[2], $neworder[4]);
777 $this->assertEquals($oldsections[3], $neworder[2]);
778 $this->assertEquals($oldsections[4], $neworder[3]);
779 $this->assertEquals($oldsections[5], $neworder[5]);
780 $this->assertEquals($oldsections[6], $neworder[6]);
781
eb01aa2c
RT
782 $neworder = reorder_sections($sections, 4, 2);
783 $neworder = array_keys($neworder);
784 $this->assertEquals($oldsections[0], $neworder[0]);
785 $this->assertEquals($oldsections[1], $neworder[1]);
786 $this->assertEquals($oldsections[2], $neworder[3]);
787 $this->assertEquals($oldsections[3], $neworder[4]);
788 $this->assertEquals($oldsections[4], $neworder[2]);
789 $this->assertEquals($oldsections[5], $neworder[5]);
790 $this->assertEquals($oldsections[6], $neworder[6]);
791
354b214c
PS
792 $neworder = reorder_sections(1, 2, 4);
793 $this->assertFalse($neworder);
794 }
795
3d8fe482 796 public function test_move_section_down() {
354b214c
PS
797 global $DB;
798 $this->resetAfterTest(true);
799
800 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
801 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
802 $oldsections = array();
803 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
804 $oldsections[$section->section] = $section->id;
805 }
806 ksort($oldsections);
807
3d8fe482 808 // Test move section down..
354b214c
PS
809 move_section_to($course, 2, 4);
810 $sections = array();
811 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
812 $sections[$section->section] = $section->id;
813 }
814 ksort($sections);
815
816 $this->assertEquals($oldsections[0], $sections[0]);
817 $this->assertEquals($oldsections[1], $sections[1]);
818 $this->assertEquals($oldsections[2], $sections[4]);
819 $this->assertEquals($oldsections[3], $sections[2]);
820 $this->assertEquals($oldsections[4], $sections[3]);
821 $this->assertEquals($oldsections[5], $sections[5]);
822 $this->assertEquals($oldsections[6], $sections[6]);
823 }
824
3d8fe482
DP
825 public function test_move_section_up() {
826 global $DB;
827 $this->resetAfterTest(true);
828
829 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
830 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
831 $oldsections = array();
832 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
833 $oldsections[$section->section] = $section->id;
834 }
835 ksort($oldsections);
836
837 // Test move section up..
838 move_section_to($course, 6, 4);
839 $sections = array();
840 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
841 $sections[$section->section] = $section->id;
842 }
843 ksort($sections);
844
845 $this->assertEquals($oldsections[0], $sections[0]);
846 $this->assertEquals($oldsections[1], $sections[1]);
847 $this->assertEquals($oldsections[2], $sections[2]);
848 $this->assertEquals($oldsections[3], $sections[3]);
849 $this->assertEquals($oldsections[4], $sections[5]);
850 $this->assertEquals($oldsections[5], $sections[6]);
851 $this->assertEquals($oldsections[6], $sections[4]);
852 }
853
854 public function test_move_section_marker() {
855 global $DB;
856 $this->resetAfterTest(true);
857
858 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
859 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
860
861 // Set course marker to the section we are going to move..
862 course_set_marker($course->id, 2);
863 // Verify that the course marker is set correctly.
864 $course = $DB->get_record('course', array('id' => $course->id));
865 $this->assertEquals(2, $course->marker);
866
867 // Test move the marked section down..
868 move_section_to($course, 2, 4);
869
870 // Verify that the coruse marker has been moved along with the section..
871 $course = $DB->get_record('course', array('id' => $course->id));
872 $this->assertEquals(4, $course->marker);
873
874 // Test move the marked section up..
875 move_section_to($course, 4, 3);
876
877 // Verify that the course marker has been moved along with the section..
878 $course = $DB->get_record('course', array('id' => $course->id));
879 $this->assertEquals(3, $course->marker);
880
881 // Test moving a non-marked section above the marked section..
882 move_section_to($course, 4, 2);
883
884 // Verify that the course marker has been moved down to accomodate..
885 $course = $DB->get_record('course', array('id' => $course->id));
886 $this->assertEquals(4, $course->marker);
887
888 // Test moving a non-marked section below the marked section..
889 move_section_to($course, 3, 6);
890
891 // Verify that the course marker has been up to accomodate..
892 $course = $DB->get_record('course', array('id' => $course->id));
893 $this->assertEquals(3, $course->marker);
894 }
895
354b214c
PS
896 public function test_get_course_display_name_for_list() {
897 global $CFG;
898 $this->resetAfterTest(true);
899
900 $course = $this->getDataGenerator()->create_course(array('shortname' => 'FROG101', 'fullname' => 'Introduction to pond life'));
901
902 $CFG->courselistshortnames = 0;
903 $this->assertEquals('Introduction to pond life', get_course_display_name_for_list($course));
904
905 $CFG->courselistshortnames = 1;
906 $this->assertEquals('FROG101 Introduction to pond life', get_course_display_name_for_list($course));
907 }
b1a8aa73 908
384c3510 909 public function test_move_module_in_course() {
3f61e4cb
ARN
910 global $DB;
911
384c3510
MG
912 $this->resetAfterTest(true);
913 // Setup fixture
3f61e4cb 914 $course = $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections' => true));
384c3510
MG
915 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
916
384c3510
MG
917 $cms = get_fast_modinfo($course)->get_cms();
918 $cm = reset($cms);
919
3f61e4cb
ARN
920 $newsection = get_fast_modinfo($course)->get_section_info(3);
921 $oldsectionid = $cm->section;
922
923 // Perform the move
924 moveto_module($cm, $newsection);
384c3510 925
3f61e4cb
ARN
926 $cms = get_fast_modinfo($course)->get_cms();
927 $cm = reset($cms);
384c3510 928
3f61e4cb 929 // Check that the cached modinfo contains the correct section info
384c3510
MG
930 $modinfo = get_fast_modinfo($course);
931 $this->assertTrue(empty($modinfo->sections[0]));
932 $this->assertFalse(empty($modinfo->sections[3]));
3f61e4cb
ARN
933
934 // Check that the old section's sequence no longer contains this ID
935 $oldsection = $DB->get_record('course_sections', array('id' => $oldsectionid));
936 $oldsequences = explode(',', $newsection->sequence);
937 $this->assertFalse(in_array($cm->id, $oldsequences));
938
939 // Check that the new section's sequence now contains this ID
940 $newsection = $DB->get_record('course_sections', array('id' => $newsection->id));
941 $newsequences = explode(',', $newsection->sequence);
942 $this->assertTrue(in_array($cm->id, $newsequences));
943
944 // Check that the section number has been changed in the cm
945 $this->assertEquals($newsection->id, $cm->section);
946
947
948 // Perform a second move as some issues were only seen on the second move
949 $newsection = get_fast_modinfo($course)->get_section_info(2);
950 $oldsectionid = $cm->section;
d55f05ef 951 moveto_module($cm, $newsection);
3f61e4cb 952
3f61e4cb
ARN
953 $cms = get_fast_modinfo($course)->get_cms();
954 $cm = reset($cms);
955
956 // Check that the cached modinfo contains the correct section info
957 $modinfo = get_fast_modinfo($course);
958 $this->assertTrue(empty($modinfo->sections[0]));
959 $this->assertFalse(empty($modinfo->sections[2]));
960
961 // Check that the old section's sequence no longer contains this ID
962 $oldsection = $DB->get_record('course_sections', array('id' => $oldsectionid));
963 $oldsequences = explode(',', $newsection->sequence);
964 $this->assertFalse(in_array($cm->id, $oldsequences));
965
966 // Check that the new section's sequence now contains this ID
967 $newsection = $DB->get_record('course_sections', array('id' => $newsection->id));
968 $newsequences = explode(',', $newsection->sequence);
969 $this->assertTrue(in_array($cm->id, $newsequences));
384c3510 970 }
f7d6e650
FM
971
972 public function test_module_visibility() {
973 $this->setAdminUser();
974 $this->resetAfterTest(true);
975
976 // Create course and modules.
977 $course = $this->getDataGenerator()->create_course(array('numsections' => 5));
978 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
979 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 'course' => $course->id));
980 $modules = compact('forum', 'assign');
981
982 // Hiding the modules.
983 foreach ($modules as $mod) {
984 set_coursemodule_visible($mod->cmid, 0);
985 $this->check_module_visibility($mod, 0, 0);
986 }
987
988 // Showing the modules.
989 foreach ($modules as $mod) {
990 set_coursemodule_visible($mod->cmid, 1);
991 $this->check_module_visibility($mod, 1, 1);
992 }
993 }
994
995 public function test_section_visibility() {
996 $this->setAdminUser();
997 $this->resetAfterTest(true);
998
999 // Create course.
1000 $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true));
1001
1002 // Testing an empty section.
1003 $sectionnumber = 1;
1004 set_section_visible($course->id, $sectionnumber, 0);
1005 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1006 $this->assertEquals($section_info->visible, 0);
1007 set_section_visible($course->id, $sectionnumber, 1);
1008 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1009 $this->assertEquals($section_info->visible, 1);
1010
1011 // Testing a section with visible modules.
1012 $sectionnumber = 2;
1013 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
1014 array('section' => $sectionnumber));
1015 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(),
1016 'course' => $course->id), array('section' => $sectionnumber));
1017 $modules = compact('forum', 'assign');
1018 set_section_visible($course->id, $sectionnumber, 0);
1019 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1020 $this->assertEquals($section_info->visible, 0);
1021 foreach ($modules as $mod) {
1022 $this->check_module_visibility($mod, 0, 1);
1023 }
1024 set_section_visible($course->id, $sectionnumber, 1);
1025 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1026 $this->assertEquals($section_info->visible, 1);
1027 foreach ($modules as $mod) {
1028 $this->check_module_visibility($mod, 1, 1);
1029 }
1030
1031 // Testing a section with hidden modules, which should stay hidden.
1032 $sectionnumber = 3;
1033 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
1034 array('section' => $sectionnumber));
1035 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(),
1036 'course' => $course->id), array('section' => $sectionnumber));
1037 $modules = compact('forum', 'assign');
1038 foreach ($modules as $mod) {
1039 set_coursemodule_visible($mod->cmid, 0);
1040 $this->check_module_visibility($mod, 0, 0);
1041 }
1042 set_section_visible($course->id, $sectionnumber, 0);
1043 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1044 $this->assertEquals($section_info->visible, 0);
1045 foreach ($modules as $mod) {
1046 $this->check_module_visibility($mod, 0, 0);
1047 }
1048 set_section_visible($course->id, $sectionnumber, 1);
1049 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
1050 $this->assertEquals($section_info->visible, 1);
1051 foreach ($modules as $mod) {
1052 $this->check_module_visibility($mod, 0, 0);
1053 }
1054 }
1055
1056 /**
1057 * Helper function to assert that a module has correctly been made visible, or hidden.
1058 *
1059 * @param stdClass $mod module information
1060 * @param int $visibility the current state of the module
1061 * @param int $visibleold the current state of the visibleold property
1062 * @return void
1063 */
1064 public function check_module_visibility($mod, $visibility, $visibleold) {
1065 global $DB;
1066 $cm = get_fast_modinfo($mod->course)->get_cm($mod->cmid);
1067 $this->assertEquals($visibility, $cm->visible);
1068 $this->assertEquals($visibleold, $cm->visibleold);
1069
1070 // Check the module grade items.
1071 $grade_items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $cm->modname,
1072 'iteminstance' => $cm->instance, 'courseid' => $cm->course));
1073 if ($grade_items) {
1074 foreach ($grade_items as $grade_item) {
1075 if ($visibility) {
1076 $this->assertFalse($grade_item->is_hidden(), "$cm->modname grade_item not visible");
1077 } else {
1078 $this->assertTrue($grade_item->is_hidden(), "$cm->modname grade_item not hidden");
1079 }
1080 }
1081 }
1082
1083 // Check the events visibility.
1084 if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $cm->modname))) {
1085 foreach ($events as $event) {
1086 $calevent = new calendar_event($event);
1087 $this->assertEquals($visibility, $calevent->visible, "$cm->modname calendar_event visibility");
1088 }
1089 }
1090 }
1091
b7d3bb0f
AG
1092 public function test_course_page_type_list() {
1093 global $DB;
1094 $this->resetAfterTest(true);
1095
1096 // Create a category.
1097 $category = new stdClass();
1098 $category->name = 'Test Category';
1099
1100 $testcategory = $this->getDataGenerator()->create_category($category);
1101
1102 // Create a course.
1103 $course = new stdClass();
1104 $course->fullname = 'Apu loves Unit Təsts';
1105 $course->shortname = 'Spread the lŭve';
1106 $course->idnumber = '123';
1107 $course->summary = 'Awesome!';
1108 $course->summaryformat = FORMAT_PLAIN;
1109 $course->format = 'topics';
1110 $course->newsitems = 0;
1111 $course->numsections = 5;
1112 $course->category = $testcategory->id;
1113
1114 $testcourse = $this->getDataGenerator()->create_course($course);
1115
1116 // Create contexts.
1117 $coursecontext = context_course::instance($testcourse->id);
1118 $parentcontext = $coursecontext->get_parent_context(); // Not actually used.
1119 $pagetype = 'page-course-x'; // Not used either.
1120 $pagetypelist = course_page_type_list($pagetype, $parentcontext, $coursecontext);
1121
1122 // Page type lists for normal courses.
1123 $testpagetypelist1 = array();
1124 $testpagetypelist1['*'] = 'Any page';
1125 $testpagetypelist1['course-*'] = 'Any course page';
1126 $testpagetypelist1['course-view-*'] = 'Any type of course main page';
1127
1128 $this->assertEquals($testpagetypelist1, $pagetypelist);
1129
1130 // Get the context for the front page course.
1131 $sitecoursecontext = context_course::instance(SITEID);
1132 $pagetypelist = course_page_type_list($pagetype, $parentcontext, $sitecoursecontext);
1133
1134 // Page type list for the front page course.
1135 $testpagetypelist2 = array('*' => 'Any page');
1136 $this->assertEquals($testpagetypelist2, $pagetypelist);
1137
1138 // Make sure that providing no current context to the function doesn't result in an error.
1139 // Calls made from generate_page_type_patterns() may provide null values.
1140 $pagetypelist = course_page_type_list($pagetype, null, null);
1141 $this->assertEquals($pagetypelist, $testpagetypelist1);
1142 }
bf6b3c7a
AA
1143
1144 public function test_compare_activities_by_time_desc() {
1145
1146 // Let's create some test data.
1147 $activitiesivities = array();
1148 $x = new stdClass();
1149 $x->timestamp = null;
1150 $activities[] = $x;
1151
1152 $x = new stdClass();
1153 $x->timestamp = 1;
1154 $activities[] = $x;
1155
1156 $x = new stdClass();
1157 $x->timestamp = 3;
1158 $activities[] = $x;
1159
1160 $x = new stdClass();
1161 $x->timestamp = 0;
1162 $activities[] = $x;
1163
1164 $x = new stdClass();
1165 $x->timestamp = 5;
1166 $activities[] = $x;
1167
1168 $x = new stdClass();
1169 $activities[] = $x;
1170
1171 $x = new stdClass();
1172 $x->timestamp = 5;
1173 $activities[] = $x;
1174
1175 // Do the sorting.
1176 usort($activities, 'compare_activities_by_time_desc');
1177
1178 // Let's check the result.
1179 $last = 10;
1180 foreach($activities as $activity) {
1181 if (empty($activity->timestamp)) {
1182 $activity->timestamp = 0;
1183 }
1184 $this->assertLessThanOrEqual($last, $activity->timestamp);
1185 }
1186 }
1187
1188 public function test_compare_activities_by_time_asc() {
1189
1190 // Let's create some test data.
1191 $activities = array();
1192 $x = new stdClass();
1193 $x->timestamp = null;
1194 $activities[] = $x;
1195
1196 $x = new stdClass();
1197 $x->timestamp = 1;
1198 $activities[] = $x;
1199
1200 $x = new stdClass();
1201 $x->timestamp = 3;
1202 $activities[] = $x;
1203
1204 $x = new stdClass();
1205 $x->timestamp = 0;
1206 $activities[] = $x;
1207
1208 $x = new stdClass();
1209 $x->timestamp = 5;
1210 $activities[] = $x;
1211
1212 $x = new stdClass();
1213 $activities[] = $x;
1214
1215 $x = new stdClass();
1216 $x->timestamp = 5;
1217 $activities[] = $x;
1218
1219 // Do the sorting.
1220 usort($activities, 'compare_activities_by_time_asc');
1221
1222 // Let's check the result.
1223 $last = 0;
1224 foreach($activities as $activity) {
1225 if (empty($activity->timestamp)) {
1226 $activity->timestamp = 0;
1227 }
1228 $this->assertGreaterThanOrEqual($last, $activity->timestamp);
1229 }
1230 }
9ab0aece 1231
1fff1b8c
DP
1232 /**
1233 * Tests moving a module between hidden/visible sections and
1234 * verifies that the course/module visiblity seettings are
1235 * retained.
1236 */
1237 public function test_moveto_module_between_hidden_sections() {
1238 global $DB;
1239
1240 $this->resetAfterTest(true);
1241
1242 $course = $this->getDataGenerator()->create_course(array('numsections' => 4), array('createsections' => true));
1243 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
1244 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
1245 $quiz= $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
1246
1247 // Set the page as hidden
1248 set_coursemodule_visible($page->cmid, 0);
1249
1250 // Set sections 3 as hidden.
1251 set_section_visible($course->id, 3, 0);
1252
1253 $modinfo = get_fast_modinfo($course);
1254
1255 $hiddensection = $modinfo->get_section_info(3);
1256 // New section is definitely not visible:
1257 $this->assertEquals($hiddensection->visible, 0);
1258
1259 $forumcm = $modinfo->cms[$forum->cmid];
1260 $pagecm = $modinfo->cms[$page->cmid];
1261
d55f05ef
MG
1262 // Move the forum and the page to a hidden section, make sure moveto_module returns 0 as new visibility state.
1263 $this->assertEquals(0, moveto_module($forumcm, $hiddensection));
1264 $this->assertEquals(0, moveto_module($pagecm, $hiddensection));
1fff1b8c 1265
1fff1b8c
DP
1266 $modinfo = get_fast_modinfo($course);
1267
1268 // Verify that forum and page have been moved to the hidden section and quiz has not.
1269 $this->assertContains($forum->cmid, $modinfo->sections[3]);
1270 $this->assertContains($page->cmid, $modinfo->sections[3]);
1271 $this->assertNotContains($quiz->cmid, $modinfo->sections[3]);
1272
1273 // Verify that forum has been made invisible.
1274 $forumcm = $modinfo->cms[$forum->cmid];
1275 $this->assertEquals($forumcm->visible, 0);
1276 // Verify that old state has been retained.
1277 $this->assertEquals($forumcm->visibleold, 1);
1278
1279 // Verify that page has stayed invisible.
1280 $pagecm = $modinfo->cms[$page->cmid];
1281 $this->assertEquals($pagecm->visible, 0);
1282 // Verify that old state has been retained.
1283 $this->assertEquals($pagecm->visibleold, 0);
1284
1285 // Verify that quiz has been unaffected.
1286 $quizcm = $modinfo->cms[$quiz->cmid];
1287 $this->assertEquals($quizcm->visible, 1);
1288
1289 // Move forum and page back to visible section.
d55f05ef 1290 // Make sure the visibility is restored to the original value (visible for forum and hidden for page).
1fff1b8c 1291 $visiblesection = $modinfo->get_section_info(2);
d55f05ef
MG
1292 $this->assertEquals(1, moveto_module($forumcm, $visiblesection));
1293 $this->assertEquals(0, moveto_module($pagecm, $visiblesection));
1fff1b8c 1294
1fff1b8c
DP
1295 $modinfo = get_fast_modinfo($course);
1296
d55f05ef 1297 // Double check that forum has been made visible.
1fff1b8c
DP
1298 $forumcm = $modinfo->cms[$forum->cmid];
1299 $this->assertEquals($forumcm->visible, 1);
1300
d55f05ef 1301 // Double check that page has stayed invisible.
1fff1b8c
DP
1302 $pagecm = $modinfo->cms[$page->cmid];
1303 $this->assertEquals($pagecm->visible, 0);
1304
d55f05ef
MG
1305 // Move the page in the same section (this is what mod duplicate does).
1306 // Visibility of page remains 0.
1307 $this->assertEquals(0, moveto_module($pagecm, $visiblesection, $forumcm));
1fff1b8c 1308
d55f05ef 1309 // Double check that the the page is still hidden.
1fff1b8c
DP
1310 $modinfo = get_fast_modinfo($course);
1311 $pagecm = $modinfo->cms[$page->cmid];
1312 $this->assertEquals($pagecm->visible, 0);
1313 }
1314
1315 /**
1316 * Tests moving a module around in the same section. moveto_module()
1317 * is called this way in modduplicate.
1318 */
1319 public function test_moveto_module_in_same_section() {
1320 global $DB;
1321
1322 $this->resetAfterTest(true);
1323
1324 $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true));
1325 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
1326 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
1327
1328 // Simulate inconsistent visible/visibleold values (MDL-38713).
1329 $cm = $DB->get_record('course_modules', array('id' => $page->cmid), '*', MUST_EXIST);
1330 $cm->visible = 0;
1331 $cm->visibleold = 1;
1332 $DB->update_record('course_modules', $cm);
1333
1334 $modinfo = get_fast_modinfo($course);
1335 $forumcm = $modinfo->cms[$forum->cmid];
1336 $pagecm = $modinfo->cms[$page->cmid];
1337
1338 // Verify that page is hidden.
1339 $this->assertEquals($pagecm->visible, 0);
1340
1341 // Verify section 0 is where all mods added.
1342 $section = $modinfo->get_section_info(0);
1343 $this->assertEquals($section->id, $forumcm->section);
1344 $this->assertEquals($section->id, $pagecm->section);
1345
1346
d55f05ef
MG
1347 // Move the page inside the hidden section. Make sure it is hidden.
1348 $this->assertEquals(0, moveto_module($pagecm, $section, $forumcm));
1fff1b8c 1349
d55f05ef 1350 // Double check that the the page is still hidden.
1fff1b8c
DP
1351 $modinfo = get_fast_modinfo($course);
1352 $pagecm = $modinfo->cms[$page->cmid];
1353 $this->assertEquals($pagecm->visible, 0);
1354 }
e52c1ea6
DP
1355
1356 public function test_course_delete_module() {
1357 global $DB;
1358 $this->resetAfterTest(true);
1359 $this->setAdminUser();
1360
1361 // Create course and modules.
1362 $course = $this->getDataGenerator()->create_course(array('numsections' => 5));
1363
1364 // Generate an assignment with due date (will generate a course event).
1365 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 'course' => $course->id));
1366
1367 $cm = get_coursemodule_from_instance('assign', $assign->id);
1368
1369 // Verify context exists.
1370 $this->assertInstanceOf('context_module', context_module::instance($cm->id, IGNORE_MISSING));
1371
1372 // Verify event assignment event has been generated.
1373 $eventcount = $DB->count_records('event', array('instance' => $assign->id, 'modulename' => 'assign'));
1374 $this->assertEquals(1, $eventcount);
1375
1376 // Run delete..
1377 course_delete_module($cm->id);
1378
1379 // Verify the context has been removed.
1380 $this->assertFalse(context_module::instance($cm->id, IGNORE_MISSING));
1381
1382 // Verify the course_module record has been deleted.
1383 $cmcount = $DB->count_records('course_modules', array('id' => $cm->id));
1384 $this->assertEmpty($cmcount);
1385
1386 // Verify event assignment events have been removed.
1387 $eventcount = $DB->count_records('event', array('instance' => $assign->id, 'modulename' => 'assign'));
1388 $this->assertEmpty($eventcount);
1389 }
35ad79e2
MN
1390
1391 /**
1392 * Test that triggering a course_created event works as expected.
1393 */
1394 public function test_course_created_event() {
3a11e2d2
RT
1395 global $DB;
1396
35ad79e2
MN
1397 $this->resetAfterTest();
1398
1399 // Catch the events.
1400 $sink = $this->redirectEvents();
1401
1402 // Create the course.
1403 $course = $this->getDataGenerator()->create_course();
3a11e2d2
RT
1404 // Get course from DB for comparison.
1405 $course = $DB->get_record('course', array('id' => $course->id));
35ad79e2
MN
1406
1407 // Capture the event.
1408 $events = $sink->get_events();
1409 $sink->close();
1410
1411 // Validate the event.
1412 $event = $events[0];
1413 $this->assertInstanceOf('\core\event\course_created', $event);
1414 $this->assertEquals('course', $event->objecttable);
1415 $this->assertEquals($course->id, $event->objectid);
3a11e2d2 1416 $this->assertEquals(context_course::instance($course->id), $event->get_context());
35ad79e2
MN
1417 $this->assertEquals($course, $event->get_record_snapshot('course', $course->id));
1418 $this->assertEquals('course_created', $event->get_legacy_eventname());
1419 $this->assertEventLegacyData($course, $event);
1420 $expectedlog = array(SITEID, 'course', 'new', 'view.php?id=' . $course->id, $course->fullname . ' (ID ' . $course->id . ')');
1421 $this->assertEventLegacyLogData($expectedlog, $event);
1422 }
4fd391d5
MN
1423
1424 /**
1425 * Test that triggering a course_updated event works as expected.
1426 */
1427 public function test_course_updated_event() {
1428 global $DB;
1429
1430 $this->resetAfterTest();
1431
e5959771 1432 // Create a course.
4fd391d5
MN
1433 $course = $this->getDataGenerator()->create_course();
1434
e5959771
MN
1435 // Create a category we are going to move this course to.
1436 $category = $this->getDataGenerator()->create_category();
1437
aec8fe2f
MG
1438 // Create a hidden category we are going to move this course to.
1439 $categoryhidden = $this->getDataGenerator()->create_category(array('visible' => 0));
1440
3a11e2d2 1441 // Update course and catch course_updated event.
4fd391d5 1442 $sink = $this->redirectEvents();
4fd391d5 1443 update_course($course);
4fd391d5
MN
1444 $events = $sink->get_events();
1445 $sink->close();
1446
3a11e2d2
RT
1447 // Get updated course information from the DB.
1448 $updatedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
1449 // Validate event.
1450 $event = array_shift($events);
4fd391d5
MN
1451 $this->assertInstanceOf('\core\event\course_updated', $event);
1452 $this->assertEquals('course', $event->objecttable);
e5959771 1453 $this->assertEquals($updatedcourse->id, $event->objectid);
3a11e2d2
RT
1454 $this->assertEquals(context_course::instance($course->id), $event->get_context());
1455 $this->assertEquals($updatedcourse, $event->get_record_snapshot('course', $event->objectid));
4fd391d5 1456 $this->assertEquals('course_updated', $event->get_legacy_eventname());
e5959771
MN
1457 $this->assertEventLegacyData($updatedcourse, $event);
1458 $expectedlog = array($updatedcourse->id, 'course', 'update', 'edit.php?id=' . $course->id, $course->id);
1459 $this->assertEventLegacyLogData($expectedlog, $event);
1460
3a11e2d2
RT
1461 // Move course and catch course_updated event.
1462 $sink = $this->redirectEvents();
1463 move_courses(array($course->id), $category->id);
1464 $events = $sink->get_events();
1465 $sink->close();
1466
1467 // Return the moved course information from the DB.
1468 $movedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
1469 // Validate event.
1470 $event = array_shift($events);
e5959771
MN
1471 $this->assertInstanceOf('\core\event\course_updated', $event);
1472 $this->assertEquals('course', $event->objecttable);
1473 $this->assertEquals($movedcourse->id, $event->objectid);
3a11e2d2 1474 $this->assertEquals(context_course::instance($course->id), $event->get_context());
e5959771
MN
1475 $this->assertEquals($movedcourse, $event->get_record_snapshot('course', $movedcourse->id));
1476 $this->assertEquals('course_updated', $event->get_legacy_eventname());
9e2d9135 1477 $this->assertEventLegacyData($movedcourse, $event);
e5959771 1478 $expectedlog = array($movedcourse->id, 'course', 'move', 'edit.php?id=' . $movedcourse->id, $movedcourse->id);
4fd391d5 1479 $this->assertEventLegacyLogData($expectedlog, $event);
aec8fe2f 1480
3a11e2d2
RT
1481 // Move course to hidden category and catch course_updated event.
1482 $sink = $this->redirectEvents();
1483 move_courses(array($course->id), $categoryhidden->id);
1484 $events = $sink->get_events();
1485 $sink->close();
1486
1487 // Return the moved course information from the DB.
1488 $movedcoursehidden = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
1489 // Validate event.
1490 $event = array_shift($events);
aec8fe2f
MG
1491 $this->assertInstanceOf('\core\event\course_updated', $event);
1492 $this->assertEquals('course', $event->objecttable);
1493 $this->assertEquals($movedcoursehidden->id, $event->objectid);
3a11e2d2 1494 $this->assertEquals(context_course::instance($course->id), $event->get_context());
aec8fe2f
MG
1495 $this->assertEquals($movedcoursehidden, $event->get_record_snapshot('course', $movedcoursehidden->id));
1496 $this->assertEquals('course_updated', $event->get_legacy_eventname());
1497 $this->assertEventLegacyData($movedcoursehidden, $event);
1498 $expectedlog = array($movedcoursehidden->id, 'course', 'move', 'edit.php?id=' . $movedcoursehidden->id, $movedcoursehidden->id);
1499 $this->assertEventLegacyLogData($expectedlog, $event);
4fd391d5 1500 }
bc3c5b22
MN
1501
1502 /**
1503 * Test that triggering a course_deleted event works as expected.
1504 */
1505 public function test_course_deleted_event() {
1506 $this->resetAfterTest();
1507
1508 // Create the course.
1509 $course = $this->getDataGenerator()->create_course();
1510
1511 // Save the course context before we delete the course.
1512 $coursecontext = context_course::instance($course->id);
1513
1514 // Catch the update event.
1515 $sink = $this->redirectEvents();
1516
1517 // Call delete_course() which will trigger the course_deleted event and the course_content_deleted
1518 // event. This function prints out data to the screen, which we do not want during a PHPUnit test,
1519 // so use ob_start and ob_end_clean to prevent this.
1520 ob_start();
1521 delete_course($course);
1522 ob_end_clean();
1523
1524 // Capture the event.
1525 $events = $sink->get_events();
1526 $sink->close();
1527
1528 // Validate the event.
ec8f23de 1529 $event = $events[1];
bc3c5b22
MN
1530 $this->assertInstanceOf('\core\event\course_deleted', $event);
1531 $this->assertEquals('course', $event->objecttable);
1532 $this->assertEquals($course->id, $event->objectid);
1533 $this->assertEquals($coursecontext->id, $event->contextid);
1534 $this->assertEquals($course, $event->get_record_snapshot('course', $course->id));
1535 $this->assertEquals('course_deleted', $event->get_legacy_eventname());
1536 // The legacy data also passed the context in the course object.
1537 $course->context = $coursecontext;
1538 $this->assertEventLegacyData($course, $event);
1539 $expectedlog = array(SITEID, 'course', 'delete', 'view.php?id=' . $course->id, $course->fullname . '(ID ' . $course->id . ')');
1540 $this->assertEventLegacyLogData($expectedlog, $event);
1541 }
ec8f23de
MN
1542
1543 /**
1544 * Test that triggering a course_content_deleted event works as expected.
1545 */
1546 public function test_course_content_deleted_event() {
1547 global $DB;
1548
1549 $this->resetAfterTest();
1550
1551 // Create the course.
1552 $course = $this->getDataGenerator()->create_course();
1553
1554 // Get the course from the DB. The data generator adds some extra properties, such as
1555 // numsections, to the course object which will fail the assertions later on.
1556 $course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
1557
1558 // Save the course context before we delete the course.
1559 $coursecontext = context_course::instance($course->id);
1560
1561 // Catch the update event.
1562 $sink = $this->redirectEvents();
1563
1564 // Call remove_course_contents() which will trigger the course_content_deleted event.
1565 // This function prints out data to the screen, which we do not want during a PHPUnit
1566 // test, so use ob_start and ob_end_clean to prevent this.
1567 ob_start();
1568 remove_course_contents($course->id);
1569 ob_end_clean();
1570
1571 // Capture the event.
1572 $events = $sink->get_events();
1573 $sink->close();
1574
1575 // Validate the event.
1576 $event = $events[0];
1577 $this->assertInstanceOf('\core\event\course_content_deleted', $event);
1578 $this->assertEquals('course', $event->objecttable);
1579 $this->assertEquals($course->id, $event->objectid);
1580 $this->assertEquals($coursecontext->id, $event->contextid);
1581 $this->assertEquals($course, $event->get_record_snapshot('course', $course->id));
1582 $this->assertEquals('course_content_removed', $event->get_legacy_eventname());
1583 // The legacy data also passed the context and options in the course object.
1584 $course->context = $coursecontext;
1585 $course->options = array();
1586 $this->assertEventLegacyData($course, $event);
1587 }
7240cd92
MN
1588
1589 /**
1590 * Test that triggering a course_category_deleted event works as expected.
1591 */
1592 public function test_course_category_deleted_event() {
1593 $this->resetAfterTest();
1594
1595 // Create a category.
1596 $category = $this->getDataGenerator()->create_category();
1597
1598 // Save the context before it is deleted.
1599 $categorycontext = context_coursecat::instance($category->id);
1600
1601 // Catch the update event.
1602 $sink = $this->redirectEvents();
1603
1604 // Delete the category.
1605 $category->delete_full();
1606
1607 // Capture the event.
1608 $events = $sink->get_events();
1609 $sink->close();
1610
1611 // Validate the event.
1612 $event = $events[0];
1613 $this->assertInstanceOf('\core\event\course_category_deleted', $event);
1614 $this->assertEquals('course_categories', $event->objecttable);
1615 $this->assertEquals($category->id, $event->objectid);
1616 $this->assertEquals($categorycontext->id, $event->contextid);
1617 $this->assertEquals('course_category_deleted', $event->get_legacy_eventname());
1618 $this->assertEventLegacyData($category, $event);
1619 $expectedlog = array(SITEID, 'category', 'delete', 'index.php', $category->name . '(ID ' . $category->id . ')');
1620 $this->assertEventLegacyLogData($expectedlog, $event);
1621
1622 // Create two categories.
1623 $category = $this->getDataGenerator()->create_category();
1624 $category2 = $this->getDataGenerator()->create_category();
1625
1626 // Save the context before it is moved and then deleted.
1627 $category2context = context_coursecat::instance($category2->id);
1628
1629 // Catch the update event.
1630 $sink = $this->redirectEvents();
1631
1632 // Move the category.
1633 $category2->delete_move($category->id);
1634
1635 // Capture the event.
1636 $events = $sink->get_events();
1637 $sink->close();
1638
1639 // Validate the event.
1640 $event = $events[0];
1641 $this->assertInstanceOf('\core\event\course_category_deleted', $event);
1642 $this->assertEquals('course_categories', $event->objecttable);
1643 $this->assertEquals($category2->id, $event->objectid);
1644 $this->assertEquals($category2context->id, $event->contextid);
1645 $this->assertEquals('course_category_deleted', $event->get_legacy_eventname());
9e2d9135 1646 $this->assertEventLegacyData($category2, $event);
7240cd92
MN
1647 $expectedlog = array(SITEID, 'category', 'delete', 'index.php', $category2->name . '(ID ' . $category2->id . ')');
1648 $this->assertEventLegacyLogData($expectedlog, $event);
1649 }
02cbb621
MN
1650
1651 /**
1652 * Test that triggering a course_restored event works as expected.
1653 */
1654 public function test_course_restored_event() {
1655 global $CFG;
1656
1657 // Get the necessary files to perform backup and restore.
1658 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1659 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1660
1661 $this->resetAfterTest();
1662
1663 // Set to admin user.
1664 $this->setAdminUser();
1665
1666 // The user id is going to be 2 since we are the admin user.
1667 $userid = 2;
1668
1669 // Create a course.
1670 $course = $this->getDataGenerator()->create_course();
1671
1672 // Create backup file and save it to the backup location.
1673 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1674 backup::INTERACTIVE_NO, backup::MODE_GENERAL, $userid);
1675 $bc->execute_plan();
1676 $results = $bc->get_results();
1677 $file = $results['backup_destination'];
1678 $fp = get_file_packer();
1679 $filepath = $CFG->dataroot . '/temp/backup/test-restore-course-event';
1680 $file->extract_to_pathname($fp, $filepath);
1681 $bc->destroy();
1682 unset($bc);
1683
1684 // Now we want to catch the restore course event.
1685 $sink = $this->redirectEvents();
1686
1687 // Now restore the course to trigger the event.
1688 $rc = new restore_controller('test-restore-course-event', $course->id, backup::INTERACTIVE_NO,
1689 backup::MODE_GENERAL, $userid, backup::TARGET_NEW_COURSE);
1690 $rc->execute_precheck();
1691 $rc->execute_plan();
1692
1693 // Capture the event.
1694 $events = $sink->get_events();
1695 $sink->close();
1696
1697 // Validate the event.
1698 $event = $events[0];
1699 $this->assertInstanceOf('\core\event\course_restored', $event);
1700 $this->assertEquals('course', $event->objecttable);
1701 $this->assertEquals($rc->get_courseid(), $event->objectid);
1702 $this->assertEquals(context_course::instance($rc->get_courseid())->id, $event->contextid);
1703 $this->assertEquals('course_restored', $event->get_legacy_eventname());
1704 $legacydata = (object) array(
1705 'courseid' => $rc->get_courseid(),
1706 'userid' => $rc->get_userid(),
1707 'type' => $rc->get_type(),
1708 'target' => $rc->get_target(),
1709 'mode' => $rc->get_mode(),
1710 'operation' => $rc->get_operation(),
1711 'samesite' => $rc->is_samesite()
1712 );
1713 $this->assertEventLegacyData($legacydata, $event);
1714
1715 // Destroy the resource controller since we are done using it.
1716 $rc->destroy();
1717 unset($rc);
1718
1719 // Clear the time limit, otherwise PHPUnit complains.
1720 set_time_limit(0);
1721 }
5a10d2a7 1722
ed29bf0f
RT
1723 /**
1724 * Test that triggering a course_section_updated event works as expected.
1725 */
1726 public function test_course_section_updated_event() {
1727 global $DB;
1728
1729 $this->resetAfterTest();
1730
1731 // Create the course with sections.
1732 $course = $this->getDataGenerator()->create_course(array('numsections' => 10), array('createsections' => true));
1733 $sections = $DB->get_records('course_sections', array('course' => $course->id));
1734
1735 $coursecontext = context_course::instance($course->id);
1736
1737 $section = array_pop($sections);
1738 $section->name = 'Test section';
1739 $section->summary = 'Test section summary';
1740 $DB->update_record('course_sections', $section);
1741
1742 // Trigger an event for course section update.
1743 $event = \core\event\course_section_updated::create(
1744 array(
1745 'objectid' => $section->id,
1746 'courseid' => $course->id,
1747 'context' => context_course::instance($course->id)
1748 )
1749 );
1750 $event->add_record_snapshot('course_sections', $section);
1751 // Trigger and catch event.
1752 $sink = $this->redirectEvents();
1753 $event->trigger();
1754 $events = $sink->get_events();
1755 $sink->close();
1756
1757 // Validate the event.
1758 $event = $events[0];
1759 $this->assertInstanceOf('\core\event\course_section_updated', $event);
1760 $this->assertEquals('course_sections', $event->objecttable);
1761 $this->assertEquals($section->id, $event->objectid);
1762 $this->assertEquals($course->id, $event->courseid);
1763 $this->assertEquals($coursecontext->id, $event->contextid);
1764 $expecteddesc = 'Course ' . $event->courseid . ' section ' . $event->other['sectionnum'] . ' updated by user ' . $event->userid;
1765 $this->assertEquals($expecteddesc, $event->get_description());
1766 $this->assertEquals($section, $event->get_record_snapshot('course_sections', $event->objectid));
1767 $id = $section->id;
1768 $sectionnum = $section->section;
1769 $expectedlegacydata = array($course->id, "course", "editsection", 'editsection.php?id=' . $id, $sectionnum);
1770 $this->assertEventLegacyLogData($expectedlegacydata, $event);
1771 }
749ce98e
DW
1772
1773 public function test_course_integrity_check() {
1774 global $DB;
1775
1776 $this->resetAfterTest(true);
1777 $course = $this->getDataGenerator()->create_course(array('numsections' => 1),
1778 array('createsections'=>true));
1779
1780 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
1781 array('section' => 0));
1782 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id),
1783 array('section' => 0));
1784 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id),
1785 array('section' => 0));
1786 $correctseq = join(',', array($forum->cmid, $page->cmid, $quiz->cmid));
1787
1788 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1789 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1790 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1791 $this->assertEquals($correctseq, $section0->sequence);
1792 $this->assertEmpty($section1->sequence);
1793 $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1794 $this->assertEquals($section0->id, $cms[$page->cmid]->section);
1795 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1796 $this->assertEmpty(course_integrity_check($course->id));
1797
1798 // Now let's make manual change in DB and let course_integrity_check() fix it:
1799
1800 // 1. Module appears twice in one section.
1801 $DB->update_record('course_sections', array('id' => $section0->id, 'sequence' => $section0->sequence. ','. $page->cmid));
1802 $this->assertEquals(
1803 array('Failed integrity check for course ['. $course->id.
1804 ']. Sequence for course section ['. $section0->id. '] is "'.
1805 $section0->sequence. ','. $page->cmid. '", must be "'.
1806 $section0->sequence. '"'),
1807 course_integrity_check($course->id));
1808 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1809 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1810 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1811 $this->assertEquals($correctseq, $section0->sequence);
1812 $this->assertEmpty($section1->sequence);
1813 $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1814 $this->assertEquals($section0->id, $cms[$page->cmid]->section);
1815 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1816
1817 // 2. Module appears in two sections (last section wins).
1818 $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''. $page->cmid));
1819 // First message about double mentioning in sequence, second message about wrong section field for $page.
1820 $this->assertEquals(array(
1821 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
1822 '] must be removed from sequence of section ['. $section0->id.
1823 '] because it is also present in sequence of section ['. $section1->id. ']',
1824 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
1825 '] points to section ['. $section0->id. '] instead of ['. $section1->id. ']'),
1826 course_integrity_check($course->id));
1827 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1828 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1829 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1830 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence);
1831 $this->assertEquals(''. $page->cmid, $section1->sequence);
1832 $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1833 $this->assertEquals($section1->id, $cms[$page->cmid]->section);
1834 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1835
1836 // 3. Module id is not present in course_section.sequence (integrity check with $fullcheck = false).
1837 $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''));
1838 $this->assertEmpty(course_integrity_check($course->id)); // Not an error!
1839 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1840 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1841 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1842 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence);
1843 $this->assertEmpty($section1->sequence);
1844 $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1845 $this->assertEquals($section1->id, $cms[$page->cmid]->section); // Not changed.
1846 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1847
1848 // 4. Module id is not present in course_section.sequence (integrity check with $fullcheck = true).
1849 $this->assertEquals(array('Failed integrity check for course ['. $course->id. ']. Course module ['.
1850 $page->cmid. '] is missing from sequence of section ['. $section1->id. ']'),
1851 course_integrity_check($course->id, null, null, true)); // Error!
1852 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1853 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1854 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1855 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence);
1856 $this->assertEquals(''. $page->cmid, $section1->sequence); // Yay, module added to section.
1857 $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1858 $this->assertEquals($section1->id, $cms[$page->cmid]->section); // Not changed.
1859 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1860
1861 // 5. Module id is not present in course_section.sequence and it's section is invalid (integrity check with $fullcheck = true).
1862 $DB->update_record('course_modules', array('id' => $page->cmid, 'section' => 8765));
1863 $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''));
1864 $this->assertEquals(array(
1865 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
e2e9cb6a 1866 '] is missing from sequence of section ['. $section0->id. ']',
749ce98e
DW
1867 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid.
1868 '] points to section [8765] instead of ['. $section0->id. ']'),
1869 course_integrity_check($course->id, null, null, true));
1870 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1871 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1872 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1873 $this->assertEquals($forum->cmid. ','. $quiz->cmid. ','. $page->cmid, $section0->sequence); // Module added to section.
1874 $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1875 $this->assertEquals($section0->id, $cms[$page->cmid]->section); // Section changed to section0.
1876 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1877
1878 // 6. Module is deleted from course_modules but not deleted in sequence (integrity check with $fullcheck = true).
1879 $DB->delete_records('course_modules', array('id' => $page->cmid));
1880 $this->assertEquals(array('Failed integrity check for course ['. $course->id. ']. Course module ['.
1881 $page->cmid. '] does not exist but is present in the sequence of section ['. $section0->id. ']'),
1882 course_integrity_check($course->id, null, null, true));
1883 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0));
1884 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1));
1885 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section');
1886 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence);
1887 $this->assertEmpty($section1->sequence);
1888 $this->assertEquals($section0->id, $cms[$forum->cmid]->section);
1889 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section);
1890 $this->assertEquals(2, count($cms));
1891 }
b9b994c7
AA
1892
1893 /**
1894 * Tests for event related to course module creation.
1895 */
1896 public function test_course_module_created_event() {
1897 global $USER, $DB;
1898 $this->resetAfterTest();
1899
1900 // Create an assign module.
1901 $sink = $this->redirectEvents();
1902 $modinfo = $this->create_specific_module_test('assign');
1903 $events = $sink->get_events();
1904 $event = array_pop($events);
1905 $sink->close();
1906
1907 $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST);
1908 $mod = $DB->get_record('assign', array('id' => $modinfo->instance), '*', MUST_EXIST);
1909
1910 // Validate event data.
1911 $this->assertInstanceOf('\core\event\course_module_created', $event);
1912 $this->assertEquals($cm->id, $event->objectid);
1913 $this->assertEquals($USER->id, $event->userid);
1914 $this->assertEquals('course_modules', $event->objecttable);
1915 $url = new moodle_url('/mod/assign/view.php', array('id' => $mod->id));
1916 $this->assertEquals($url, $event->get_url());
1917
1918 // Test legacy data.
1919 $this->assertSame('mod_created', $event->get_legacy_eventname());
1920 $eventdata = new stdClass();
1921 $eventdata->modulename = 'assign';
1922 $eventdata->name = $mod->name;
1923 $eventdata->cmid = $cm->id;
1924 $eventdata->courseid = $cm->course;
1925 $eventdata->userid = $USER->id;
1926 $this->assertEventLegacyData($eventdata, $event);
1927
1928 $arr = array($cm->course, "course", "add mod", "../mod/assign/view.php?id=$mod->id", "assign $cm->instance");
1929 $this->assertEventLegacyLogData($arr, $event);
1930
1931 }
1932
1933 /**
1934 * Tests for event validations related to course module creation.
1935 */
1936 public function test_course_module_created_event_exceptions() {
1937
1938 $this->resetAfterTest();
1939
1940 // Generate data.
1941 $modinfo = $this->create_specific_module_test('assign');
1942 $context = context_module::instance($modinfo->coursemodule);
1943
1944 // Test not setting instanceid.
1945 try {
1946 $event = \core\event\course_module_created::create(array(
1947 'courseid' => $modinfo->course,
1948 'context' => $context,
1949 'objectid' => $modinfo->coursemodule,
1950 'other' => array(
1951 'modulename' => 'assign',
1952 'name' => 'My assignment',
1953 )
1954 ));
1955 $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without
1956 other['instanceid']");
1957 } catch (coding_exception $e) {
1958 $this->assertContains("Field other['instanceid'] cannot be empty", $e->getMessage());
1959 }
1960
1961 // Test not setting modulename.
1962 try {
1963 $event = \core\event\course_module_created::create(array(
1964 'courseid' => $modinfo->course,
1965 'context' => $context,
1966 'objectid' => $modinfo->coursemodule,
1967 'other' => array(
1968 'instanceid' => $modinfo->instance,
1969 'name' => 'My assignment',
1970 )
1971 ));
1972 $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without
1973 other['modulename']");
1974 } catch (coding_exception $e) {
1975 $this->assertContains("Field other['modulename'] cannot be empty", $e->getMessage());
1976 }
1977
1978 // Test not setting name.
1979
1980 try {
1981 $event = \core\event\course_module_created::create(array(
1982 'courseid' => $modinfo->course,
1983 'context' => $context,
1984 'objectid' => $modinfo->coursemodule,
1985 'other' => array(
1986 'modulename' => 'assign',
1987 'instanceid' => $modinfo->instance,
1988 )
1989 ));
1990 $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without
1991 other['name']");
1992 } catch (coding_exception $e) {
1993 $this->assertContains("Field other['name'] cannot be empty", $e->getMessage());
1994 }
1995
1996 }
1997
1998 /**
1999 * Tests for event related to course module updates.
2000 */
2001 public function test_course_module_updated_event() {
2002 global $USER, $DB;
2003 $this->resetAfterTest();
2004
2005 // Update a forum module.
2006 $sink = $this->redirectEvents();
2007 $modinfo = $this->update_specific_module_test('forum');
2008 $events = $sink->get_events();
2009 $event = array_pop($events);
2010 $sink->close();
2011
2012 $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST);
2013 $mod = $DB->get_record('forum', array('id' => $cm->instance), '*', MUST_EXIST);
2014
2015 // Validate event data.
2016 $this->assertInstanceOf('\core\event\course_module_updated', $event);
2017 $this->assertEquals($cm->id, $event->objectid);
2018 $this->assertEquals($USER->id, $event->userid);
2019 $this->assertEquals('course_modules', $event->objecttable);
2020 $url = new moodle_url('/mod/forum/view.php', array('id' => $mod->id));
2021 $this->assertEquals($url, $event->get_url());
2022
2023 // Test legacy data.
2024 $this->assertSame('mod_updated', $event->get_legacy_eventname());
2025 $eventdata = new stdClass();
2026 $eventdata->modulename = 'forum';
2027 $eventdata->name = $mod->name;
2028 $eventdata->cmid = $cm->id;
2029 $eventdata->courseid = $cm->course;
2030 $eventdata->userid = $USER->id;
2031 $this->assertEventLegacyData($eventdata, $event);
2032
2033 $arr = array($cm->course, "course", "update mod", "../mod/forum/view.php?id=$mod->id", "forum $cm->instance");
2034 $this->assertEventLegacyLogData($arr, $event);
2035
2036 }
2037
2038 /**
2039 * Tests for event validations related to course module update.
2040 */
2041 public function test_course_module_updated_event_exceptions() {
2042
2043 $this->resetAfterTest();
2044
2045 // Generate data.
2046 $modinfo = $this->create_specific_module_test('assign');
2047 $context = context_module::instance($modinfo->coursemodule);
2048
2049 // Test not setting instanceid.
2050 try {
2051 $event = \core\event\course_module_updated::create(array(
2052 'courseid' => $modinfo->course,
2053 'context' => $context,
2054 'objectid' => $modinfo->coursemodule,
2055 'other' => array(
2056 'modulename' => 'assign',
2057 'name' => 'My assignment',
2058 )
2059 ));
2060 $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without
2061 other['instanceid']");
2062 } catch (coding_exception $e) {
2063 $this->assertContains("Field other['instanceid'] cannot be empty", $e->getMessage());
2064 }
2065
2066 // Test not setting modulename.
2067 try {
2068 $event = \core\event\course_module_updated::create(array(
2069 'courseid' => $modinfo->course,
2070 'context' => $context,
2071 'objectid' => $modinfo->coursemodule,
2072 'other' => array(
2073 'instanceid' => $modinfo->instance,
2074 'name' => 'My assignment',
2075 )
2076 ));
2077 $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without
2078 other['modulename']");
2079 } catch (coding_exception $e) {
2080 $this->assertContains("Field other['modulename'] cannot be empty", $e->getMessage());
2081 }
2082
2083 // Test not setting name.
2084
2085 try {
2086 $event = \core\event\course_module_updated::create(array(
2087 'courseid' => $modinfo->course,
2088 'context' => $context,
2089 'objectid' => $modinfo->coursemodule,
2090 'other' => array(
2091 'modulename' => 'assign',
2092 'instanceid' => $modinfo->instance,
2093 )
2094 ));
2095 $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without
2096 other['name']");
2097 } catch (coding_exception $e) {
2098 $this->assertContains("Field other['name'] cannot be empty", $e->getMessage());
2099 }
2100
2101 }
2102
2103 /**
2104 * Tests for event related to course module delete.
2105 */
2106 public function test_course_module_deleted_event() {
2107 global $USER, $DB;
2108 $this->resetAfterTest();
2109
2110 // Create and delete a module.
2111 $sink = $this->redirectEvents();
2112 $modinfo = $this->create_specific_module_test('forum');
2113 $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST);
2114 course_delete_module($modinfo->coursemodule);
2115 $events = $sink->get_events();
2116 $event = array_pop($events); // delete module event.;
2117 $sink->close();
2118
2119 // Validate event data.
2120 $this->assertInstanceOf('\core\event\course_module_deleted', $event);
2121 $this->assertEquals($cm->id, $event->objectid);
2122 $this->assertEquals($USER->id, $event->userid);
2123 $this->assertEquals('course_modules', $event->objecttable);
2124 $this->assertEquals(null, $event->get_url());
2125 $this->assertEquals($cm, $event->get_record_snapshot('course_modules', $cm->id));
2126
2127 // Test legacy data.
2128 $this->assertSame('mod_deleted', $event->get_legacy_eventname());
2129 $eventdata = new stdClass();
2130 $eventdata->modulename = 'forum';
2131 $eventdata->cmid = $cm->id;
2132 $eventdata->courseid = $cm->course;
2133 $eventdata->userid = $USER->id;
2134 $this->assertEventLegacyData($eventdata, $event);
2135
2136 $arr = array($cm->course, 'course', "delete mod", "view.php?id=$cm->course", "forum $cm->instance", $cm->id);
2137 $this->assertEventLegacyLogData($arr, $event);
2138
2139 }
2140
2141 /**
2142 * Tests for event validations related to course module deletion.
2143 */
2144 public function test_course_module_deleted_event_exceptions() {
2145
2146 $this->resetAfterTest();
2147
2148 // Generate data.
2149 $modinfo = $this->create_specific_module_test('assign');
2150 $context = context_module::instance($modinfo->coursemodule);
2151
2152 // Test not setting instanceid.
2153 try {
2154 $event = \core\event\course_module_deleted::create(array(
2155 'courseid' => $modinfo->course,
2156 'context' => $context,
2157 'objectid' => $modinfo->coursemodule,
2158 'other' => array(
2159 'modulename' => 'assign',
2160 'name' => 'My assignment',
2161 )
2162 ));
2163 $this->fail("Event validation should not allow \\core\\event\\course_module_deleted to be triggered without
2164 other['instanceid']");
2165 } catch (coding_exception $e) {
2166 $this->assertContains("Field other['instanceid'] cannot be empty", $e->getMessage());
2167 }
2168
2169 // Test not setting modulename.
2170 try {
2171 $event = \core\event\course_module_deleted::create(array(
2172 'courseid' => $modinfo->course,
2173 'context' => $context,
2174 'objectid' => $modinfo->coursemodule,
2175 'other' => array(
2176 'instanceid' => $modinfo->instance,
2177 'name' => 'My assignment',
2178 )
2179 ));
2180 $this->fail("Event validation should not allow \\core\\event\\course_module_deleted to be triggered without
2181 other['modulename']");
2182 } catch (coding_exception $e) {
2183 $this->assertContains("Field other['modulename'] cannot be empty", $e->getMessage());
2184 }
2185 }
354b214c 2186}