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