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