Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core; 18 19 use advanced_testcase; 20 use cache; 21 use cm_info; 22 use coding_exception; 23 use context_course; 24 use context_module; 25 use course_modinfo; 26 use moodle_exception; 27 use moodle_url; 28 use Exception; 29 30 /** 31 * Unit tests for lib/modinfolib.php. 32 * 33 * @package core 34 * @category phpunit 35 * @copyright 2012 Andrew Davis 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class modinfolib_test extends advanced_testcase { 39 public function test_section_info_properties() { 40 global $DB, $CFG; 41 42 $this->resetAfterTest(); 43 $oldcfgenableavailability = $CFG->enableavailability; 44 $oldcfgenablecompletion = $CFG->enablecompletion; 45 set_config('enableavailability', 1); 46 set_config('enablecompletion', 1); 47 $this->setAdminUser(); 48 49 // Generate the course and pre-requisite module. 50 $course = $this->getDataGenerator()->create_course( 51 array('format' => 'topics', 52 'numsections' => 3, 53 'enablecompletion' => 1, 54 'groupmode' => SEPARATEGROUPS, 55 'forcegroupmode' => 0), 56 array('createsections' => true)); 57 $coursecontext = context_course::instance($course->id); 58 $prereqforum = $this->getDataGenerator()->create_module('forum', 59 array('course' => $course->id), 60 array('completion' => 1)); 61 62 // Add availability conditions. 63 $availability = '{"op":"&","showc":[true,true,true],"c":[' . 64 '{"type":"completion","cm":' . $prereqforum->cmid . ',"e":"' . 65 COMPLETION_COMPLETE . '"},' . 66 '{"type":"grade","id":666,"min":0.4},' . 67 '{"type":"profile","op":"contains","sf":"email","v":"test"}' . 68 ']}'; 69 $DB->set_field('course_sections', 'availability', $availability, 70 array('course' => $course->id, 'section' => 2)); 71 rebuild_course_cache($course->id, true); 72 $sectiondb = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 2)); 73 74 // Create and enrol a student. 75 $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 76 $student = $this->getDataGenerator()->create_user(); 77 role_assign($studentrole->id, $student->id, $coursecontext); 78 $enrolplugin = enrol_get_plugin('manual'); 79 $enrolinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual')); 80 $enrolplugin->enrol_user($enrolinstance, $student->id); 81 $this->setUser($student); 82 83 // Get modinfo. 84 $modinfo = get_fast_modinfo($course->id); 85 $si = $modinfo->get_section_info(2); 86 87 $this->assertEquals($sectiondb->id, $si->id); 88 $this->assertEquals($sectiondb->course, $si->course); 89 $this->assertEquals($sectiondb->section, $si->section); 90 $this->assertEquals($sectiondb->name, $si->name); 91 $this->assertEquals($sectiondb->visible, $si->visible); 92 $this->assertEquals($sectiondb->summary, $si->summary); 93 $this->assertEquals($sectiondb->summaryformat, $si->summaryformat); 94 $this->assertEquals($sectiondb->sequence, $si->sequence); // Since this section does not contain invalid modules. 95 $this->assertEquals($availability, $si->availability); 96 97 // Dynamic fields, just test that they can be retrieved (must be carefully tested in each activity type). 98 $this->assertEquals(0, $si->available); 99 $this->assertNotEmpty($si->availableinfo); // Lists all unmet availability conditions. 100 $this->assertEquals(0, $si->uservisible); 101 102 // Restore settings. 103 set_config('enableavailability', $oldcfgenableavailability); 104 set_config('enablecompletion', $oldcfgenablecompletion); 105 } 106 107 public function test_cm_info_properties() { 108 global $DB, $CFG; 109 110 $this->resetAfterTest(); 111 $oldcfgenableavailability = $CFG->enableavailability; 112 $oldcfgenablecompletion = $CFG->enablecompletion; 113 set_config('enableavailability', 1); 114 set_config('enablecompletion', 1); 115 $this->setAdminUser(); 116 117 // Generate the course and pre-requisite module. 118 $course = $this->getDataGenerator()->create_course( 119 array('format' => 'topics', 120 'numsections' => 3, 121 'enablecompletion' => 1, 122 'groupmode' => SEPARATEGROUPS, 123 'forcegroupmode' => 0), 124 array('createsections' => true)); 125 $coursecontext = context_course::instance($course->id); 126 $prereqforum = $this->getDataGenerator()->create_module('forum', 127 array('course' => $course->id), 128 array('completion' => 1)); 129 130 // Generate module and add availability conditions. 131 $availability = '{"op":"&","showc":[true,true,true],"c":[' . 132 '{"type":"completion","cm":' . $prereqforum->cmid . ',"e":"' . 133 COMPLETION_COMPLETE . '"},' . 134 '{"type":"grade","id":666,"min":0.4},' . 135 '{"type":"profile","op":"contains","sf":"email","v":"test"}' . 136 ']}'; 137 $assign = $this->getDataGenerator()->create_module('assign', 138 array('course' => $course->id), 139 array('idnumber' => 123, 140 'groupmode' => VISIBLEGROUPS, 141 'availability' => $availability)); 142 rebuild_course_cache($course->id, true); 143 144 // Retrieve all related records from DB. 145 $assigndb = $DB->get_record('assign', array('id' => $assign->id)); 146 $moduletypedb = $DB->get_record('modules', array('name' => 'assign')); 147 $moduledb = $DB->get_record('course_modules', array('module' => $moduletypedb->id, 'instance' => $assign->id)); 148 $sectiondb = $DB->get_record('course_sections', array('id' => $moduledb->section)); 149 $modnamessingular = get_module_types_names(false); 150 $modnamesplural = get_module_types_names(true); 151 152 // Create and enrol a student. 153 $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 154 $student = $this->getDataGenerator()->create_user(); 155 role_assign($studentrole->id, $student->id, $coursecontext); 156 $enrolplugin = enrol_get_plugin('manual'); 157 $enrolinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual')); 158 $enrolplugin->enrol_user($enrolinstance, $student->id); 159 $this->setUser($student); 160 161 // Emulate data used in building course cache to receive the same instance of cached_cm_info as was used in building modinfo. 162 $rawmods = get_course_mods($course->id); 163 $cachedcminfo = assign_get_coursemodule_info($rawmods[$moduledb->id]); 164 165 // Get modinfo. 166 $modinfo = get_fast_modinfo($course->id); 167 $cm = $modinfo->instances['assign'][$assign->id]; 168 169 $this->assertEquals($moduledb->id, $cm->id); 170 $this->assertEquals($assigndb->id, $cm->instance); 171 $this->assertEquals($moduledb->course, $cm->course); 172 $this->assertEquals($moduledb->idnumber, $cm->idnumber); 173 $this->assertEquals($moduledb->added, $cm->added); 174 $this->assertEquals($moduledb->visible, $cm->visible); 175 $this->assertEquals($moduledb->visibleold, $cm->visibleold); 176 $this->assertEquals($moduledb->groupmode, $cm->groupmode); 177 $this->assertEquals(VISIBLEGROUPS, $cm->groupmode); 178 $this->assertEquals($moduledb->groupingid, $cm->groupingid); 179 $this->assertEquals($course->groupmodeforce, $cm->coursegroupmodeforce); 180 $this->assertEquals($course->groupmode, $cm->coursegroupmode); 181 $this->assertEquals(SEPARATEGROUPS, $cm->coursegroupmode); 182 $this->assertEquals($course->groupmodeforce ? $course->groupmode : $moduledb->groupmode, 183 $cm->effectivegroupmode); // (since mod_assign supports groups). 184 $this->assertEquals(VISIBLEGROUPS, $cm->effectivegroupmode); 185 $this->assertEquals($moduledb->indent, $cm->indent); 186 $this->assertEquals($moduledb->completion, $cm->completion); 187 $this->assertEquals($moduledb->completiongradeitemnumber, $cm->completiongradeitemnumber); 188 $this->assertEquals($moduledb->completionpassgrade, $cm->completionpassgrade); 189 $this->assertEquals($moduledb->completionview, $cm->completionview); 190 $this->assertEquals($moduledb->completionexpected, $cm->completionexpected); 191 $this->assertEquals($moduledb->showdescription, $cm->showdescription); 192 $this->assertEquals(null, $cm->extra); // Deprecated field. Used in module types that don't return cached_cm_info. 193 $this->assertEquals($cachedcminfo->icon, $cm->icon); 194 $this->assertEquals($cachedcminfo->iconcomponent, $cm->iconcomponent); 195 $this->assertEquals('assign', $cm->modname); 196 $this->assertEquals($moduledb->module, $cm->module); 197 $this->assertEquals($cachedcminfo->name, $cm->name); 198 $this->assertEquals($sectiondb->section, $cm->sectionnum); 199 $this->assertEquals($moduledb->section, $cm->section); 200 $this->assertEquals($availability, $cm->availability); 201 $this->assertEquals(context_module::instance($moduledb->id), $cm->context); 202 $this->assertEquals($modnamessingular['assign'], $cm->modfullname); 203 $this->assertEquals($modnamesplural['assign'], $cm->modplural); 204 $this->assertEquals(new moodle_url('/mod/assign/view.php', array('id' => $moduledb->id)), $cm->url); 205 $this->assertEquals($cachedcminfo->customdata, $cm->customdata); 206 207 // Dynamic fields, just test that they can be retrieved (must be carefully tested in each activity type). 208 $this->assertNotEmpty($cm->availableinfo); // Lists all unmet availability conditions. 209 $this->assertEquals(0, $cm->uservisible); 210 $this->assertEquals('', $cm->extraclasses); 211 $this->assertEquals('', $cm->onclick); 212 $this->assertEquals(null, $cm->afterlink); 213 $this->assertEquals(null, $cm->afterediticons); 214 $this->assertEquals('', $cm->content); 215 216 // Attempt to access and set non-existing field. 217 $this->assertTrue(empty($modinfo->somefield)); 218 $this->assertFalse(isset($modinfo->somefield)); 219 $cm->somefield; 220 $this->assertDebuggingCalled(); 221 $cm->somefield = 'Some value'; 222 $this->assertDebuggingCalled(); 223 $this->assertEmpty($cm->somefield); 224 $this->assertDebuggingCalled(); 225 226 // Attempt to overwrite an existing field. 227 $prevvalue = $cm->name; 228 $this->assertNotEmpty($cm->name); 229 $this->assertFalse(empty($cm->name)); 230 $this->assertTrue(isset($cm->name)); 231 $cm->name = 'Illegal overwriting'; 232 $this->assertDebuggingCalled(); 233 $this->assertEquals($prevvalue, $cm->name); 234 $this->assertDebuggingNotCalled(); 235 236 // Restore settings. 237 set_config('enableavailability', $oldcfgenableavailability); 238 set_config('enablecompletion', $oldcfgenablecompletion); 239 } 240 241 public function test_matching_cacherev() { 242 global $DB, $CFG; 243 244 $this->resetAfterTest(); 245 $this->setAdminUser(); 246 $cache = cache::make('core', 'coursemodinfo'); 247 248 // Generate the course and pre-requisite module. 249 $course = $this->getDataGenerator()->create_course( 250 array('format' => 'topics', 251 'numsections' => 3), 252 array('createsections' => true)); 253 254 // Make sure the cacherev is set. 255 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); 256 $this->assertGreaterThan(0, $cacherev); 257 $prevcacherev = $cacherev; 258 259 // Reset course cache and make sure cacherev is bumped up but cache is empty. 260 rebuild_course_cache($course->id, true); 261 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); 262 $this->assertGreaterThan($prevcacherev, $cacherev); 263 $this->assertEmpty($cache->get_versioned($course->id, $prevcacherev)); 264 $prevcacherev = $cacherev; 265 266 // Build course cache. Cacherev should not change but cache is now not empty. Make sure cacherev is the same everywhere. 267 $modinfo = get_fast_modinfo($course->id); 268 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); 269 $this->assertEquals($prevcacherev, $cacherev); 270 $cachedvalue = $cache->get_versioned($course->id, $cacherev); 271 $this->assertNotEmpty($cachedvalue); 272 $this->assertEquals($cacherev, $cachedvalue->cacherev); 273 $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); 274 $prevcacherev = $cacherev; 275 276 // Little trick to check that cache is not rebuilt druing the next step - substitute the value in MUC and later check that it is still there. 277 $cache->set_versioned($course->id, $cacherev, (object)array_merge((array)$cachedvalue, array('secretfield' => 1))); 278 279 // Clear static cache and call get_fast_modinfo() again (pretend we are in another request). Cache should not be rebuilt. 280 course_modinfo::clear_instance_cache(); 281 $modinfo = get_fast_modinfo($course->id); 282 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); 283 $this->assertEquals($prevcacherev, $cacherev); 284 $cachedvalue = $cache->get_versioned($course->id, $cacherev); 285 $this->assertNotEmpty($cachedvalue); 286 $this->assertEquals($cacherev, $cachedvalue->cacherev); 287 $this->assertNotEmpty($cachedvalue->secretfield); 288 $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); 289 $prevcacherev = $cacherev; 290 291 // Rebuild course cache. Cacherev must be incremented everywhere. 292 rebuild_course_cache($course->id); 293 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); 294 $this->assertGreaterThan($prevcacherev, $cacherev); 295 $cachedvalue = $cache->get_versioned($course->id, $cacherev); 296 $this->assertNotEmpty($cachedvalue); 297 $this->assertEquals($cacherev, $cachedvalue->cacherev); 298 $modinfo = get_fast_modinfo($course->id); 299 $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); 300 $prevcacherev = $cacherev; 301 302 // Update cacherev in DB and make sure the cache will be rebuilt on the next call to get_fast_modinfo(). 303 increment_revision_number('course', 'cacherev', 'id = ?', array($course->id)); 304 // We need to clear static cache for course_modinfo instances too. 305 course_modinfo::clear_instance_cache(); 306 $modinfo = get_fast_modinfo($course->id); 307 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); 308 $this->assertGreaterThan($prevcacherev, $cacherev); 309 $cachedvalue = $cache->get_versioned($course->id, $cacherev); 310 $this->assertNotEmpty($cachedvalue); 311 $this->assertEquals($cacherev, $cachedvalue->cacherev); 312 $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); 313 $prevcacherev = $cacherev; 314 315 // Reset cache for all courses and make sure this course cache is reset. 316 rebuild_course_cache(0, true); 317 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); 318 $this->assertGreaterThan($prevcacherev, $cacherev); 319 $this->assertEmpty($cache->get_versioned($course->id, $cacherev)); 320 // Rebuild again. 321 $modinfo = get_fast_modinfo($course->id); 322 $cachedvalue = $cache->get_versioned($course->id, $cacherev); 323 $this->assertNotEmpty($cachedvalue); 324 $this->assertEquals($cacherev, $cachedvalue->cacherev); 325 $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); 326 $prevcacherev = $cacherev; 327 328 // Purge all caches and make sure cacherev is increased and data from MUC erased. 329 purge_all_caches(); 330 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); 331 $this->assertGreaterThan($prevcacherev, $cacherev); 332 $this->assertEmpty($cache->get($course->id)); 333 } 334 335 public function test_course_modinfo_properties() { 336 global $USER, $DB; 337 338 $this->resetAfterTest(); 339 $this->setAdminUser(); 340 341 // Generate the course and some modules. Make one section hidden. 342 $course = $this->getDataGenerator()->create_course( 343 array('format' => 'topics', 344 'numsections' => 3), 345 array('createsections' => true)); 346 $DB->execute('UPDATE {course_sections} SET visible = 0 WHERE course = ? and section = ?', 347 array($course->id, 3)); 348 $coursecontext = context_course::instance($course->id); 349 $forum0 = $this->getDataGenerator()->create_module('forum', 350 array('course' => $course->id), array('section' => 0)); 351 $assign0 = $this->getDataGenerator()->create_module('assign', 352 array('course' => $course->id), array('section' => 0, 'visible' => 0)); 353 $forum1 = $this->getDataGenerator()->create_module('forum', 354 array('course' => $course->id), array('section' => 1)); 355 $assign1 = $this->getDataGenerator()->create_module('assign', 356 array('course' => $course->id), array('section' => 1)); 357 $page1 = $this->getDataGenerator()->create_module('page', 358 array('course' => $course->id), array('section' => 1)); 359 $page3 = $this->getDataGenerator()->create_module('page', 360 array('course' => $course->id), array('section' => 3)); 361 362 $modinfo = get_fast_modinfo($course->id); 363 364 $this->assertEquals(array($forum0->cmid, $assign0->cmid, $forum1->cmid, $assign1->cmid, $page1->cmid, $page3->cmid), 365 array_keys($modinfo->cms)); 366 $this->assertEquals($course->id, $modinfo->courseid); 367 $this->assertEquals($USER->id, $modinfo->userid); 368 $this->assertEquals(array(0 => array($forum0->cmid, $assign0->cmid), 369 1 => array($forum1->cmid, $assign1->cmid, $page1->cmid), 3 => array($page3->cmid)), $modinfo->sections); 370 $this->assertEquals(array('forum', 'assign', 'page'), array_keys($modinfo->instances)); 371 $this->assertEquals(array($assign0->id, $assign1->id), array_keys($modinfo->instances['assign'])); 372 $this->assertEquals(array($forum0->id, $forum1->id), array_keys($modinfo->instances['forum'])); 373 $this->assertEquals(array($page1->id, $page3->id), array_keys($modinfo->instances['page'])); 374 $this->assertEquals(groups_get_user_groups($course->id), $modinfo->groups); 375 $this->assertEquals(array(0 => array($forum0->cmid, $assign0->cmid), 376 1 => array($forum1->cmid, $assign1->cmid, $page1->cmid), 377 3 => array($page3->cmid)), $modinfo->get_sections()); 378 $this->assertEquals(array(0, 1, 2, 3), array_keys($modinfo->get_section_info_all())); 379 $this->assertEquals($forum0->cmid . ',' . $assign0->cmid, $modinfo->get_section_info(0)->sequence); 380 $this->assertEquals($forum1->cmid . ',' . $assign1->cmid . ',' . $page1->cmid, $modinfo->get_section_info(1)->sequence); 381 $this->assertEquals('', $modinfo->get_section_info(2)->sequence); 382 $this->assertEquals($page3->cmid, $modinfo->get_section_info(3)->sequence); 383 $this->assertEquals($course->id, $modinfo->get_course()->id); 384 $names = array_keys($modinfo->get_used_module_names()); 385 sort($names); 386 $this->assertEquals(array('assign', 'forum', 'page'), $names); 387 $names = array_keys($modinfo->get_used_module_names(true)); 388 sort($names); 389 $this->assertEquals(array('assign', 'forum', 'page'), $names); 390 // Admin can see hidden modules/sections. 391 $this->assertTrue($modinfo->cms[$assign0->cmid]->uservisible); 392 $this->assertTrue($modinfo->get_section_info(3)->uservisible); 393 394 // Get modinfo for non-current user (without capability to view hidden activities/sections). 395 $user = $this->getDataGenerator()->create_user(); 396 $modinfo = get_fast_modinfo($course->id, $user->id); 397 $this->assertEquals($user->id, $modinfo->userid); 398 $this->assertFalse($modinfo->cms[$assign0->cmid]->uservisible); 399 $this->assertFalse($modinfo->get_section_info(3)->uservisible); 400 401 // Attempt to access and set non-existing field. 402 $this->assertTrue(empty($modinfo->somefield)); 403 $this->assertFalse(isset($modinfo->somefield)); 404 $modinfo->somefield; 405 $this->assertDebuggingCalled(); 406 $modinfo->somefield = 'Some value'; 407 $this->assertDebuggingCalled(); 408 $this->assertEmpty($modinfo->somefield); 409 $this->assertDebuggingCalled(); 410 411 // Attempt to overwrite existing field. 412 $this->assertFalse(empty($modinfo->cms)); 413 $this->assertTrue(isset($modinfo->cms)); 414 $modinfo->cms = 'Illegal overwriting'; 415 $this->assertDebuggingCalled(); 416 $this->assertNotEquals('Illegal overwriting', $modinfo->cms); 417 } 418 419 public function test_is_user_access_restricted_by_capability() { 420 global $DB; 421 422 $this->resetAfterTest(); 423 424 // Create a course and a mod_assign instance. 425 $course = $this->getDataGenerator()->create_course(); 426 $assign = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id)); 427 428 // Create and enrol a student. 429 $coursecontext = context_course::instance($course->id); 430 $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 431 $student = $this->getDataGenerator()->create_user(); 432 role_assign($studentrole->id, $student->id, $coursecontext); 433 $enrolplugin = enrol_get_plugin('manual'); 434 $enrolinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual')); 435 $enrolplugin->enrol_user($enrolinstance, $student->id); 436 $this->setUser($student); 437 438 // Make sure student can see the module. 439 $cm = get_fast_modinfo($course->id)->instances['assign'][$assign->id]; 440 $this->assertTrue($cm->uservisible); 441 $this->assertFalse($cm->is_user_access_restricted_by_capability()); 442 443 // Prohibit student to view mod_assign for the course. 444 role_change_permission($studentrole->id, $coursecontext, 'mod/assign:view', CAP_PROHIBIT); 445 get_fast_modinfo($course->id, 0, true); 446 $cm = get_fast_modinfo($course->id)->instances['assign'][$assign->id]; 447 $this->assertFalse($cm->uservisible); 448 $this->assertTrue($cm->is_user_access_restricted_by_capability()); 449 450 // Restore permission to student to view mod_assign for the course. 451 role_change_permission($studentrole->id, $coursecontext, 'mod/assign:view', CAP_INHERIT); 452 get_fast_modinfo($course->id, 0, true); 453 $cm = get_fast_modinfo($course->id)->instances['assign'][$assign->id]; 454 $this->assertTrue($cm->uservisible); 455 $this->assertFalse($cm->is_user_access_restricted_by_capability()); 456 457 // Prohibit student to view mod_assign for the particular module. 458 role_change_permission($studentrole->id, context_module::instance($cm->id), 'mod/assign:view', CAP_PROHIBIT); 459 get_fast_modinfo($course->id, 0, true); 460 $cm = get_fast_modinfo($course->id)->instances['assign'][$assign->id]; 461 $this->assertFalse($cm->uservisible); 462 $this->assertTrue($cm->is_user_access_restricted_by_capability()); 463 464 // Check calling get_fast_modinfo() for different user: 465 $this->setAdminUser(); 466 $cm = get_fast_modinfo($course->id)->instances['assign'][$assign->id]; 467 $this->assertTrue($cm->uservisible); 468 $this->assertFalse($cm->is_user_access_restricted_by_capability()); 469 $cm = get_fast_modinfo($course->id, $student->id)->instances['assign'][$assign->id]; 470 $this->assertFalse($cm->uservisible); 471 $this->assertTrue($cm->is_user_access_restricted_by_capability()); 472 } 473 474 /** 475 * Tests for function cm_info::get_course_module_record() 476 */ 477 public function test_cm_info_get_course_module_record() { 478 global $DB; 479 480 $this->resetAfterTest(); 481 $this->setAdminUser(); 482 483 set_config('enableavailability', 1); 484 set_config('enablecompletion', 1); 485 486 $course = $this->getDataGenerator()->create_course( 487 array('format' => 'topics', 'numsections' => 3, 'enablecompletion' => 1), 488 array('createsections' => true)); 489 $mods = array(); 490 $mods[0] = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 491 $mods[1] = $this->getDataGenerator()->create_module('assign', 492 array('course' => $course->id, 493 'section' => 3, 494 'idnumber' => '12345', 495 'showdescription' => true 496 )); 497 // Pick a small valid availability value to use. 498 $availabilityvalue = '{"op":"|","show":true,"c":[{"type":"date","d":">=","t":4}]}'; 499 $mods[2] = $this->getDataGenerator()->create_module('book', 500 array('course' => $course->id, 501 'indent' => 5, 502 'availability' => $availabilityvalue, 503 'showdescription' => false, 504 'completion' => true, 505 'completionview' => true, 506 'completionexpected' => time() + 5000, 507 )); 508 $mods[3] = $this->getDataGenerator()->create_module('forum', 509 array('course' => $course->id, 510 'visible' => 0, 511 'groupmode' => 1, 512 'availability' => null)); 513 $mods[4] = $this->getDataGenerator()->create_module('forum', 514 array('course' => $course->id, 515 'grouping' => 12)); 516 517 $modinfo = get_fast_modinfo($course->id); 518 519 // Make sure that object returned by get_course_module_record(false) has exactly the same fields as DB table 'course_modules'. 520 $dbfields = array_keys($DB->get_columns('course_modules')); 521 sort($dbfields); 522 $cmrecord = $modinfo->get_cm($mods[0]->cmid)->get_course_module_record(); 523 $cmrecordfields = array_keys((array)$cmrecord); 524 sort($cmrecordfields); 525 $this->assertEquals($dbfields, $cmrecordfields); 526 527 // Make sure that object returned by get_course_module_record(true) has exactly the same fields 528 // as object returned by get_coursemodule_from_id(,,,true,); 529 $cmrecordfull = $modinfo->get_cm($mods[0]->cmid)->get_course_module_record(true); 530 $cmrecordfullfields = array_keys((array)$cmrecordfull); 531 $cm = get_coursemodule_from_id(null, $mods[0]->cmid, 0, true, MUST_EXIST); 532 $cmfields = array_keys((array)$cm); 533 $this->assertEquals($cmfields, $cmrecordfullfields); 534 535 // Make sure that object returned by get_course_module_record(true) has exactly the same fields 536 // as object returned by get_coursemodule_from_instance(,,,true,); 537 $cm = get_coursemodule_from_instance('forum', $mods[0]->id, null, true, MUST_EXIST); 538 $cmfields = array_keys((array)$cm); 539 $this->assertEquals($cmfields, $cmrecordfullfields); 540 541 // Make sure the objects have the same properties. 542 $cm1 = get_coursemodule_from_id(null, $mods[0]->cmid, 0, true, MUST_EXIST); 543 $cm2 = get_coursemodule_from_instance('forum', $mods[0]->id, 0, true, MUST_EXIST); 544 $cminfo = $modinfo->get_cm($mods[0]->cmid); 545 $record = $DB->get_record('course_modules', array('id' => $mods[0]->cmid)); 546 $this->assertEquals($record, $cminfo->get_course_module_record()); 547 $this->assertEquals($cm1, $cminfo->get_course_module_record(true)); 548 $this->assertEquals($cm2, $cminfo->get_course_module_record(true)); 549 550 $cm1 = get_coursemodule_from_id(null, $mods[1]->cmid, 0, true, MUST_EXIST); 551 $cm2 = get_coursemodule_from_instance('assign', $mods[1]->id, 0, true, MUST_EXIST); 552 $cminfo = $modinfo->get_cm($mods[1]->cmid); 553 $record = $DB->get_record('course_modules', array('id' => $mods[1]->cmid)); 554 $this->assertEquals($record, $cminfo->get_course_module_record()); 555 $this->assertEquals($cm1, $cminfo->get_course_module_record(true)); 556 $this->assertEquals($cm2, $cminfo->get_course_module_record(true)); 557 558 $cm1 = get_coursemodule_from_id(null, $mods[2]->cmid, 0, true, MUST_EXIST); 559 $cm2 = get_coursemodule_from_instance('book', $mods[2]->id, 0, true, MUST_EXIST); 560 $cminfo = $modinfo->get_cm($mods[2]->cmid); 561 $record = $DB->get_record('course_modules', array('id' => $mods[2]->cmid)); 562 $this->assertEquals($record, $cminfo->get_course_module_record()); 563 $this->assertEquals($cm1, $cminfo->get_course_module_record(true)); 564 $this->assertEquals($cm2, $cminfo->get_course_module_record(true)); 565 566 $cm1 = get_coursemodule_from_id(null, $mods[3]->cmid, 0, true, MUST_EXIST); 567 $cm2 = get_coursemodule_from_instance('forum', $mods[3]->id, 0, true, MUST_EXIST); 568 $cminfo = $modinfo->get_cm($mods[3]->cmid); 569 $record = $DB->get_record('course_modules', array('id' => $mods[3]->cmid)); 570 $this->assertEquals($record, $cminfo->get_course_module_record()); 571 $this->assertEquals($cm1, $cminfo->get_course_module_record(true)); 572 $this->assertEquals($cm2, $cminfo->get_course_module_record(true)); 573 574 $cm1 = get_coursemodule_from_id(null, $mods[4]->cmid, 0, true, MUST_EXIST); 575 $cm2 = get_coursemodule_from_instance('forum', $mods[4]->id, 0, true, MUST_EXIST); 576 $cminfo = $modinfo->get_cm($mods[4]->cmid); 577 $record = $DB->get_record('course_modules', array('id' => $mods[4]->cmid)); 578 $this->assertEquals($record, $cminfo->get_course_module_record()); 579 $this->assertEquals($cm1, $cminfo->get_course_module_record(true)); 580 $this->assertEquals($cm2, $cminfo->get_course_module_record(true)); 581 582 } 583 584 /** 585 * Tests the availability property that has been added to course modules 586 * and sections (just to see that it is correctly saved and accessed). 587 */ 588 public function test_availability_property() { 589 global $DB, $CFG; 590 591 $this->resetAfterTest(); 592 593 // Create a course with two modules and three sections. 594 $course = $this->getDataGenerator()->create_course( 595 array('format' => 'topics', 'numsections' => 3), 596 array('createsections' => true)); 597 $forum = $this->getDataGenerator()->create_module('forum', 598 array('course' => $course->id)); 599 $forum2 = $this->getDataGenerator()->create_module('forum', 600 array('course' => $course->id)); 601 602 // Get modinfo. Check that availability is null for both cm and sections. 603 $modinfo = get_fast_modinfo($course->id); 604 $cm = $modinfo->get_cm($forum->cmid); 605 $this->assertNull($cm->availability); 606 $section = $modinfo->get_section_info(1, MUST_EXIST); 607 $this->assertNull($section->availability); 608 609 // Update availability for cm and section in database. 610 $DB->set_field('course_modules', 'availability', '{}', array('id' => $cm->id)); 611 $DB->set_field('course_sections', 'availability', '{}', array('id' => $section->id)); 612 613 // Clear cache and get modinfo again. 614 rebuild_course_cache($course->id, true); 615 get_fast_modinfo(0, 0, true); 616 $modinfo = get_fast_modinfo($course->id); 617 618 // Check values that were changed. 619 $cm = $modinfo->get_cm($forum->cmid); 620 $this->assertEquals('{}', $cm->availability); 621 $section = $modinfo->get_section_info(1, MUST_EXIST); 622 $this->assertEquals('{}', $section->availability); 623 624 // Check other values are still null. 625 $cm = $modinfo->get_cm($forum2->cmid); 626 $this->assertNull($cm->availability); 627 $section = $modinfo->get_section_info(2, MUST_EXIST); 628 $this->assertNull($section->availability); 629 } 630 631 /** 632 * Tests for get_groups() method. 633 */ 634 public function test_get_groups() { 635 $this->resetAfterTest(); 636 $generator = $this->getDataGenerator(); 637 638 // Create courses. 639 $course1 = $generator->create_course(); 640 $course2 = $generator->create_course(); 641 $course3 = $generator->create_course(); 642 643 // Create users. 644 $user1 = $generator->create_user(); 645 $user2 = $generator->create_user(); 646 $user3 = $generator->create_user(); 647 648 // Enrol users on courses. 649 $generator->enrol_user($user1->id, $course1->id); 650 $generator->enrol_user($user2->id, $course2->id); 651 $generator->enrol_user($user3->id, $course2->id); 652 $generator->enrol_user($user3->id, $course3->id); 653 654 // Create groups. 655 $group1 = $generator->create_group(array('courseid' => $course1->id)); 656 $group2 = $generator->create_group(array('courseid' => $course2->id)); 657 $group3 = $generator->create_group(array('courseid' => $course2->id)); 658 659 // Assign users to groups and assert the result. 660 $this->assertTrue($generator->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id))); 661 $this->assertTrue($generator->create_group_member(array('groupid' => $group2->id, 'userid' => $user2->id))); 662 $this->assertTrue($generator->create_group_member(array('groupid' => $group3->id, 'userid' => $user2->id))); 663 $this->assertTrue($generator->create_group_member(array('groupid' => $group2->id, 'userid' => $user3->id))); 664 665 // Create groupings. 666 $grouping1 = $generator->create_grouping(array('courseid' => $course1->id)); 667 $grouping2 = $generator->create_grouping(array('courseid' => $course2->id)); 668 669 // Assign and assert group to groupings. 670 groups_assign_grouping($grouping1->id, $group1->id); 671 groups_assign_grouping($grouping2->id, $group2->id); 672 groups_assign_grouping($grouping2->id, $group3->id); 673 674 // Test with one single group. 675 $modinfo = get_fast_modinfo($course1, $user1->id); 676 $groups = $modinfo->get_groups($grouping1->id); 677 $this->assertCount(1, $groups); 678 $this->assertArrayHasKey($group1->id, $groups); 679 680 // Test with two groups. 681 $modinfo = get_fast_modinfo($course2, $user2->id); 682 $groups = $modinfo->get_groups(); 683 $this->assertCount(2, $groups); 684 $this->assertTrue(in_array($group2->id, $groups)); 685 $this->assertTrue(in_array($group3->id, $groups)); 686 687 // Test with no groups. 688 $modinfo = get_fast_modinfo($course3, $user3->id); 689 $groups = $modinfo->get_groups(); 690 $this->assertCount(0, $groups); 691 $this->assertArrayNotHasKey($group1->id, $groups); 692 } 693 694 /** 695 * Tests the function for constructing a cm_info from mixed data. 696 */ 697 public function test_create() { 698 global $CFG, $DB; 699 $this->resetAfterTest(); 700 701 // Create a course and an activity. 702 $generator = $this->getDataGenerator(); 703 $course = $generator->create_course(); 704 $page = $generator->create_module('page', array('course' => $course->id, 705 'name' => 'Annie')); 706 707 // Null is passed through. 708 $this->assertNull(cm_info::create(null)); 709 710 // Stdclass object turns into cm_info. 711 $cm = cm_info::create( 712 (object)array('id' => $page->cmid, 'course' => $course->id)); 713 $this->assertInstanceOf('cm_info', $cm); 714 $this->assertEquals('Annie', $cm->name); 715 716 // A cm_info object stays as cm_info. 717 $this->assertSame($cm, cm_info::create($cm)); 718 719 // Invalid object (missing fields) causes error. 720 try { 721 cm_info::create((object)array('id' => $page->cmid)); 722 $this->fail(); 723 } catch (Exception $e) { 724 $this->assertInstanceOf('coding_exception', $e); 725 } 726 727 // Create a second hidden activity. 728 $hiddenpage = $generator->create_module('page', array('course' => $course->id, 729 'name' => 'Annie', 'visible' => 0)); 730 731 // Create 2 user accounts, one is a manager who can see everything. 732 $user = $generator->create_user(); 733 $generator->enrol_user($user->id, $course->id); 734 $manager = $generator->create_user(); 735 $generator->enrol_user($manager->id, $course->id, 736 $DB->get_field('role', 'id', array('shortname' => 'manager'), MUST_EXIST)); 737 738 // User can see the normal page but not the hidden one. 739 $cm = cm_info::create((object)array('id' => $page->cmid, 'course' => $course->id), 740 $user->id); 741 $this->assertTrue($cm->uservisible); 742 $cm = cm_info::create((object)array('id' => $hiddenpage->cmid, 'course' => $course->id), 743 $user->id); 744 $this->assertFalse($cm->uservisible); 745 746 // Manager can see the hidden one too. 747 $cm = cm_info::create((object)array('id' => $hiddenpage->cmid, 'course' => $course->id), 748 $manager->id); 749 $this->assertTrue($cm->uservisible); 750 } 751 752 /** 753 * Tests function for getting $course and $cm at once quickly from modinfo 754 * based on cmid or cm record. 755 */ 756 public function test_get_course_and_cm_from_cmid() { 757 global $CFG, $DB; 758 $this->resetAfterTest(); 759 760 // Create a course and an activity. 761 $generator = $this->getDataGenerator(); 762 $course = $generator->create_course(array('shortname' => 'Halls')); 763 $page = $generator->create_module('page', array('course' => $course->id, 764 'name' => 'Annie')); 765 766 // Successful usage. 767 list($course, $cm) = get_course_and_cm_from_cmid($page->cmid); 768 $this->assertEquals('Halls', $course->shortname); 769 $this->assertInstanceOf('cm_info', $cm); 770 $this->assertEquals('Annie', $cm->name); 771 772 // Specified module type. 773 list($course, $cm) = get_course_and_cm_from_cmid($page->cmid, 'page'); 774 $this->assertEquals('Annie', $cm->name); 775 776 // With id in object. 777 $fakecm = (object)array('id' => $page->cmid); 778 list($course, $cm) = get_course_and_cm_from_cmid($fakecm); 779 $this->assertEquals('Halls', $course->shortname); 780 $this->assertEquals('Annie', $cm->name); 781 782 // With both id and course in object. 783 $fakecm->course = $course->id; 784 list($course, $cm) = get_course_and_cm_from_cmid($fakecm); 785 $this->assertEquals('Halls', $course->shortname); 786 $this->assertEquals('Annie', $cm->name); 787 788 // With supplied course id. 789 list($course, $cm) = get_course_and_cm_from_cmid($page->cmid, 'page', $course->id); 790 $this->assertEquals('Annie', $cm->name); 791 792 // With supplied course object (modified just so we can check it is 793 // indeed reusing the supplied object). 794 $course->silly = true; 795 list($course, $cm) = get_course_and_cm_from_cmid($page->cmid, 'page', $course); 796 $this->assertEquals('Annie', $cm->name); 797 $this->assertTrue($course->silly); 798 799 // Incorrect module type. 800 try { 801 get_course_and_cm_from_cmid($page->cmid, 'forum'); 802 $this->fail(); 803 } catch (moodle_exception $e) { 804 $this->assertEquals('invalidcoursemodule', $e->errorcode); 805 } 806 807 // Invalid module name. 808 try { 809 get_course_and_cm_from_cmid($page->cmid, 'pigs can fly'); 810 $this->fail(); 811 } catch (coding_exception $e) { 812 $this->assertStringContainsString('Invalid modulename parameter', $e->getMessage()); 813 } 814 815 // Doesn't exist. 816 try { 817 get_course_and_cm_from_cmid($page->cmid + 1); 818 $this->fail(); 819 } catch (moodle_exception $e) { 820 $this->assertInstanceOf('dml_exception', $e); 821 } 822 823 // Create a second hidden activity. 824 $hiddenpage = $generator->create_module('page', array('course' => $course->id, 825 'name' => 'Annie', 'visible' => 0)); 826 827 // Create 2 user accounts, one is a manager who can see everything. 828 $user = $generator->create_user(); 829 $generator->enrol_user($user->id, $course->id); 830 $manager = $generator->create_user(); 831 $generator->enrol_user($manager->id, $course->id, 832 $DB->get_field('role', 'id', array('shortname' => 'manager'), MUST_EXIST)); 833 834 // User can see the normal page but not the hidden one. 835 list($course, $cm) = get_course_and_cm_from_cmid($page->cmid, 'page', 0, $user->id); 836 $this->assertTrue($cm->uservisible); 837 list($course, $cm) = get_course_and_cm_from_cmid($hiddenpage->cmid, 'page', 0, $user->id); 838 $this->assertFalse($cm->uservisible); 839 840 // Manager can see the hidden one too. 841 list($course, $cm) = get_course_and_cm_from_cmid($hiddenpage->cmid, 'page', 0, $manager->id); 842 $this->assertTrue($cm->uservisible); 843 } 844 845 /** 846 * Tests function for getting $course and $cm at once quickly from modinfo 847 * based on instance id or record. 848 */ 849 public function test_get_course_and_cm_from_instance() { 850 global $CFG, $DB; 851 $this->resetAfterTest(); 852 853 // Create a course and an activity. 854 $generator = $this->getDataGenerator(); 855 $course = $generator->create_course(array('shortname' => 'Halls')); 856 $page = $generator->create_module('page', array('course' => $course->id, 857 'name' => 'Annie')); 858 859 // Successful usage. 860 list($course, $cm) = get_course_and_cm_from_instance($page->id, 'page'); 861 $this->assertEquals('Halls', $course->shortname); 862 $this->assertInstanceOf('cm_info', $cm); 863 $this->assertEquals('Annie', $cm->name); 864 865 // With id in object. 866 $fakeinstance = (object)array('id' => $page->id); 867 list($course, $cm) = get_course_and_cm_from_instance($fakeinstance, 'page'); 868 $this->assertEquals('Halls', $course->shortname); 869 $this->assertEquals('Annie', $cm->name); 870 871 // With both id and course in object. 872 $fakeinstance->course = $course->id; 873 list($course, $cm) = get_course_and_cm_from_instance($fakeinstance, 'page'); 874 $this->assertEquals('Halls', $course->shortname); 875 $this->assertEquals('Annie', $cm->name); 876 877 // With supplied course id. 878 list($course, $cm) = get_course_and_cm_from_instance($page->id, 'page', $course->id); 879 $this->assertEquals('Annie', $cm->name); 880 881 // With supplied course object (modified just so we can check it is 882 // indeed reusing the supplied object). 883 $course->silly = true; 884 list($course, $cm) = get_course_and_cm_from_instance($page->id, 'page', $course); 885 $this->assertEquals('Annie', $cm->name); 886 $this->assertTrue($course->silly); 887 888 // Doesn't exist (or is wrong type). 889 try { 890 get_course_and_cm_from_instance($page->id, 'forum'); 891 $this->fail(); 892 } catch (moodle_exception $e) { 893 $this->assertInstanceOf('dml_exception', $e); 894 } 895 896 // Invalid module name. 897 try { 898 get_course_and_cm_from_cmid($page->cmid, '1337 h4x0ring'); 899 $this->fail(); 900 } catch (coding_exception $e) { 901 $this->assertStringContainsString('Invalid modulename parameter', $e->getMessage()); 902 } 903 904 // Create a second hidden activity. 905 $hiddenpage = $generator->create_module('page', array('course' => $course->id, 906 'name' => 'Annie', 'visible' => 0)); 907 908 // Create 2 user accounts, one is a manager who can see everything. 909 $user = $generator->create_user(); 910 $generator->enrol_user($user->id, $course->id); 911 $manager = $generator->create_user(); 912 $generator->enrol_user($manager->id, $course->id, 913 $DB->get_field('role', 'id', array('shortname' => 'manager'), MUST_EXIST)); 914 915 // User can see the normal page but not the hidden one. 916 list($course, $cm) = get_course_and_cm_from_cmid($page->cmid, 'page', 0, $user->id); 917 $this->assertTrue($cm->uservisible); 918 list($course, $cm) = get_course_and_cm_from_cmid($hiddenpage->cmid, 'page', 0, $user->id); 919 $this->assertFalse($cm->uservisible); 920 921 // Manager can see the hidden one too. 922 list($course, $cm) = get_course_and_cm_from_cmid($hiddenpage->cmid, 'page', 0, $manager->id); 923 $this->assertTrue($cm->uservisible); 924 } 925 926 /** 927 * Test test_get_section_info_by_id method 928 * 929 * @dataProvider get_section_info_by_id_provider 930 * @covers \course_modinfo::get_section_info_by_id 931 * 932 * @param int $sectionnum the section number 933 * @param int $strictness the search strict mode 934 * @param bool $expectnull if the function will return a null 935 * @param bool $expectexception if the function will throw an exception 936 */ 937 public function test_get_section_info_by_id( 938 int $sectionnum, 939 int $strictness = IGNORE_MISSING, 940 bool $expectnull = false, 941 bool $expectexception = false 942 ) { 943 global $DB; 944 945 $this->resetAfterTest(); 946 947 // Create a course with 4 sections. 948 $course = $this->getDataGenerator()->create_course(['numsections' => 4]); 949 950 // Index sections. 951 $sectionindex = []; 952 $modinfo = get_fast_modinfo($course); 953 $allsections = $modinfo->get_section_info_all(); 954 foreach ($allsections as $section) { 955 $sectionindex[$section->section] = $section->id; 956 } 957 958 if ($expectexception) { 959 $this->expectException(moodle_exception::class); 960 } 961 962 $sectionid = $sectionindex[$sectionnum] ?? -1; 963 964 $section = $modinfo->get_section_info_by_id($sectionid, $strictness); 965 966 if ($expectnull) { 967 $this->assertNull($section); 968 } else { 969 $this->assertEquals($sectionid, $section->id); 970 $this->assertEquals($sectionnum, $section->section); 971 } 972 } 973 974 /** 975 * Data provider for test_get_section_info_by_id(). 976 * 977 * @return array 978 */ 979 public function get_section_info_by_id_provider() { 980 return [ 981 'Valid section id' => [ 982 'sectionnum' => 1, 983 'strictness' => IGNORE_MISSING, 984 'expectnull' => false, 985 'expectexception' => false, 986 ], 987 'Section zero' => [ 988 'sectionnum' => 0, 989 'strictness' => IGNORE_MISSING, 990 'expectnull' => false, 991 'expectexception' => false, 992 ], 993 'invalid section ignore missing' => [ 994 'sectionnum' => -1, 995 'strictness' => IGNORE_MISSING, 996 'expectnull' => true, 997 'expectexception' => false, 998 ], 999 'invalid section must exists' => [ 1000 'sectionnum' => -1, 1001 'strictness' => MUST_EXIST, 1002 'expectnull' => false, 1003 'expectexception' => true, 1004 ], 1005 ]; 1006 } 1007 1008 /** 1009 * Test purge_section_cache_by_id method 1010 * 1011 * @covers \course_modinfo::purge_course_section_cache_by_id 1012 * @return void 1013 */ 1014 public function test_purge_section_cache_by_id(): void { 1015 $this->resetAfterTest(); 1016 $this->setAdminUser(); 1017 $cache = cache::make('core', 'coursemodinfo'); 1018 1019 // Generate the course and pre-requisite section. 1020 $course = $this->getDataGenerator()->create_course(['format' => 'topics', 'numsections' => 3], ['createsections' => true]); 1021 // Reset course cache. 1022 rebuild_course_cache($course->id, true); 1023 // Build course cache. 1024 get_fast_modinfo($course->id); 1025 // Get the course modinfo cache. 1026 $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); 1027 // Get the section cache. 1028 $sectioncaches = $coursemodinfo->sectioncache; 1029 1030 // Make sure that we will have 4 section caches here. 1031 $this->assertCount(4, $sectioncaches); 1032 $this->assertArrayHasKey(0, $sectioncaches); 1033 $this->assertArrayHasKey(1, $sectioncaches); 1034 $this->assertArrayHasKey(2, $sectioncaches); 1035 $this->assertArrayHasKey(3, $sectioncaches); 1036 1037 // Purge cache for the section by id. 1038 course_modinfo::purge_course_section_cache_by_id($course->id, $sectioncaches[1]->id); 1039 // Get the course modinfo cache. 1040 $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); 1041 // Get the section cache. 1042 $sectioncaches = $coursemodinfo->sectioncache; 1043 1044 // Make sure that we will have 3 section caches left. 1045 $this->assertCount(3, $sectioncaches); 1046 $this->assertArrayNotHasKey(1, $sectioncaches); 1047 $this->assertArrayHasKey(0, $sectioncaches); 1048 $this->assertArrayHasKey(2, $sectioncaches); 1049 $this->assertArrayHasKey(3, $sectioncaches); 1050 // Make sure that the cacherev will be reset. 1051 $this->assertEquals(-1, $coursemodinfo->cacherev); 1052 } 1053 1054 /** 1055 * Test purge_section_cache_by_number method 1056 * 1057 * @covers \course_modinfo::purge_course_section_cache_by_number 1058 * @return void 1059 */ 1060 public function test_section_cache_by_number(): void { 1061 $this->resetAfterTest(); 1062 $this->setAdminUser(); 1063 $cache = cache::make('core', 'coursemodinfo'); 1064 1065 // Generate the course and pre-requisite section. 1066 $course = $this->getDataGenerator()->create_course(['format' => 'topics', 'numsections' => 3], ['createsections' => true]); 1067 // Reset course cache. 1068 rebuild_course_cache($course->id, true); 1069 // Build course cache. 1070 get_fast_modinfo($course->id); 1071 // Get the course modinfo cache. 1072 $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); 1073 // Get the section cache. 1074 $sectioncaches = $coursemodinfo->sectioncache; 1075 1076 // Make sure that we will have 4 section caches here. 1077 $this->assertCount(4, $sectioncaches); 1078 $this->assertArrayHasKey(0, $sectioncaches); 1079 $this->assertArrayHasKey(1, $sectioncaches); 1080 $this->assertArrayHasKey(2, $sectioncaches); 1081 $this->assertArrayHasKey(3, $sectioncaches); 1082 1083 // Purge cache for the section with section number is 1. 1084 course_modinfo::purge_course_section_cache_by_number($course->id, 1); 1085 // Get the course modinfo cache. 1086 $coursemodinfo = $cache->get_versioned($course->id, $course->cacherev); 1087 // Get the section cache. 1088 $sectioncaches = $coursemodinfo->sectioncache; 1089 1090 // Make sure that we will have 3 section caches left. 1091 $this->assertCount(3, $sectioncaches); 1092 $this->assertArrayNotHasKey(1, $sectioncaches); 1093 $this->assertArrayHasKey(0, $sectioncaches); 1094 $this->assertArrayHasKey(2, $sectioncaches); 1095 $this->assertArrayHasKey(3, $sectioncaches); 1096 // Make sure that the cacherev will be reset. 1097 $this->assertEquals(-1, $coursemodinfo->cacherev); 1098 } 1099 1100 /** 1101 * Tests that if the modinfo cache returns a newer-than-expected version, Moodle won't rebuild 1102 * it. 1103 * 1104 * This is important to avoid wasted time/effort and poor performance, for example in cases 1105 * where multiple requests are accessing the course. 1106 * 1107 * Certain cases could be particularly bad if this test fails. For example, if using clustered 1108 * databases where there is a 100ms delay between updates to the course table being available 1109 * to all users (but no such delay on the cache infrastructure), then during that 100ms, every 1110 * request that calls get_fast_modinfo and uses the read-only database will rebuild the course 1111 * cache. Since these will then create a still-newer version, future requests for the next 1112 * 100ms will also rebuild it again... etc. 1113 * 1114 * @covers \course_modinfo 1115 */ 1116 public function test_get_modinfo_with_newer_version(): void { 1117 global $DB; 1118 1119 $this->resetAfterTest(); 1120 1121 // Get info about a course and build the initial cache, then drop it from memory. 1122 $course = $this->getDataGenerator()->create_course(); 1123 get_fast_modinfo($course); 1124 get_fast_modinfo(0, 0, true); 1125 1126 // User A starts a request, which takes some time... 1127 $useracourse = $DB->get_record('course', ['id' => $course->id]); 1128 1129 // User B also starts a request and makes a change to the course. 1130 $userbcourse = $DB->get_record('course', ['id' => $course->id]); 1131 $this->getDataGenerator()->create_module('page', ['course' => $course->id]); 1132 rebuild_course_cache($userbcourse->id, false); 1133 1134 // Finally, user A's request now gets modinfo. It should accept the version from B even 1135 // though the course version (of cache) is newer than the one expected by A. 1136 $before = $DB->perf_get_queries(); 1137 $modinfo = get_fast_modinfo($useracourse); 1138 $after = $DB->perf_get_queries(); 1139 $this->assertEquals($after, $before, 'Should use cached version, making no DB queries'); 1140 1141 // Obviously, modinfo should include the Page now. 1142 $this->assertCount(1, $modinfo->get_instances_of('page')); 1143 } 1144 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body