[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
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 26 defined('MOODLE_INTERNAL') || die(); 27 28 global $CFG; 29 require_once($CFG->dirroot . '/course/lib.php'); 30 require_once($CFG->dirroot . '/course/tests/fixtures/course_capability_assignment.php'); 31 require_once($CFG->dirroot . '/enrol/imsenterprise/tests/imsenterprise_test.php'); 32 require_once($CFG->dirroot . '/tag/lib.php'); 33 34 class core_course_courselib_testcase extends advanced_testcase { 35 36 /** 37 * Tidy up open files that may be left open. 38 */ 39 protected function tearDown() { 40 gc_collect_cycles(); 41 } 42 43 /** 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. 47 */ 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; 53 54 // Specific values to the Forum module. 55 $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE; 56 $moduleinfo->type = 'single'; 57 $moduleinfo->trackingtype = FORUM_TRACKING_FORCED; 58 $moduleinfo->maxbytes = 10240; 59 $moduleinfo->maxattachments = 2; 60 61 // Post threshold for blocking - specific to forum. 62 $moduleinfo->blockperiod = 60*60*24; 63 $moduleinfo->blockafter = 10; 64 $moduleinfo->warnafter = 5; 65 } 66 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 } 93 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) { 100 // Specific values to the Assign module. 101 $moduleinfo->alwaysshowdescription = true; 102 $moduleinfo->submissiondrafts = true; 103 $moduleinfo->requiresubmissionstatement = true; 104 $moduleinfo->sendnotifications = true; 105 $moduleinfo->sendlatenotifications = true; 106 $moduleinfo->duedate = time() + (7 * 24 * 3600); 107 $moduleinfo->cutoffdate = time() + (7 * 24 * 3600); 108 $moduleinfo->allowsubmissionsfromdate = time(); 109 $moduleinfo->teamsubmission = true; 110 $moduleinfo->requireallteammemberssubmit = true; 111 $moduleinfo->teamsubmissiongroupingid = true; 112 $moduleinfo->blindmarking = true; 113 $moduleinfo->markingworkflow = true; 114 $moduleinfo->markingallocation = true; 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 124 // Advanced grading. 125 $gradingmethods = grading_manager::available_methods(); 126 $moduleinfo->advancedgradingmethod_submissions = current(array_keys($gradingmethods)); 127 } 128 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); 149 $this->assertEquals($moduleinfo->markingworkflow, $dbmodinstance->markingworkflow); 150 $this->assertEquals($moduleinfo->markingallocation, $dbmodinstance->markingallocation); 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 153 // Advanced grading. 154 $cm = get_coursemodule_from_instance('assign', $dbmodinstance->id); 155 $contextmodule = context_module::instance($cm->id); 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)); 196 197 // Create assign module instance for test. 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); 202 203 // Module test values. 204 $moduleinfo = new stdClass(); 205 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; 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; 226 $moduleinfo->completiongradeitemnumber = 1; 227 $moduleinfo->completionexpected = time() + (7 * 24 * 3600); 228 229 // Conditional activity. 230 $moduleinfo->availability = '{"op":"&","showc":[true,true],"c":[' . 231 '{"type":"date","d":">=","t":' . time() . '},' . 232 '{"type":"date","d":"<","t":' . (time() + (7 * 24 * 3600)) . '}' . 233 ']}'; 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)); 236 $moduleinfo->conditionfieldgroup = array(array('conditionfield' => 'email', 'conditionfieldoperator' => OP_CONTAINS, 'conditionfieldvalue' => '@')); 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 246 // RSS. 247 $moduleinfo->rsstype = 2; 248 $moduleinfo->rssarticles = 10; 249 250 // Optional intro editor (depends of module). 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); 254 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 } 259 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)); 276 // Retrieve the grade item. 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); 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); 290 $this->assertEquals($moduleinfo->availability, $dbcm->availability); 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 306 // Test specific to the module. 307 $modulerunasserts = $modulename.'_create_run_asserts'; 308 $this->$modulerunasserts($moduleinfo, $dbmodinstance); 309 return $moduleinfo; 310 } 311 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. 317 // Create the match MODULENAME_create_set_values() and MODULENAME_create_run_asserts(). 318 $modules = array('forum', 'assign'); 319 // Run all tests. 320 foreach ($modules as $modulename) { 321 $this->create_specific_module_test($modulename); 322 } 323 } 324 325 /** 326 * Test update_module() for multiple modules defined in the $modules array (first declaration of the function). 327 */ 328 public function test_update_module() { 329 // Add the module name you want to test here. 330 // Create the match MODULENAME_update_set_values() and MODULENAME_update_run_asserts(). 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 349 // Specific values to the Forum module. 350 $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE; 351 $moduleinfo->type = 'single'; 352 $moduleinfo->trackingtype = FORUM_TRACKING_FORCED; 353 $moduleinfo->maxbytes = 10240; 354 $moduleinfo->maxattachments = 2; 355 356 // Post threshold for blocking - specific to forum. 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) { 397 global $DB, $CFG; 398 399 $this->resetAfterTest(true); 400 401 $this->setAdminUser(); 402 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'); 405 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), 419 array('createsections'=>true)); 420 421 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id)); 422 423 // Create assign module instance for testing gradeitem. 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); 428 429 // Create the test forum to update. 430 $initvalues = new stdClass(); 431 $initvalues->introformat = FORMAT_HTML; 432 $initvalues->course = $course->id; 433 $forum = self::getDataGenerator()->create_module('forum', $initvalues); 434 435 // Retrieve course module. 436 $cm = get_coursemodule_from_instance('forum', $forum->id); 437 438 // Module test values. 439 $moduleinfo = new stdClass(); 440 441 // Always mandatory generic values to any module. 442 $moduleinfo->coursemodule = $cm->id; 443 $moduleinfo->modulename = $modulename; 444 $moduleinfo->course = $course->id; 445 $moduleinfo->groupingid = $grouping->id; 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; 461 $moduleinfo->completiongradeitemnumber = 1; 462 $moduleinfo->completionexpected = time() + (7 * 24 * 3600); 463 $moduleinfo->completionunlocked = 1; 464 465 // Conditional activity. 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. 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)), '&')); 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 481 // RSS. 482 $moduleinfo->rsstype = 2; 483 $moduleinfo->rssarticles = 10; 484 485 // Optional intro editor (depends of module). 486 $draftid_editor = 0; 487 file_prepare_draft_area($draftid_editor, null, null, null, null); 488 $moduleinfo->introeditor = array('text' => 'This is a module', 'format' => FORMAT_HTML, 'itemid' => $draftid_editor); 489 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. 495 // No plagiarism plugin installed by default. Use this space to make your own test. 496 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); 507 // Retrieve the grade item. 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); 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); 520 $this->assertEquals($moduleinfo->availability, $dbcm->availability); 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 536 // Test specific to the module. 537 $modulerunasserts = $modulename.'_update_run_asserts'; 538 $this->$modulerunasserts($moduleinfo, $dbmodinstance); 539 return $moduleinfo; 540 } 541 542 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; 558 $original = (array) $course; 559 560 $created = create_course($course); 561 $context = context_course::instance($created->id); 562 563 // Compare original and created. 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); 573 574 // Ensure that the shortname isn't duplicated. 575 try { 576 $created = create_course($course); 577 $this->fail('Exception expected'); 578 } catch (moodle_exception $e) { 579 $this->assertSame(get_string('shortnametaken', 'error', $course->shortname), $e->getMessage()); 580 } 581 582 // Ensure that the idnumber isn't duplicated. 583 $course->shortname .= '1'; 584 try { 585 $created = create_course($course); 586 $this->fail('Exception expected'); 587 } catch (moodle_exception $e) { 588 $this->assertSame(get_string('courseidnumbertaken', 'error', $course->idnumber), $e->getMessage()); 589 } 590 } 591 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 612 // Ensure all 6 (0-5) sections were created and course content cache works properly 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 627 public function test_update_course() { 628 global $DB; 629 630 $this->resetAfterTest(); 631 632 $defaultcategory = $DB->get_field_select('course_categories', 'MIN(id)', 'parent = 0'); 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; 644 645 $created = create_course($course); 646 // Ensure the checks only work on idnumber/shortname that are not already ours. 647 update_course($created); 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'; 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 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. 702 $coursecacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); 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 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)); 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 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(); 732 foreach ($DB->get_records('course_sections', array('course'=>$course->id), 'id') as $section) { 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 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 758 $neworder = reorder_sections(1, 2, 4); 759 $this->assertFalse($neworder); 760 } 761 762 public function test_move_section_down() { 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 774 // Test move section down.. 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 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 862 public function test_get_course_display_name_for_list() { 863 global $CFG; 864 $this->resetAfterTest(true); 865 866 $course = $this->getDataGenerator()->create_course(array('shortname' => 'FROG101', 'fullname' => 'Introduction to pond life')); 867 868 $CFG->courselistshortnames = 0; 869 $this->assertEquals('Introduction to pond life', get_course_display_name_for_list($course)); 870 871 $CFG->courselistshortnames = 1; 872 $this->assertEquals('FROG101 Introduction to pond life', get_course_display_name_for_list($course)); 873 } 874 875 public function test_move_module_in_course() { 876 global $DB; 877 878 $this->resetAfterTest(true); 879 // Setup fixture 880 $course = $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections' => true)); 881 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id)); 882 883 $cms = get_fast_modinfo($course)->get_cms(); 884 $cm = reset($cms); 885 886 $newsection = get_fast_modinfo($course)->get_section_info(3); 887 $oldsectionid = $cm->section; 888 889 // Perform the move 890 moveto_module($cm, $newsection); 891 892 $cms = get_fast_modinfo($course)->get_cms(); 893 $cm = reset($cms); 894 895 // Check that the cached modinfo contains the correct section info 896 $modinfo = get_fast_modinfo($course); 897 $this->assertTrue(empty($modinfo->sections[0])); 898 $this->assertFalse(empty($modinfo->sections[3])); 899 900 // Check that the old section's sequence no longer contains this ID 901 $oldsection = $DB->get_record('course_sections', array('id' => $oldsectionid)); 902 $oldsequences = explode(',', $newsection->sequence); 903 $this->assertFalse(in_array($cm->id, $oldsequences)); 904 905 // Check that the new section's sequence now contains this ID 906 $newsection = $DB->get_record('course_sections', array('id' => $newsection->id)); 907 $newsequences = explode(',', $newsection->sequence); 908 $this->assertTrue(in_array($cm->id, $newsequences)); 909 910 // Check that the section number has been changed in the cm 911 $this->assertEquals($newsection->id, $cm->section); 912 913 914 // Perform a second move as some issues were only seen on the second move 915 $newsection = get_fast_modinfo($course)->get_section_info(2); 916 $oldsectionid = $cm->section; 917 moveto_module($cm, $newsection); 918 919 $cms = get_fast_modinfo($course)->get_cms(); 920 $cm = reset($cms); 921 922 // Check that the cached modinfo contains the correct section info 923 $modinfo = get_fast_modinfo($course); 924 $this->assertTrue(empty($modinfo->sections[0])); 925 $this->assertFalse(empty($modinfo->sections[2])); 926 927 // Check that the old section's sequence no longer contains this ID 928 $oldsection = $DB->get_record('course_sections', array('id' => $oldsectionid)); 929 $oldsequences = explode(',', $newsection->sequence); 930 $this->assertFalse(in_array($cm->id, $oldsequences)); 931 932 // Check that the new section's sequence now contains this ID 933 $newsection = $DB->get_record('course_sections', array('id' => $newsection->id)); 934 $newsequences = explode(',', $newsection->sequence); 935 $this->assertTrue(in_array($cm->id, $newsequences)); 936 } 937 938 public function test_module_visibility() { 939 $this->setAdminUser(); 940 $this->resetAfterTest(true); 941 942 // Create course and modules. 943 $course = $this->getDataGenerator()->create_course(array('numsections' => 5)); 944 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 945 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 'course' => $course->id)); 946 $modules = compact('forum', 'assign'); 947 948 // Hiding the modules. 949 foreach ($modules as $mod) { 950 set_coursemodule_visible($mod->cmid, 0); 951 $this->check_module_visibility($mod, 0, 0); 952 } 953 954 // Showing the modules. 955 foreach ($modules as $mod) { 956 set_coursemodule_visible($mod->cmid, 1); 957 $this->check_module_visibility($mod, 1, 1); 958 } 959 } 960 961 public function test_section_visibility_events() { 962 $this->setAdminUser(); 963 $this->resetAfterTest(true); 964 965 $course = $this->getDataGenerator()->create_course(array('numsections' => 1), array('createsections' => true)); 966 $sectionnumber = 1; 967 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 968 array('section' => $sectionnumber)); 969 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 970 'course' => $course->id), array('section' => $sectionnumber)); 971 $sink = $this->redirectEvents(); 972 set_section_visible($course->id, $sectionnumber, 0); 973 $events = $sink->get_events(); 974 975 // Extract the number of events related to what we are testing, other events 976 // such as course_section_updated could have been triggered. 977 $count = 0; 978 foreach ($events as $event) { 979 if ($event instanceof \core\event\course_module_updated) { 980 $count++; 981 } 982 } 983 $this->assertSame(2, $count); 984 $sink->close(); 985 } 986 987 public function test_section_visibility() { 988 $this->setAdminUser(); 989 $this->resetAfterTest(true); 990 991 // Create course. 992 $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true)); 993 994 $sink = $this->redirectEvents(); 995 996 // Testing an empty section. 997 $sectionnumber = 1; 998 set_section_visible($course->id, $sectionnumber, 0); 999 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1000 $this->assertEquals($section_info->visible, 0); 1001 set_section_visible($course->id, $sectionnumber, 1); 1002 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1003 $this->assertEquals($section_info->visible, 1); 1004 1005 // Checking that an event was fired. 1006 $events = $sink->get_events(); 1007 $this->assertInstanceOf('\core\event\course_section_updated', $events[0]); 1008 $sink->close(); 1009 1010 // Testing a section with visible modules. 1011 $sectionnumber = 2; 1012 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 1013 array('section' => $sectionnumber)); 1014 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 1015 'course' => $course->id), array('section' => $sectionnumber)); 1016 $modules = compact('forum', 'assign'); 1017 set_section_visible($course->id, $sectionnumber, 0); 1018 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1019 $this->assertEquals($section_info->visible, 0); 1020 foreach ($modules as $mod) { 1021 $this->check_module_visibility($mod, 0, 1); 1022 } 1023 set_section_visible($course->id, $sectionnumber, 1); 1024 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1025 $this->assertEquals($section_info->visible, 1); 1026 foreach ($modules as $mod) { 1027 $this->check_module_visibility($mod, 1, 1); 1028 } 1029 1030 // Testing a section with hidden modules, which should stay hidden. 1031 $sectionnumber = 3; 1032 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 1033 array('section' => $sectionnumber)); 1034 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 1035 'course' => $course->id), array('section' => $sectionnumber)); 1036 $modules = compact('forum', 'assign'); 1037 foreach ($modules as $mod) { 1038 set_coursemodule_visible($mod->cmid, 0); 1039 $this->check_module_visibility($mod, 0, 0); 1040 } 1041 set_section_visible($course->id, $sectionnumber, 0); 1042 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1043 $this->assertEquals($section_info->visible, 0); 1044 foreach ($modules as $mod) { 1045 $this->check_module_visibility($mod, 0, 0); 1046 } 1047 set_section_visible($course->id, $sectionnumber, 1); 1048 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1049 $this->assertEquals($section_info->visible, 1); 1050 foreach ($modules as $mod) { 1051 $this->check_module_visibility($mod, 0, 0); 1052 } 1053 } 1054 1055 /** 1056 * Helper function to assert that a module has correctly been made visible, or hidden. 1057 * 1058 * @param stdClass $mod module information 1059 * @param int $visibility the current state of the module 1060 * @param int $visibleold the current state of the visibleold property 1061 * @return void 1062 */ 1063 public function check_module_visibility($mod, $visibility, $visibleold) { 1064 global $DB; 1065 $cm = get_fast_modinfo($mod->course)->get_cm($mod->cmid); 1066 $this->assertEquals($visibility, $cm->visible); 1067 $this->assertEquals($visibleold, $cm->visibleold); 1068 1069 // Check the module grade items. 1070 $grade_items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $cm->modname, 1071 'iteminstance' => $cm->instance, 'courseid' => $cm->course)); 1072 if ($grade_items) { 1073 foreach ($grade_items as $grade_item) { 1074 if ($visibility) { 1075 $this->assertFalse($grade_item->is_hidden(), "$cm->modname grade_item not visible"); 1076 } else { 1077 $this->assertTrue($grade_item->is_hidden(), "$cm->modname grade_item not hidden"); 1078 } 1079 } 1080 } 1081 1082 // Check the events visibility. 1083 if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $cm->modname))) { 1084 foreach ($events as $event) { 1085 $calevent = new calendar_event($event); 1086 $this->assertEquals($visibility, $calevent->visible, "$cm->modname calendar_event visibility"); 1087 } 1088 } 1089 } 1090 1091 public function test_course_page_type_list() { 1092 global $DB; 1093 $this->resetAfterTest(true); 1094 1095 // Create a category. 1096 $category = new stdClass(); 1097 $category->name = 'Test Category'; 1098 1099 $testcategory = $this->getDataGenerator()->create_category($category); 1100 1101 // Create a course. 1102 $course = new stdClass(); 1103 $course->fullname = 'Apu loves Unit TÉ™sts'; 1104 $course->shortname = 'Spread the lÅve'; 1105 $course->idnumber = '123'; 1106 $course->summary = 'Awesome!'; 1107 $course->summaryformat = FORMAT_PLAIN; 1108 $course->format = 'topics'; 1109 $course->newsitems = 0; 1110 $course->numsections = 5; 1111 $course->category = $testcategory->id; 1112 1113 $testcourse = $this->getDataGenerator()->create_course($course); 1114 1115 // Create contexts. 1116 $coursecontext = context_course::instance($testcourse->id); 1117 $parentcontext = $coursecontext->get_parent_context(); // Not actually used. 1118 $pagetype = 'page-course-x'; // Not used either. 1119 $pagetypelist = course_page_type_list($pagetype, $parentcontext, $coursecontext); 1120 1121 // Page type lists for normal courses. 1122 $testpagetypelist1 = array(); 1123 $testpagetypelist1['*'] = 'Any page'; 1124 $testpagetypelist1['course-*'] = 'Any course page'; 1125 $testpagetypelist1['course-view-*'] = 'Any type of course main page'; 1126 1127 $this->assertEquals($testpagetypelist1, $pagetypelist); 1128 1129 // Get the context for the front page course. 1130 $sitecoursecontext = context_course::instance(SITEID); 1131 $pagetypelist = course_page_type_list($pagetype, $parentcontext, $sitecoursecontext); 1132 1133 // Page type list for the front page course. 1134 $testpagetypelist2 = array('*' => 'Any page'); 1135 $this->assertEquals($testpagetypelist2, $pagetypelist); 1136 1137 // Make sure that providing no current context to the function doesn't result in an error. 1138 // Calls made from generate_page_type_patterns() may provide null values. 1139 $pagetypelist = course_page_type_list($pagetype, null, null); 1140 $this->assertEquals($pagetypelist, $testpagetypelist1); 1141 } 1142 1143 public function test_compare_activities_by_time_desc() { 1144 1145 // Let's create some test data. 1146 $activitiesivities = array(); 1147 $x = new stdClass(); 1148 $x->timestamp = null; 1149 $activities[] = $x; 1150 1151 $x = new stdClass(); 1152 $x->timestamp = 1; 1153 $activities[] = $x; 1154 1155 $x = new stdClass(); 1156 $x->timestamp = 3; 1157 $activities[] = $x; 1158 1159 $x = new stdClass(); 1160 $x->timestamp = 0; 1161 $activities[] = $x; 1162 1163 $x = new stdClass(); 1164 $x->timestamp = 5; 1165 $activities[] = $x; 1166 1167 $x = new stdClass(); 1168 $activities[] = $x; 1169 1170 $x = new stdClass(); 1171 $x->timestamp = 5; 1172 $activities[] = $x; 1173 1174 // Do the sorting. 1175 usort($activities, 'compare_activities_by_time_desc'); 1176 1177 // Let's check the result. 1178 $last = 10; 1179 foreach($activities as $activity) { 1180 if (empty($activity->timestamp)) { 1181 $activity->timestamp = 0; 1182 } 1183 $this->assertLessThanOrEqual($last, $activity->timestamp); 1184 } 1185 } 1186 1187 public function test_compare_activities_by_time_asc() { 1188 1189 // Let's create some test data. 1190 $activities = array(); 1191 $x = new stdClass(); 1192 $x->timestamp = null; 1193 $activities[] = $x; 1194 1195 $x = new stdClass(); 1196 $x->timestamp = 1; 1197 $activities[] = $x; 1198 1199 $x = new stdClass(); 1200 $x->timestamp = 3; 1201 $activities[] = $x; 1202 1203 $x = new stdClass(); 1204 $x->timestamp = 0; 1205 $activities[] = $x; 1206 1207 $x = new stdClass(); 1208 $x->timestamp = 5; 1209 $activities[] = $x; 1210 1211 $x = new stdClass(); 1212 $activities[] = $x; 1213 1214 $x = new stdClass(); 1215 $x->timestamp = 5; 1216 $activities[] = $x; 1217 1218 // Do the sorting. 1219 usort($activities, 'compare_activities_by_time_asc'); 1220 1221 // Let's check the result. 1222 $last = 0; 1223 foreach($activities as $activity) { 1224 if (empty($activity->timestamp)) { 1225 $activity->timestamp = 0; 1226 } 1227 $this->assertGreaterThanOrEqual($last, $activity->timestamp); 1228 } 1229 } 1230 1231 /** 1232 * Tests moving a module between hidden/visible sections and 1233 * verifies that the course/module visiblity seettings are 1234 * retained. 1235 */ 1236 public function test_moveto_module_between_hidden_sections() { 1237 global $DB; 1238 1239 $this->resetAfterTest(true); 1240 1241 $course = $this->getDataGenerator()->create_course(array('numsections' => 4), array('createsections' => true)); 1242 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 1243 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id)); 1244 $quiz= $this->getDataGenerator()->create_module('quiz', array('course' => $course->id)); 1245 1246 // Set the page as hidden 1247 set_coursemodule_visible($page->cmid, 0); 1248 1249 // Set sections 3 as hidden. 1250 set_section_visible($course->id, 3, 0); 1251 1252 $modinfo = get_fast_modinfo($course); 1253 1254 $hiddensection = $modinfo->get_section_info(3); 1255 // New section is definitely not visible: 1256 $this->assertEquals($hiddensection->visible, 0); 1257 1258 $forumcm = $modinfo->cms[$forum->cmid]; 1259 $pagecm = $modinfo->cms[$page->cmid]; 1260 1261 // Move the forum and the page to a hidden section, make sure moveto_module returns 0 as new visibility state. 1262 $this->assertEquals(0, moveto_module($forumcm, $hiddensection)); 1263 $this->assertEquals(0, moveto_module($pagecm, $hiddensection)); 1264 1265 $modinfo = get_fast_modinfo($course); 1266 1267 // Verify that forum and page have been moved to the hidden section and quiz has not. 1268 $this->assertContains($forum->cmid, $modinfo->sections[3]); 1269 $this->assertContains($page->cmid, $modinfo->sections[3]); 1270 $this->assertNotContains($quiz->cmid, $modinfo->sections[3]); 1271 1272 // Verify that forum has been made invisible. 1273 $forumcm = $modinfo->cms[$forum->cmid]; 1274 $this->assertEquals($forumcm->visible, 0); 1275 // Verify that old state has been retained. 1276 $this->assertEquals($forumcm->visibleold, 1); 1277 1278 // Verify that page has stayed invisible. 1279 $pagecm = $modinfo->cms[$page->cmid]; 1280 $this->assertEquals($pagecm->visible, 0); 1281 // Verify that old state has been retained. 1282 $this->assertEquals($pagecm->visibleold, 0); 1283 1284 // Verify that quiz has been unaffected. 1285 $quizcm = $modinfo->cms[$quiz->cmid]; 1286 $this->assertEquals($quizcm->visible, 1); 1287 1288 // Move forum and page back to visible section. 1289 // Make sure the visibility is restored to the original value (visible for forum and hidden for page). 1290 $visiblesection = $modinfo->get_section_info(2); 1291 $this->assertEquals(1, moveto_module($forumcm, $visiblesection)); 1292 $this->assertEquals(0, moveto_module($pagecm, $visiblesection)); 1293 1294 $modinfo = get_fast_modinfo($course); 1295 1296 // Double check that forum has been made visible. 1297 $forumcm = $modinfo->cms[$forum->cmid]; 1298 $this->assertEquals($forumcm->visible, 1); 1299 1300 // Double check that page has stayed invisible. 1301 $pagecm = $modinfo->cms[$page->cmid]; 1302 $this->assertEquals($pagecm->visible, 0); 1303 1304 // Move the page in the same section (this is what mod duplicate does). 1305 // Visibility of page remains 0. 1306 $this->assertEquals(0, moveto_module($pagecm, $visiblesection, $forumcm)); 1307 1308 // Double check that the the page is still hidden. 1309 $modinfo = get_fast_modinfo($course); 1310 $pagecm = $modinfo->cms[$page->cmid]; 1311 $this->assertEquals($pagecm->visible, 0); 1312 } 1313 1314 /** 1315 * Tests moving a module around in the same section. moveto_module() 1316 * is called this way in modduplicate. 1317 */ 1318 public function test_moveto_module_in_same_section() { 1319 global $DB; 1320 1321 $this->resetAfterTest(true); 1322 1323 $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true)); 1324 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id)); 1325 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 1326 1327 // Simulate inconsistent visible/visibleold values (MDL-38713). 1328 $cm = $DB->get_record('course_modules', array('id' => $page->cmid), '*', MUST_EXIST); 1329 $cm->visible = 0; 1330 $cm->visibleold = 1; 1331 $DB->update_record('course_modules', $cm); 1332 1333 $modinfo = get_fast_modinfo($course); 1334 $forumcm = $modinfo->cms[$forum->cmid]; 1335 $pagecm = $modinfo->cms[$page->cmid]; 1336 1337 // Verify that page is hidden. 1338 $this->assertEquals($pagecm->visible, 0); 1339 1340 // Verify section 0 is where all mods added. 1341 $section = $modinfo->get_section_info(0); 1342 $this->assertEquals($section->id, $forumcm->section); 1343 $this->assertEquals($section->id, $pagecm->section); 1344 1345 1346 // Move the page inside the hidden section. Make sure it is hidden. 1347 $this->assertEquals(0, moveto_module($pagecm, $section, $forumcm)); 1348 1349 // Double check that the the page is still hidden. 1350 $modinfo = get_fast_modinfo($course); 1351 $pagecm = $modinfo->cms[$page->cmid]; 1352 $this->assertEquals($pagecm->visible, 0); 1353 } 1354 1355 public function test_course_delete_module() { 1356 global $DB; 1357 $this->resetAfterTest(true); 1358 $this->setAdminUser(); 1359 1360 // Create course and modules. 1361 $course = $this->getDataGenerator()->create_course(array('numsections' => 5)); 1362 1363 // Generate an assignment with due date (will generate a course event). 1364 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 'course' => $course->id)); 1365 1366 // Get the module context. 1367 $modcontext = context_module::instance($assign->cmid); 1368 1369 // Verify context exists. 1370 $this->assertInstanceOf('context_module', $modcontext); 1371 1372 // Add some tags to this assignment. 1373 tag_set('assign', $assign->id, array('Tag 1', 'Tag 2', 'Tag 3'), 'mod_assign', $modcontext->id); 1374 1375 // Confirm the tag instances were added. 1376 $this->assertEquals(3, $DB->count_records('tag_instance', array('component' => 'mod_assign', 'contextid' => 1377 $modcontext->id))); 1378 1379 // Verify event assignment event has been generated. 1380 $eventcount = $DB->count_records('event', array('instance' => $assign->id, 'modulename' => 'assign')); 1381 $this->assertEquals(1, $eventcount); 1382 1383 // Run delete.. 1384 course_delete_module($assign->cmid); 1385 1386 // Verify the context has been removed. 1387 $this->assertFalse(context_module::instance($assign->cmid, IGNORE_MISSING)); 1388 1389 // Verify the course_module record has been deleted. 1390 $cmcount = $DB->count_records('course_modules', array('id' => $assign->cmid)); 1391 $this->assertEmpty($cmcount); 1392 1393 // Verify event assignment events have been removed. 1394 $eventcount = $DB->count_records('event', array('instance' => $assign->id, 'modulename' => 'assign')); 1395 $this->assertEmpty($eventcount); 1396 1397 // Verify the tag instances were deleted. 1398 $this->assertEquals(0, $DB->count_records('tag_instance', array('component' => 'mod_assign', 'contextid' => 1399 $modcontext->id))); 1400 } 1401 1402 /** 1403 * Test that triggering a course_created event works as expected. 1404 */ 1405 public function test_course_created_event() { 1406 global $DB; 1407 1408 $this->resetAfterTest(); 1409 1410 // Catch the events. 1411 $sink = $this->redirectEvents(); 1412 1413 // Create the course with an id number which is used later when generating a course via the imsenterprise plugin. 1414 $data = new stdClass(); 1415 $data->idnumber = 'idnumber'; 1416 $course = $this->getDataGenerator()->create_course($data); 1417 // Get course from DB for comparison. 1418 $course = $DB->get_record('course', array('id' => $course->id)); 1419 1420 // Capture the event. 1421 $events = $sink->get_events(); 1422 $sink->close(); 1423 1424 // Validate the event. 1425 $event = $events[0]; 1426 $this->assertInstanceOf('\core\event\course_created', $event); 1427 $this->assertEquals('course', $event->objecttable); 1428 $this->assertEquals($course->id, $event->objectid); 1429 $this->assertEquals(context_course::instance($course->id), $event->get_context()); 1430 $this->assertEquals($course, $event->get_record_snapshot('course', $course->id)); 1431 $this->assertEquals('course_created', $event->get_legacy_eventname()); 1432 $this->assertEventLegacyData($course, $event); 1433 $expectedlog = array(SITEID, 'course', 'new', 'view.php?id=' . $course->id, $course->fullname . ' (ID ' . $course->id . ')'); 1434 $this->assertEventLegacyLogData($expectedlog, $event); 1435 1436 // Now we want to trigger creating a course via the imsenterprise. 1437 // Delete the course we created earlier, as we want the imsenterprise plugin to create this. 1438 // We do not want print out any of the text this function generates while doing this, which is why 1439 // we are using ob_start() and ob_end_clean(). 1440 ob_start(); 1441 delete_course($course); 1442 ob_end_clean(); 1443 1444 // Create the XML file we want to use. 1445 $imstestcase = new enrol_imsenterprise_testcase(); 1446 $imstestcase->imsplugin = enrol_get_plugin('imsenterprise'); 1447 $imstestcase->set_test_config(); 1448 $imstestcase->set_xml_file(false, array($course)); 1449 1450 // Capture the event. 1451 $sink = $this->redirectEvents(); 1452 $imstestcase->imsplugin->cron(); 1453 $events = $sink->get_events(); 1454 $sink->close(); 1455 $event = $events[0]; 1456 1457 // Validate the event triggered is \core\event\course_created. There is no need to validate the other values 1458 // as they have already been validated in the previous steps. Here we only want to make sure that when the 1459 // imsenterprise plugin creates a course an event is triggered. 1460 $this->assertInstanceOf('\core\event\course_created', $event); 1461 $this->assertEventContextNotUsed($event); 1462 } 1463 1464 /** 1465 * Test that triggering a course_updated event works as expected. 1466 */ 1467 public function test_course_updated_event() { 1468 global $DB; 1469 1470 $this->resetAfterTest(); 1471 1472 // Create a course. 1473 $course = $this->getDataGenerator()->create_course(); 1474 1475 // Create a category we are going to move this course to. 1476 $category = $this->getDataGenerator()->create_category(); 1477 1478 // Create a hidden category we are going to move this course to. 1479 $categoryhidden = $this->getDataGenerator()->create_category(array('visible' => 0)); 1480 1481 // Update course and catch course_updated event. 1482 $sink = $this->redirectEvents(); 1483 update_course($course); 1484 $events = $sink->get_events(); 1485 $sink->close(); 1486 1487 // Get updated course information from the DB. 1488 $updatedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); 1489 // Validate event. 1490 $event = array_shift($events); 1491 $this->assertInstanceOf('\core\event\course_updated', $event); 1492 $this->assertEquals('course', $event->objecttable); 1493 $this->assertEquals($updatedcourse->id, $event->objectid); 1494 $this->assertEquals(context_course::instance($course->id), $event->get_context()); 1495 $url = new moodle_url('/course/edit.php', array('id' => $event->objectid)); 1496 $this->assertEquals($url, $event->get_url()); 1497 $this->assertEquals($updatedcourse, $event->get_record_snapshot('course', $event->objectid)); 1498 $this->assertEquals('course_updated', $event->get_legacy_eventname()); 1499 $this->assertEventLegacyData($updatedcourse, $event); 1500 $expectedlog = array($updatedcourse->id, 'course', 'update', 'edit.php?id=' . $course->id, $course->id); 1501 $this->assertEventLegacyLogData($expectedlog, $event); 1502 1503 // Move course and catch course_updated event. 1504 $sink = $this->redirectEvents(); 1505 move_courses(array($course->id), $category->id); 1506 $events = $sink->get_events(); 1507 $sink->close(); 1508 1509 // Return the moved course information from the DB. 1510 $movedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); 1511 // Validate event. 1512 $event = array_shift($events); 1513 $this->assertInstanceOf('\core\event\course_updated', $event); 1514 $this->assertEquals('course', $event->objecttable); 1515 $this->assertEquals($movedcourse->id, $event->objectid); 1516 $this->assertEquals(context_course::instance($course->id), $event->get_context()); 1517 $this->assertEquals($movedcourse, $event->get_record_snapshot('course', $movedcourse->id)); 1518 $this->assertEquals('course_updated', $event->get_legacy_eventname()); 1519 $this->assertEventLegacyData($movedcourse, $event); 1520 $expectedlog = array($movedcourse->id, 'course', 'move', 'edit.php?id=' . $movedcourse->id, $movedcourse->id); 1521 $this->assertEventLegacyLogData($expectedlog, $event); 1522 1523 // Move course to hidden category and catch course_updated event. 1524 $sink = $this->redirectEvents(); 1525 move_courses(array($course->id), $categoryhidden->id); 1526 $events = $sink->get_events(); 1527 $sink->close(); 1528 1529 // Return the moved course information from the DB. 1530 $movedcoursehidden = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); 1531 // Validate event. 1532 $event = array_shift($events); 1533 $this->assertInstanceOf('\core\event\course_updated', $event); 1534 $this->assertEquals('course', $event->objecttable); 1535 $this->assertEquals($movedcoursehidden->id, $event->objectid); 1536 $this->assertEquals(context_course::instance($course->id), $event->get_context()); 1537 $this->assertEquals($movedcoursehidden, $event->get_record_snapshot('course', $movedcoursehidden->id)); 1538 $this->assertEquals('course_updated', $event->get_legacy_eventname()); 1539 $this->assertEventLegacyData($movedcoursehidden, $event); 1540 $expectedlog = array($movedcoursehidden->id, 'course', 'move', 'edit.php?id=' . $movedcoursehidden->id, $movedcoursehidden->id); 1541 $this->assertEventLegacyLogData($expectedlog, $event); 1542 $this->assertEventContextNotUsed($event); 1543 } 1544 1545 /** 1546 * Test that triggering a course_deleted event works as expected. 1547 */ 1548 public function test_course_deleted_event() { 1549 $this->resetAfterTest(); 1550 1551 // Create the course. 1552 $course = $this->getDataGenerator()->create_course(); 1553 1554 // Save the course context before we delete the course. 1555 $coursecontext = context_course::instance($course->id); 1556 1557 // Catch the update event. 1558 $sink = $this->redirectEvents(); 1559 1560 // Call delete_course() which will trigger the course_deleted event and the course_content_deleted 1561 // event. This function prints out data to the screen, which we do not want during a PHPUnit test, 1562 // so use ob_start and ob_end_clean to prevent this. 1563 ob_start(); 1564 delete_course($course); 1565 ob_end_clean(); 1566 1567 // Capture the event. 1568 $events = $sink->get_events(); 1569 $sink->close(); 1570 1571 // Validate the event. 1572 $event = $events[1]; 1573 $this->assertInstanceOf('\core\event\course_deleted', $event); 1574 $this->assertEquals('course', $event->objecttable); 1575 $this->assertEquals($course->id, $event->objectid); 1576 $this->assertEquals($coursecontext->id, $event->contextid); 1577 $this->assertEquals($course, $event->get_record_snapshot('course', $course->id)); 1578 $this->assertEquals('course_deleted', $event->get_legacy_eventname()); 1579 $eventdata = $event->get_data(); 1580 $this->assertSame($course->idnumber, $eventdata['other']['idnumber']); 1581 $this->assertSame($course->fullname, $eventdata['other']['fullname']); 1582 $this->assertSame($course->shortname, $eventdata['other']['shortname']); 1583 1584 // The legacy data also passed the context in the course object and substitutes timemodified with the current date. 1585 $expectedlegacy = clone($course); 1586 $expectedlegacy->context = $coursecontext; 1587 $expectedlegacy->timemodified = $event->timecreated; 1588 $this->assertEventLegacyData($expectedlegacy, $event); 1589 1590 // Validate legacy log data. 1591 $expectedlog = array(SITEID, 'course', 'delete', 'view.php?id=' . $course->id, $course->fullname . '(ID ' . $course->id . ')'); 1592 $this->assertEventLegacyLogData($expectedlog, $event); 1593 $this->assertEventContextNotUsed($event); 1594 } 1595 1596 /** 1597 * Test that triggering a course_content_deleted event works as expected. 1598 */ 1599 public function test_course_content_deleted_event() { 1600 global $DB; 1601 1602 $this->resetAfterTest(); 1603 1604 // Create the course. 1605 $course = $this->getDataGenerator()->create_course(); 1606 1607 // Get the course from the DB. The data generator adds some extra properties, such as 1608 // numsections, to the course object which will fail the assertions later on. 1609 $course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); 1610 1611 // Save the course context before we delete the course. 1612 $coursecontext = context_course::instance($course->id); 1613 1614 // Catch the update event. 1615 $sink = $this->redirectEvents(); 1616 1617 remove_course_contents($course->id, false); 1618 1619 // Capture the event. 1620 $events = $sink->get_events(); 1621 $sink->close(); 1622 1623 // Validate the event. 1624 $event = $events[0]; 1625 $this->assertInstanceOf('\core\event\course_content_deleted', $event); 1626 $this->assertEquals('course', $event->objecttable); 1627 $this->assertEquals($course->id, $event->objectid); 1628 $this->assertEquals($coursecontext->id, $event->contextid); 1629 $this->assertEquals($course, $event->get_record_snapshot('course', $course->id)); 1630 $this->assertEquals('course_content_removed', $event->get_legacy_eventname()); 1631 // The legacy data also passed the context and options in the course object. 1632 $course->context = $coursecontext; 1633 $course->options = array(); 1634 $this->assertEventLegacyData($course, $event); 1635 $this->assertEventContextNotUsed($event); 1636 } 1637 1638 /** 1639 * Test that triggering a course_category_deleted event works as expected. 1640 */ 1641 public function test_course_category_deleted_event() { 1642 $this->resetAfterTest(); 1643 1644 // Create a category. 1645 $category = $this->getDataGenerator()->create_category(); 1646 1647 // Save the context before it is deleted. 1648 $categorycontext = context_coursecat::instance($category->id); 1649 1650 // Catch the update event. 1651 $sink = $this->redirectEvents(); 1652 1653 // Delete the category. 1654 $category->delete_full(); 1655 1656 // Capture the event. 1657 $events = $sink->get_events(); 1658 $sink->close(); 1659 1660 // Validate the event. 1661 $event = $events[0]; 1662 $this->assertInstanceOf('\core\event\course_category_deleted', $event); 1663 $this->assertEquals('course_categories', $event->objecttable); 1664 $this->assertEquals($category->id, $event->objectid); 1665 $this->assertEquals($categorycontext->id, $event->contextid); 1666 $this->assertEquals('course_category_deleted', $event->get_legacy_eventname()); 1667 $this->assertEquals(null, $event->get_url()); 1668 $this->assertEventLegacyData($category, $event); 1669 $expectedlog = array(SITEID, 'category', 'delete', 'index.php', $category->name . '(ID ' . $category->id . ')'); 1670 $this->assertEventLegacyLogData($expectedlog, $event); 1671 1672 // Create two categories. 1673 $category = $this->getDataGenerator()->create_category(); 1674 $category2 = $this->getDataGenerator()->create_category(); 1675 1676 // Save the context before it is moved and then deleted. 1677 $category2context = context_coursecat::instance($category2->id); 1678 1679 // Catch the update event. 1680 $sink = $this->redirectEvents(); 1681 1682 // Move the category. 1683 $category2->delete_move($category->id); 1684 1685 // Capture the event. 1686 $events = $sink->get_events(); 1687 $sink->close(); 1688 1689 // Validate the event. 1690 $event = $events[0]; 1691 $this->assertInstanceOf('\core\event\course_category_deleted', $event); 1692 $this->assertEquals('course_categories', $event->objecttable); 1693 $this->assertEquals($category2->id, $event->objectid); 1694 $this->assertEquals($category2context->id, $event->contextid); 1695 $this->assertEquals('course_category_deleted', $event->get_legacy_eventname()); 1696 $this->assertEventLegacyData($category2, $event); 1697 $expectedlog = array(SITEID, 'category', 'delete', 'index.php', $category2->name . '(ID ' . $category2->id . ')'); 1698 $this->assertEventLegacyLogData($expectedlog, $event); 1699 $this->assertEventContextNotUsed($event); 1700 } 1701 1702 /** 1703 * Test that triggering a course_restored event works as expected. 1704 */ 1705 public function test_course_restored_event() { 1706 global $CFG; 1707 1708 // Get the necessary files to perform backup and restore. 1709 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); 1710 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 1711 1712 $this->resetAfterTest(); 1713 1714 // Set to admin user. 1715 $this->setAdminUser(); 1716 1717 // The user id is going to be 2 since we are the admin user. 1718 $userid = 2; 1719 1720 // Create a course. 1721 $course = $this->getDataGenerator()->create_course(); 1722 1723 // Create backup file and save it to the backup location. 1724 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, 1725 backup::INTERACTIVE_NO, backup::MODE_GENERAL, $userid); 1726 $bc->execute_plan(); 1727 $results = $bc->get_results(); 1728 $file = $results['backup_destination']; 1729 $fp = get_file_packer(); 1730 $filepath = $CFG->dataroot . '/temp/backup/test-restore-course-event'; 1731 $file->extract_to_pathname($fp, $filepath); 1732 $bc->destroy(); 1733 unset($bc); 1734 1735 // Now we want to catch the restore course event. 1736 $sink = $this->redirectEvents(); 1737 1738 // Now restore the course to trigger the event. 1739 $rc = new restore_controller('test-restore-course-event', $course->id, backup::INTERACTIVE_NO, 1740 backup::MODE_GENERAL, $userid, backup::TARGET_NEW_COURSE); 1741 $rc->execute_precheck(); 1742 $rc->execute_plan(); 1743 1744 // Capture the event. 1745 $events = $sink->get_events(); 1746 $sink->close(); 1747 1748 // Validate the event. 1749 $event = $events[0]; 1750 $this->assertInstanceOf('\core\event\course_restored', $event); 1751 $this->assertEquals('course', $event->objecttable); 1752 $this->assertEquals($rc->get_courseid(), $event->objectid); 1753 $this->assertEquals(context_course::instance($rc->get_courseid())->id, $event->contextid); 1754 $this->assertEquals('course_restored', $event->get_legacy_eventname()); 1755 $legacydata = (object) array( 1756 'courseid' => $rc->get_courseid(), 1757 'userid' => $rc->get_userid(), 1758 'type' => $rc->get_type(), 1759 'target' => $rc->get_target(), 1760 'mode' => $rc->get_mode(), 1761 'operation' => $rc->get_operation(), 1762 'samesite' => $rc->is_samesite() 1763 ); 1764 $url = new moodle_url('/course/view.php', array('id' => $event->objectid)); 1765 $this->assertEquals($url, $event->get_url()); 1766 $this->assertEventLegacyData($legacydata, $event); 1767 $this->assertEventContextNotUsed($event); 1768 1769 // Destroy the resource controller since we are done using it. 1770 $rc->destroy(); 1771 unset($rc); 1772 } 1773 1774 /** 1775 * Test that triggering a course_section_updated event works as expected. 1776 */ 1777 public function test_course_section_updated_event() { 1778 global $DB; 1779 1780 $this->resetAfterTest(); 1781 1782 // Create the course with sections. 1783 $course = $this->getDataGenerator()->create_course(array('numsections' => 10), array('createsections' => true)); 1784 $sections = $DB->get_records('course_sections', array('course' => $course->id)); 1785 1786 $coursecontext = context_course::instance($course->id); 1787 1788 $section = array_pop($sections); 1789 $section->name = 'Test section'; 1790 $section->summary = 'Test section summary'; 1791 $DB->update_record('course_sections', $section); 1792 1793 // Trigger an event for course section update. 1794 $event = \core\event\course_section_updated::create( 1795 array( 1796 'objectid' => $section->id, 1797 'courseid' => $course->id, 1798 'context' => context_course::instance($course->id), 1799 'other' => array( 1800 'sectionnum' => $section->section 1801 ) 1802 ) 1803 ); 1804 $event->add_record_snapshot('course_sections', $section); 1805 // Trigger and catch event. 1806 $sink = $this->redirectEvents(); 1807 $event->trigger(); 1808 $events = $sink->get_events(); 1809 $sink->close(); 1810 1811 // Validate the event. 1812 $event = $events[0]; 1813 $this->assertInstanceOf('\core\event\course_section_updated', $event); 1814 $this->assertEquals('course_sections', $event->objecttable); 1815 $this->assertEquals($section->id, $event->objectid); 1816 $this->assertEquals($course->id, $event->courseid); 1817 $this->assertEquals($coursecontext->id, $event->contextid); 1818 $this->assertEquals($section->section, $event->other['sectionnum']); 1819 $expecteddesc = "The user with id '{$event->userid}' updated section number '{$event->other['sectionnum']}' for the course with id '{$event->courseid}'"; 1820 $this->assertEquals($expecteddesc, $event->get_description()); 1821 $url = new moodle_url('/course/editsection.php', array('id' => $event->objectid)); 1822 $this->assertEquals($url, $event->get_url()); 1823 $this->assertEquals($section, $event->get_record_snapshot('course_sections', $event->objectid)); 1824 $id = $section->id; 1825 $sectionnum = $section->section; 1826 $expectedlegacydata = array($course->id, "course", "editsection", 'editsection.php?id=' . $id, $sectionnum); 1827 $this->assertEventLegacyLogData($expectedlegacydata, $event); 1828 $this->assertEventContextNotUsed($event); 1829 } 1830 1831 public function test_course_integrity_check() { 1832 global $DB; 1833 1834 $this->resetAfterTest(true); 1835 $course = $this->getDataGenerator()->create_course(array('numsections' => 1), 1836 array('createsections'=>true)); 1837 1838 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 1839 array('section' => 0)); 1840 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id), 1841 array('section' => 0)); 1842 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id), 1843 array('section' => 0)); 1844 $correctseq = join(',', array($forum->cmid, $page->cmid, $quiz->cmid)); 1845 1846 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 1847 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 1848 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 1849 $this->assertEquals($correctseq, $section0->sequence); 1850 $this->assertEmpty($section1->sequence); 1851 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 1852 $this->assertEquals($section0->id, $cms[$page->cmid]->section); 1853 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 1854 $this->assertEmpty(course_integrity_check($course->id)); 1855 1856 // Now let's make manual change in DB and let course_integrity_check() fix it: 1857 1858 // 1. Module appears twice in one section. 1859 $DB->update_record('course_sections', array('id' => $section0->id, 'sequence' => $section0->sequence. ','. $page->cmid)); 1860 $this->assertEquals( 1861 array('Failed integrity check for course ['. $course->id. 1862 ']. Sequence for course section ['. $section0->id. '] is "'. 1863 $section0->sequence. ','. $page->cmid. '", must be "'. 1864 $section0->sequence. '"'), 1865 course_integrity_check($course->id)); 1866 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 1867 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 1868 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 1869 $this->assertEquals($correctseq, $section0->sequence); 1870 $this->assertEmpty($section1->sequence); 1871 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 1872 $this->assertEquals($section0->id, $cms[$page->cmid]->section); 1873 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 1874 1875 // 2. Module appears in two sections (last section wins). 1876 $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''. $page->cmid)); 1877 // First message about double mentioning in sequence, second message about wrong section field for $page. 1878 $this->assertEquals(array( 1879 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid. 1880 '] must be removed from sequence of section ['. $section0->id. 1881 '] because it is also present in sequence of section ['. $section1->id. ']', 1882 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid. 1883 '] points to section ['. $section0->id. '] instead of ['. $section1->id. ']'), 1884 course_integrity_check($course->id)); 1885 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 1886 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 1887 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 1888 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence); 1889 $this->assertEquals(''. $page->cmid, $section1->sequence); 1890 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 1891 $this->assertEquals($section1->id, $cms[$page->cmid]->section); 1892 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 1893 1894 // 3. Module id is not present in course_section.sequence (integrity check with $fullcheck = false). 1895 $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => '')); 1896 $this->assertEmpty(course_integrity_check($course->id)); // Not an error! 1897 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 1898 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 1899 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 1900 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence); 1901 $this->assertEmpty($section1->sequence); 1902 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 1903 $this->assertEquals($section1->id, $cms[$page->cmid]->section); // Not changed. 1904 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 1905 1906 // 4. Module id is not present in course_section.sequence (integrity check with $fullcheck = true). 1907 $this->assertEquals(array('Failed integrity check for course ['. $course->id. ']. Course module ['. 1908 $page->cmid. '] is missing from sequence of section ['. $section1->id. ']'), 1909 course_integrity_check($course->id, null, null, true)); // Error! 1910 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 1911 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 1912 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 1913 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence); 1914 $this->assertEquals(''. $page->cmid, $section1->sequence); // Yay, module added to section. 1915 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 1916 $this->assertEquals($section1->id, $cms[$page->cmid]->section); // Not changed. 1917 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 1918 1919 // 5. Module id is not present in course_section.sequence and it's section is invalid (integrity check with $fullcheck = true). 1920 $DB->update_record('course_modules', array('id' => $page->cmid, 'section' => 8765)); 1921 $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => '')); 1922 $this->assertEquals(array( 1923 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid. 1924 '] is missing from sequence of section ['. $section0->id. ']', 1925 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid. 1926 '] points to section [8765] instead of ['. $section0->id. ']'), 1927 course_integrity_check($course->id, null, null, true)); 1928 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 1929 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 1930 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 1931 $this->assertEquals($forum->cmid. ','. $quiz->cmid. ','. $page->cmid, $section0->sequence); // Module added to section. 1932 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 1933 $this->assertEquals($section0->id, $cms[$page->cmid]->section); // Section changed to section0. 1934 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 1935 1936 // 6. Module is deleted from course_modules but not deleted in sequence (integrity check with $fullcheck = true). 1937 $DB->delete_records('course_modules', array('id' => $page->cmid)); 1938 $this->assertEquals(array('Failed integrity check for course ['. $course->id. ']. Course module ['. 1939 $page->cmid. '] does not exist but is present in the sequence of section ['. $section0->id. ']'), 1940 course_integrity_check($course->id, null, null, true)); 1941 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 1942 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 1943 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 1944 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence); 1945 $this->assertEmpty($section1->sequence); 1946 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 1947 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 1948 $this->assertEquals(2, count($cms)); 1949 } 1950 1951 /** 1952 * Tests for event related to course module creation. 1953 */ 1954 public function test_course_module_created_event() { 1955 global $USER, $DB; 1956 $this->resetAfterTest(); 1957 1958 // Create an assign module. 1959 $sink = $this->redirectEvents(); 1960 $modinfo = $this->create_specific_module_test('assign'); 1961 $events = $sink->get_events(); 1962 $event = array_pop($events); 1963 1964 $cm = get_coursemodule_from_id('assign', $modinfo->coursemodule, 0, false, MUST_EXIST); 1965 $mod = $DB->get_record('assign', array('id' => $modinfo->instance), '*', MUST_EXIST); 1966 1967 // Validate event data. 1968 $this->assertInstanceOf('\core\event\course_module_created', $event); 1969 $this->assertEquals($cm->id, $event->objectid); 1970 $this->assertEquals($USER->id, $event->userid); 1971 $this->assertEquals('course_modules', $event->objecttable); 1972 $url = new moodle_url('/mod/assign/view.php', array('id' => $cm->id)); 1973 $this->assertEquals($url, $event->get_url()); 1974 1975 // Test legacy data. 1976 $this->assertSame('mod_created', $event->get_legacy_eventname()); 1977 $eventdata = new stdClass(); 1978 $eventdata->modulename = 'assign'; 1979 $eventdata->name = $mod->name; 1980 $eventdata->cmid = $cm->id; 1981 $eventdata->courseid = $cm->course; 1982 $eventdata->userid = $USER->id; 1983 $this->assertEventLegacyData($eventdata, $event); 1984 1985 $arr = array( 1986 array($cm->course, "course", "add mod", "../mod/assign/view.php?id=$cm->id", "assign $cm->instance"), 1987 array($cm->course, "assign", "add", "view.php?id=$cm->id", $cm->instance, $cm->id) 1988 ); 1989 $this->assertEventLegacyLogData($arr, $event); 1990 $this->assertEventContextNotUsed($event); 1991 1992 // Let us see if duplicating an activity results in a nice course module created event. 1993 $sink->clear(); 1994 $course = get_course($mod->course); 1995 $newcm = duplicate_module($course, $cm); 1996 $events = $sink->get_events(); 1997 $event = array_pop($events); 1998 $sink->close(); 1999 2000 // Validate event data. 2001 $this->assertInstanceOf('\core\event\course_module_created', $event); 2002 $this->assertEquals($newcm->id, $event->objectid); 2003 $this->assertEquals($USER->id, $event->userid); 2004 $this->assertEquals($course->id, $event->courseid); 2005 $url = new moodle_url('/mod/assign/view.php', array('id' => $newcm->id)); 2006 $this->assertEquals($url, $event->get_url()); 2007 } 2008 2009 /** 2010 * Tests for event validations related to course module creation. 2011 */ 2012 public function test_course_module_created_event_exceptions() { 2013 2014 $this->resetAfterTest(); 2015 2016 // Generate data. 2017 $modinfo = $this->create_specific_module_test('assign'); 2018 $context = context_module::instance($modinfo->coursemodule); 2019 2020 // Test not setting instanceid. 2021 try { 2022 $event = \core\event\course_module_created::create(array( 2023 'courseid' => $modinfo->course, 2024 'context' => $context, 2025 'objectid' => $modinfo->coursemodule, 2026 'other' => array( 2027 'modulename' => 'assign', 2028 'name' => 'My assignment', 2029 ) 2030 )); 2031 $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without 2032 other['instanceid']"); 2033 } catch (coding_exception $e) { 2034 $this->assertContains("The 'instanceid' value must be set in other.", $e->getMessage()); 2035 } 2036 2037 // Test not setting modulename. 2038 try { 2039 $event = \core\event\course_module_created::create(array( 2040 'courseid' => $modinfo->course, 2041 'context' => $context, 2042 'objectid' => $modinfo->coursemodule, 2043 'other' => array( 2044 'instanceid' => $modinfo->instance, 2045 'name' => 'My assignment', 2046 ) 2047 )); 2048 $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without 2049 other['modulename']"); 2050 } catch (coding_exception $e) { 2051 $this->assertContains("The 'modulename' value must be set in other.", $e->getMessage()); 2052 } 2053 2054 // Test not setting name. 2055 2056 try { 2057 $event = \core\event\course_module_created::create(array( 2058 'courseid' => $modinfo->course, 2059 'context' => $context, 2060 'objectid' => $modinfo->coursemodule, 2061 'other' => array( 2062 'modulename' => 'assign', 2063 'instanceid' => $modinfo->instance, 2064 ) 2065 )); 2066 $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without 2067 other['name']"); 2068 } catch (coding_exception $e) { 2069 $this->assertContains("The 'name' value must be set in other.", $e->getMessage()); 2070 } 2071 2072 } 2073 2074 /** 2075 * Tests for event related to course module updates. 2076 */ 2077 public function test_course_module_updated_event() { 2078 global $USER, $DB; 2079 $this->resetAfterTest(); 2080 2081 // Update a forum module. 2082 $sink = $this->redirectEvents(); 2083 $modinfo = $this->update_specific_module_test('forum'); 2084 $events = $sink->get_events(); 2085 $event = array_pop($events); 2086 $sink->close(); 2087 2088 $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST); 2089 $mod = $DB->get_record('forum', array('id' => $cm->instance), '*', MUST_EXIST); 2090 2091 // Validate event data. 2092 $this->assertInstanceOf('\core\event\course_module_updated', $event); 2093 $this->assertEquals($cm->id, $event->objectid); 2094 $this->assertEquals($USER->id, $event->userid); 2095 $this->assertEquals('course_modules', $event->objecttable); 2096 $url = new moodle_url('/mod/forum/view.php', array('id' => $cm->id)); 2097 $this->assertEquals($url, $event->get_url()); 2098 2099 // Test legacy data. 2100 $this->assertSame('mod_updated', $event->get_legacy_eventname()); 2101 $eventdata = new stdClass(); 2102 $eventdata->modulename = 'forum'; 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 2109 $arr = array( 2110 array($cm->course, "course", "update mod", "../mod/forum/view.php?id=$cm->id", "forum $cm->instance"), 2111 array($cm->course, "forum", "update", "view.php?id=$cm->id", $cm->instance, $cm->id) 2112 ); 2113 $this->assertEventLegacyLogData($arr, $event); 2114 $this->assertEventContextNotUsed($event); 2115 } 2116 2117 /** 2118 * Tests for create_from_cm method. 2119 */ 2120 public function test_course_module_create_from_cm() { 2121 $this->resetAfterTest(); 2122 $this->setAdminUser(); 2123 2124 // Create course and modules. 2125 $course = $this->getDataGenerator()->create_course(array('numsections' => 5)); 2126 2127 // Generate an assignment. 2128 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id)); 2129 2130 // Get the module context. 2131 $modcontext = context_module::instance($assign->cmid); 2132 2133 // Get course module. 2134 $cm = get_coursemodule_from_id(null, $assign->cmid, $course->id, false, MUST_EXIST); 2135 2136 // Create an event from course module. 2137 $event = \core\event\course_module_updated::create_from_cm($cm, $modcontext); 2138 2139 // Trigger the events. 2140 $sink = $this->redirectEvents(); 2141 $event->trigger(); 2142 $events = $sink->get_events(); 2143 $event2 = array_pop($events); 2144 2145 // Test event data. 2146 $this->assertInstanceOf('\core\event\course_module_updated', $event); 2147 $this->assertEquals($cm->id, $event2->objectid); 2148 $this->assertEquals($modcontext, $event2->get_context()); 2149 $this->assertEquals($cm->modname, $event2->other['modulename']); 2150 $this->assertEquals($cm->instance, $event2->other['instanceid']); 2151 $this->assertEquals($cm->name, $event2->other['name']); 2152 $this->assertEventContextNotUsed($event2); 2153 $this->assertSame('mod_updated', $event2->get_legacy_eventname()); 2154 $arr = array( 2155 array($cm->course, "course", "update mod", "../mod/assign/view.php?id=$cm->id", "assign $cm->instance"), 2156 array($cm->course, "assign", "update", "view.php?id=$cm->id", $cm->instance, $cm->id) 2157 ); 2158 $this->assertEventLegacyLogData($arr, $event); 2159 } 2160 2161 /** 2162 * Tests for event validations related to course module update. 2163 */ 2164 public function test_course_module_updated_event_exceptions() { 2165 2166 $this->resetAfterTest(); 2167 2168 // Generate data. 2169 $modinfo = $this->create_specific_module_test('assign'); 2170 $context = context_module::instance($modinfo->coursemodule); 2171 2172 // Test not setting instanceid. 2173 try { 2174 $event = \core\event\course_module_updated::create(array( 2175 'courseid' => $modinfo->course, 2176 'context' => $context, 2177 'objectid' => $modinfo->coursemodule, 2178 'other' => array( 2179 'modulename' => 'assign', 2180 'name' => 'My assignment', 2181 ) 2182 )); 2183 $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without 2184 other['instanceid']"); 2185 } catch (coding_exception $e) { 2186 $this->assertContains("The 'instanceid' value must be set in other.", $e->getMessage()); 2187 } 2188 2189 // Test not setting modulename. 2190 try { 2191 $event = \core\event\course_module_updated::create(array( 2192 'courseid' => $modinfo->course, 2193 'context' => $context, 2194 'objectid' => $modinfo->coursemodule, 2195 'other' => array( 2196 'instanceid' => $modinfo->instance, 2197 'name' => 'My assignment', 2198 ) 2199 )); 2200 $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without 2201 other['modulename']"); 2202 } catch (coding_exception $e) { 2203 $this->assertContains("The 'modulename' value must be set in other.", $e->getMessage()); 2204 } 2205 2206 // Test not setting name. 2207 2208 try { 2209 $event = \core\event\course_module_updated::create(array( 2210 'courseid' => $modinfo->course, 2211 'context' => $context, 2212 'objectid' => $modinfo->coursemodule, 2213 'other' => array( 2214 'modulename' => 'assign', 2215 'instanceid' => $modinfo->instance, 2216 ) 2217 )); 2218 $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without 2219 other['name']"); 2220 } catch (coding_exception $e) { 2221 $this->assertContains("The 'name' value must be set in other.", $e->getMessage()); 2222 } 2223 2224 } 2225 2226 /** 2227 * Tests for event related to course module delete. 2228 */ 2229 public function test_course_module_deleted_event() { 2230 global $USER, $DB; 2231 $this->resetAfterTest(); 2232 2233 // Create and delete a module. 2234 $sink = $this->redirectEvents(); 2235 $modinfo = $this->create_specific_module_test('forum'); 2236 $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST); 2237 course_delete_module($modinfo->coursemodule); 2238 $events = $sink->get_events(); 2239 $event = array_pop($events); // delete module event.; 2240 $sink->close(); 2241 2242 // Validate event data. 2243 $this->assertInstanceOf('\core\event\course_module_deleted', $event); 2244 $this->assertEquals($cm->id, $event->objectid); 2245 $this->assertEquals($USER->id, $event->userid); 2246 $this->assertEquals('course_modules', $event->objecttable); 2247 $this->assertEquals(null, $event->get_url()); 2248 $this->assertEquals($cm, $event->get_record_snapshot('course_modules', $cm->id)); 2249 2250 // Test legacy data. 2251 $this->assertSame('mod_deleted', $event->get_legacy_eventname()); 2252 $eventdata = new stdClass(); 2253 $eventdata->modulename = 'forum'; 2254 $eventdata->cmid = $cm->id; 2255 $eventdata->courseid = $cm->course; 2256 $eventdata->userid = $USER->id; 2257 $this->assertEventLegacyData($eventdata, $event); 2258 2259 $arr = array($cm->course, 'course', "delete mod", "view.php?id=$cm->course", "forum $cm->instance", $cm->id); 2260 $this->assertEventLegacyLogData($arr, $event); 2261 2262 } 2263 2264 /** 2265 * Tests for event validations related to course module deletion. 2266 */ 2267 public function test_course_module_deleted_event_exceptions() { 2268 2269 $this->resetAfterTest(); 2270 2271 // Generate data. 2272 $modinfo = $this->create_specific_module_test('assign'); 2273 $context = context_module::instance($modinfo->coursemodule); 2274 2275 // Test not setting instanceid. 2276 try { 2277 $event = \core\event\course_module_deleted::create(array( 2278 'courseid' => $modinfo->course, 2279 'context' => $context, 2280 'objectid' => $modinfo->coursemodule, 2281 'other' => array( 2282 'modulename' => 'assign', 2283 'name' => 'My assignment', 2284 ) 2285 )); 2286 $this->fail("Event validation should not allow \\core\\event\\course_module_deleted to be triggered without 2287 other['instanceid']"); 2288 } catch (coding_exception $e) { 2289 $this->assertContains("The 'instanceid' value must be set in other.", $e->getMessage()); 2290 } 2291 2292 // Test not setting modulename. 2293 try { 2294 $event = \core\event\course_module_deleted::create(array( 2295 'courseid' => $modinfo->course, 2296 'context' => $context, 2297 'objectid' => $modinfo->coursemodule, 2298 'other' => array( 2299 'instanceid' => $modinfo->instance, 2300 'name' => 'My assignment', 2301 ) 2302 )); 2303 $this->fail("Event validation should not allow \\core\\event\\course_module_deleted to be triggered without 2304 other['modulename']"); 2305 } catch (coding_exception $e) { 2306 $this->assertContains("The 'modulename' value must be set in other.", $e->getMessage()); 2307 } 2308 } 2309 2310 /** 2311 * Returns a user object and its assigned new role. 2312 * 2313 * @param testing_data_generator $generator 2314 * @param $contextid 2315 * @return array The user object and the role ID 2316 */ 2317 protected function get_user_objects(testing_data_generator $generator, $contextid) { 2318 global $USER; 2319 2320 if (empty($USER->id)) { 2321 $user = $generator->create_user(); 2322 $this->setUser($user); 2323 } 2324 $roleid = create_role('Test role', 'testrole', 'Test role description'); 2325 if (!is_array($contextid)) { 2326 $contextid = array($contextid); 2327 } 2328 foreach ($contextid as $cid) { 2329 $assignid = role_assign($roleid, $user->id, $cid); 2330 } 2331 return array($user, $roleid); 2332 } 2333 2334 /** 2335 * Test course move after course. 2336 */ 2337 public function test_course_change_sortorder_after_course() { 2338 global $DB; 2339 2340 $this->resetAfterTest(true); 2341 2342 $generator = $this->getDataGenerator(); 2343 $category = $generator->create_category(); 2344 $course3 = $generator->create_course(array('category' => $category->id)); 2345 $course2 = $generator->create_course(array('category' => $category->id)); 2346 $course1 = $generator->create_course(array('category' => $category->id)); 2347 $context = $category->get_context(); 2348 2349 list($user, $roleid) = $this->get_user_objects($generator, $context->id); 2350 $caps = course_capability_assignment::allow('moodle/category:manage', $roleid, $context->id); 2351 2352 $courses = $category->get_courses(); 2353 $this->assertInternalType('array', $courses); 2354 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2355 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2356 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2357 2358 // Test moving down. 2359 $this->assertTrue(course_change_sortorder_after_course($course1->id, $course3->id)); 2360 $courses = $category->get_courses(); 2361 $this->assertInternalType('array', $courses); 2362 $this->assertEquals(array($course2->id, $course3->id, $course1->id), array_keys($courses)); 2363 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2364 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2365 2366 // Test moving up. 2367 $this->assertTrue(course_change_sortorder_after_course($course1->id, $course2->id)); 2368 $courses = $category->get_courses(); 2369 $this->assertInternalType('array', $courses); 2370 $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses)); 2371 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2372 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2373 2374 // Test moving to the top. 2375 $this->assertTrue(course_change_sortorder_after_course($course1->id, 0)); 2376 $courses = $category->get_courses(); 2377 $this->assertInternalType('array', $courses); 2378 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2379 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2380 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2381 } 2382 2383 /** 2384 * Tests changing the visibility of a course. 2385 */ 2386 public function test_course_change_visibility() { 2387 global $DB; 2388 2389 $this->resetAfterTest(true); 2390 2391 $generator = $this->getDataGenerator(); 2392 $category = $generator->create_category(); 2393 $course = $generator->create_course(array('category' => $category->id)); 2394 2395 $this->assertEquals('1', $course->visible); 2396 $this->assertEquals('1', $course->visibleold); 2397 2398 $this->assertTrue(course_change_visibility($course->id, false)); 2399 $course = $DB->get_record('course', array('id' => $course->id)); 2400 $this->assertEquals('0', $course->visible); 2401 $this->assertEquals('0', $course->visibleold); 2402 2403 $this->assertTrue(course_change_visibility($course->id, true)); 2404 $course = $DB->get_record('course', array('id' => $course->id)); 2405 $this->assertEquals('1', $course->visible); 2406 $this->assertEquals('1', $course->visibleold); 2407 } 2408 2409 /** 2410 * Tests moving the course up and down by one. 2411 */ 2412 public function test_course_change_sortorder_by_one() { 2413 global $DB; 2414 2415 $this->resetAfterTest(true); 2416 2417 $generator = $this->getDataGenerator(); 2418 $category = $generator->create_category(); 2419 $course3 = $generator->create_course(array('category' => $category->id)); 2420 $course2 = $generator->create_course(array('category' => $category->id)); 2421 $course1 = $generator->create_course(array('category' => $category->id)); 2422 2423 $courses = $category->get_courses(); 2424 $this->assertInternalType('array', $courses); 2425 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2426 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2427 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2428 2429 // Test moving down. 2430 $course1 = get_course($course1->id); 2431 $this->assertTrue(course_change_sortorder_by_one($course1, false)); 2432 $courses = $category->get_courses(); 2433 $this->assertInternalType('array', $courses); 2434 $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses)); 2435 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2436 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2437 2438 // Test moving up. 2439 $course1 = get_course($course1->id); 2440 $this->assertTrue(course_change_sortorder_by_one($course1, true)); 2441 $courses = $category->get_courses(); 2442 $this->assertInternalType('array', $courses); 2443 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2444 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2445 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2446 2447 // Test moving the top course up one. 2448 $course1 = get_course($course1->id); 2449 $this->assertFalse(course_change_sortorder_by_one($course1, true)); 2450 // Check nothing changed. 2451 $courses = $category->get_courses(); 2452 $this->assertInternalType('array', $courses); 2453 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2454 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2455 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2456 2457 // Test moving the bottom course up down. 2458 $course3 = get_course($course3->id); 2459 $this->assertFalse(course_change_sortorder_by_one($course3, false)); 2460 // Check nothing changed. 2461 $courses = $category->get_courses(); 2462 $this->assertInternalType('array', $courses); 2463 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2464 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2465 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2466 } 2467 2468 public function test_view_resources_list() { 2469 $this->resetAfterTest(); 2470 2471 $course = self::getDataGenerator()->create_course(); 2472 $coursecontext = context_course::instance($course->id); 2473 2474 $event = \core\event\course_resources_list_viewed::create(array('context' => context_course::instance($course->id))); 2475 $event->set_legacy_logdata(array('book', 'page', 'resource')); 2476 $sink = $this->redirectEvents(); 2477 $event->trigger(); 2478 $events = $sink->get_events(); 2479 $sink->close(); 2480 2481 // Validate the event. 2482 $event = $events[0]; 2483 $this->assertInstanceOf('\core\event\course_resources_list_viewed', $event); 2484 $this->assertEquals(null, $event->objecttable); 2485 $this->assertEquals(null, $event->objectid); 2486 $this->assertEquals($course->id, $event->courseid); 2487 $this->assertEquals($coursecontext->id, $event->contextid); 2488 $expectedlegacydata = array( 2489 array($course->id, "book", "view all", 'index.php?id=' . $course->id, ''), 2490 array($course->id, "page", "view all", 'index.php?id=' . $course->id, ''), 2491 array($course->id, "resource", "view all", 'index.php?id=' . $course->id, ''), 2492 ); 2493 $this->assertEventLegacyLogData($expectedlegacydata, $event); 2494 $this->assertEventContextNotUsed($event); 2495 } 2496 2497 /** 2498 * Test duplicate_module() 2499 */ 2500 public function test_duplicate_module() { 2501 $this->setAdminUser(); 2502 $this->resetAfterTest(); 2503 $course = self::getDataGenerator()->create_course(); 2504 $res = self::getDataGenerator()->create_module('resource', array('course' => $course)); 2505 $cm = get_coursemodule_from_id('resource', $res->cmid, 0, false, MUST_EXIST); 2506 2507 $newcm = duplicate_module($course, $cm); 2508 2509 // Make sure they are the same, except obvious id changes. 2510 foreach ($cm as $prop => $value) { 2511 if ($prop == 'id' || $prop == 'url' || $prop == 'instance' || $prop == 'added') { 2512 // Ignore obviously different properties. 2513 continue; 2514 } 2515 $this->assertEquals($value, $newcm->$prop); 2516 } 2517 } 2518 2519 /** 2520 * Tests that when creating or updating a module, if the availability settings 2521 * are present but set to an empty tree, availability is set to null in 2522 * database. 2523 */ 2524 public function test_empty_availability_settings() { 2525 global $DB; 2526 $this->setAdminUser(); 2527 $this->resetAfterTest(); 2528 2529 // Enable availability. 2530 set_config('enableavailability', 1); 2531 2532 // Test add. 2533 $emptyavailability = json_encode(\core_availability\tree::get_root_json(array())); 2534 $course = self::getDataGenerator()->create_course(); 2535 $label = self::getDataGenerator()->create_module('label', array( 2536 'course' => $course, 'availability' => $emptyavailability)); 2537 $this->assertNull($DB->get_field('course_modules', 'availability', 2538 array('id' => $label->cmid))); 2539 2540 // Test update. 2541 $formdata = $DB->get_record('course_modules', array('id' => $label->cmid)); 2542 unset($formdata->availability); 2543 $formdata->availabilityconditionsjson = $emptyavailability; 2544 $formdata->modulename = 'label'; 2545 $formdata->coursemodule = $label->cmid; 2546 $draftid = 0; 2547 file_prepare_draft_area($draftid, context_module::instance($label->cmid)->id, 2548 'mod_label', 'intro', 0); 2549 $formdata->introeditor = array( 2550 'itemid' => $draftid, 2551 'text' => '<p>Yo</p>', 2552 'format' => FORMAT_HTML); 2553 update_module($formdata); 2554 $this->assertNull($DB->get_field('course_modules', 'availability', 2555 array('id' => $label->cmid))); 2556 } 2557 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |