Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
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 * Test for various bits of datalib.php. 19 * 20 * @package core 21 * @category phpunit 22 * @copyright 2012 The Open University 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 29 /** 30 * Test for various bits of datalib.php. 31 * 32 * @package core 33 * @category phpunit 34 * @copyright 2012 The Open University 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 class core_datalib_testcase extends advanced_testcase { 38 protected function normalise_sql($sort) { 39 return preg_replace('~\s+~', ' ', $sort); 40 } 41 42 protected function assert_same_sql($expected, $actual) { 43 $this->assertSame($this->normalise_sql($expected), $this->normalise_sql($actual)); 44 } 45 46 /** 47 * Do a test of the user search SQL with database users. 48 */ 49 public function test_users_search_sql() { 50 global $DB; 51 $this->resetAfterTest(); 52 53 // Set up test users. 54 $user1 = array( 55 'username' => 'usernametest1', 56 'idnumber' => 'idnumbertest1', 57 'firstname' => 'First Name User Test 1', 58 'lastname' => 'Last Name User Test 1', 59 'email' => 'usertest1@example.com', 60 'address' => '2 Test Street Perth 6000 WA', 61 'phone1' => '01010101010', 62 'phone2' => '02020203', 63 'icq' => 'testuser1', 64 'skype' => 'testuser1', 65 'yahoo' => 'testuser1', 66 'aim' => 'testuser1', 67 'msn' => 'testuser1', 68 'department' => 'Department of user 1', 69 'institution' => 'Institution of user 1', 70 'description' => 'This is a description for user 1', 71 'descriptionformat' => FORMAT_MOODLE, 72 'city' => 'Perth', 73 'url' => 'http://moodle.org', 74 'country' => 'AU' 75 ); 76 $user1 = self::getDataGenerator()->create_user($user1); 77 $user2 = array( 78 'username' => 'usernametest2', 79 'idnumber' => 'idnumbertest2', 80 'firstname' => 'First Name User Test 2', 81 'lastname' => 'Last Name User Test 2', 82 'email' => 'usertest2@example.com', 83 'address' => '222 Test Street Perth 6000 WA', 84 'phone1' => '01010101010', 85 'phone2' => '02020203', 86 'icq' => 'testuser1', 87 'skype' => 'testuser1', 88 'yahoo' => 'testuser1', 89 'aim' => 'testuser1', 90 'msn' => 'testuser1', 91 'department' => 'Department of user 2', 92 'institution' => 'Institution of user 2', 93 'description' => 'This is a description for user 2', 94 'descriptionformat' => FORMAT_MOODLE, 95 'city' => 'Perth', 96 'url' => 'http://moodle.org', 97 'country' => 'AU' 98 ); 99 $user2 = self::getDataGenerator()->create_user($user2); 100 101 // Search by name (anywhere in text). 102 list($sql, $params) = users_search_sql('User Test 2', ''); 103 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 104 $this->assertFalse(array_key_exists($user1->id, $results)); 105 $this->assertTrue(array_key_exists($user2->id, $results)); 106 107 // Search by (most of) full name. 108 list($sql, $params) = users_search_sql('First Name User Test 2 Last Name User', ''); 109 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 110 $this->assertFalse(array_key_exists($user1->id, $results)); 111 $this->assertTrue(array_key_exists($user2->id, $results)); 112 113 // Search by name (start of text) valid or not. 114 list($sql, $params) = users_search_sql('User Test 2', '', false); 115 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 116 $this->assertEquals(0, count($results)); 117 list($sql, $params) = users_search_sql('First Name User Test 2', '', false); 118 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 119 $this->assertFalse(array_key_exists($user1->id, $results)); 120 $this->assertTrue(array_key_exists($user2->id, $results)); 121 122 // Search by extra fields included or not (address). 123 list($sql, $params) = users_search_sql('Test Street', '', true); 124 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 125 $this->assertCount(0, $results); 126 list($sql, $params) = users_search_sql('Test Street', '', true, array('address')); 127 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 128 $this->assertCount(2, $results); 129 130 // Exclude user. 131 list($sql, $params) = users_search_sql('User Test', '', true, array(), array($user1->id)); 132 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 133 $this->assertFalse(array_key_exists($user1->id, $results)); 134 $this->assertTrue(array_key_exists($user2->id, $results)); 135 136 // Include only user. 137 list($sql, $params) = users_search_sql('User Test', '', true, array(), array(), array($user1->id)); 138 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 139 $this->assertTrue(array_key_exists($user1->id, $results)); 140 $this->assertFalse(array_key_exists($user2->id, $results)); 141 142 // Join with another table and use different prefix. 143 set_user_preference('amphibian', 'frog', $user1); 144 set_user_preference('amphibian', 'salamander', $user2); 145 list($sql, $params) = users_search_sql('User Test 1', 'qq'); 146 $results = $DB->get_records_sql(" 147 SELECT up.id, up.value 148 FROM {user} qq 149 JOIN {user_preferences} up ON up.userid = qq.id 150 WHERE up.name = :prefname 151 AND $sql", array_merge(array('prefname' => 'amphibian'), $params)); 152 $this->assertEquals(1, count($results)); 153 foreach ($results as $record) { 154 $this->assertSame('frog', $record->value); 155 } 156 } 157 158 public function test_users_order_by_sql_simple() { 159 list($sort, $params) = users_order_by_sql(); 160 $this->assert_same_sql('lastname, firstname, id', $sort); 161 $this->assertEquals(array(), $params); 162 } 163 164 public function test_users_order_by_sql_table_prefix() { 165 list($sort, $params) = users_order_by_sql('u'); 166 $this->assert_same_sql('u.lastname, u.firstname, u.id', $sort); 167 $this->assertEquals(array(), $params); 168 } 169 170 public function test_users_order_by_sql_search_no_extra_fields() { 171 global $CFG, $DB; 172 $this->resetAfterTest(true); 173 174 $CFG->showuseridentity = ''; 175 176 list($sort, $params) = users_order_by_sql('', 'search', context_system::instance()); 177 $this->assert_same_sql('CASE WHEN 178 ' . $DB->sql_fullname() . ' = :usersortexact1 OR 179 LOWER(firstname) = LOWER(:usersortexact2) OR 180 LOWER(lastname) = LOWER(:usersortexact3) 181 THEN 0 ELSE 1 END, lastname, firstname, id', $sort); 182 $this->assertEquals(array('usersortexact1' => 'search', 'usersortexact2' => 'search', 183 'usersortexact3' => 'search'), $params); 184 } 185 186 public function test_users_order_by_sql_search_with_extra_fields_and_prefix() { 187 global $CFG, $DB; 188 $this->resetAfterTest(); 189 190 $CFG->showuseridentity = 'email,idnumber'; 191 $this->setAdminUser(); 192 193 list($sort, $params) = users_order_by_sql('u', 'search', context_system::instance()); 194 $this->assert_same_sql('CASE WHEN 195 ' . $DB->sql_fullname('u.firstname', 'u.lastname') . ' = :usersortexact1 OR 196 LOWER(u.firstname) = LOWER(:usersortexact2) OR 197 LOWER(u.lastname) = LOWER(:usersortexact3) OR 198 LOWER(u.email) = LOWER(:usersortexact4) OR 199 LOWER(u.idnumber) = LOWER(:usersortexact5) 200 THEN 0 ELSE 1 END, u.lastname, u.firstname, u.id', $sort); 201 $this->assertEquals(array('usersortexact1' => 'search', 'usersortexact2' => 'search', 202 'usersortexact3' => 'search', 'usersortexact4' => 'search', 'usersortexact5' => 'search'), $params); 203 } 204 205 public function test_get_admin() { 206 global $CFG, $DB; 207 $this->resetAfterTest(); 208 209 $this->assertSame('2', $CFG->siteadmins); // Admin always has id 2 in new installs. 210 $defaultadmin = get_admin(); 211 $this->assertEquals($defaultadmin->id, 2); 212 213 unset_config('siteadmins'); 214 $this->assertFalse(get_admin()); 215 216 set_config('siteadmins', -1); 217 $this->assertFalse(get_admin()); 218 219 $user1 = $this->getDataGenerator()->create_user(); 220 $user2 = $this->getDataGenerator()->create_user(); 221 222 set_config('siteadmins', $user1->id.','.$user2->id); 223 $admin = get_admin(); 224 $this->assertEquals($user1->id, $admin->id); 225 226 set_config('siteadmins', '-1,'.$user2->id.','.$user1->id); 227 $admin = get_admin(); 228 $this->assertEquals($user2->id, $admin->id); 229 230 $odlread = $DB->perf_get_reads(); 231 get_admin(); // No DB queries on repeated call expected. 232 get_admin(); 233 get_admin(); 234 $this->assertEquals($odlread, $DB->perf_get_reads()); 235 } 236 237 public function test_get_admins() { 238 global $CFG, $DB; 239 $this->resetAfterTest(); 240 241 $this->assertSame('2', $CFG->siteadmins); // Admin always has id 2 in new installs. 242 243 $user1 = $this->getDataGenerator()->create_user(); 244 $user2 = $this->getDataGenerator()->create_user(); 245 $user3 = $this->getDataGenerator()->create_user(); 246 $user4 = $this->getDataGenerator()->create_user(); 247 248 $admins = get_admins(); 249 $this->assertCount(1, $admins); 250 $admin = reset($admins); 251 $this->assertTrue(isset($admins[$admin->id])); 252 $this->assertEquals(2, $admin->id); 253 254 unset_config('siteadmins'); 255 $this->assertSame(array(), get_admins()); 256 257 set_config('siteadmins', -1); 258 $this->assertSame(array(), get_admins()); 259 260 set_config('siteadmins', '-1,'.$user2->id.','.$user1->id.','.$user3->id); 261 $this->assertEquals(array($user2->id=>$user2, $user1->id=>$user1, $user3->id=>$user3), get_admins()); 262 263 $odlread = $DB->perf_get_reads(); 264 get_admins(); // This should make just one query. 265 $this->assertEquals($odlread+1, $DB->perf_get_reads()); 266 } 267 268 public function test_get_course() { 269 global $DB, $PAGE, $SITE; 270 $this->resetAfterTest(); 271 272 // First test course will be current course ($COURSE). 273 $course1obj = $this->getDataGenerator()->create_course(array('shortname' => 'FROGS')); 274 $PAGE->set_course($course1obj); 275 276 // Second test course is not current course. 277 $course2obj = $this->getDataGenerator()->create_course(array('shortname' => 'ZOMBIES')); 278 279 // Check it does not make any queries when requesting the $COURSE/$SITE. 280 $before = $DB->perf_get_queries(); 281 $result = get_course($course1obj->id); 282 $this->assertEquals($before, $DB->perf_get_queries()); 283 $this->assertSame('FROGS', $result->shortname); 284 $result = get_course($SITE->id); 285 $this->assertEquals($before, $DB->perf_get_queries()); 286 287 // Check it makes 1 query to request other courses. 288 $result = get_course($course2obj->id); 289 $this->assertSame('ZOMBIES', $result->shortname); 290 $this->assertEquals($before + 1, $DB->perf_get_queries()); 291 } 292 293 /** 294 * Test that specifying fields when calling get_courses always returns required fields "id, category, visible" 295 */ 296 public function test_get_courses_with_fields(): void { 297 $this->resetAfterTest(); 298 299 $category = $this->getDataGenerator()->create_category(); 300 $course = $this->getDataGenerator()->create_course(['category' => $category->id]); 301 302 // Specify "id" only. 303 $courses = get_courses($category->id, 'c.sortorder', 'c.id'); 304 $this->assertCount(1, $courses); 305 $this->assertEquals((object) [ 306 'id' => $course->id, 307 'category' => $course->category, 308 'visible' => $course->visible, 309 ], reset($courses)); 310 311 // Specify some optional fields. 312 $courses = get_courses($category->id, 'c.sortorder', 'c.id, c.shortname, c.fullname'); 313 $this->assertCount(1, $courses); 314 $this->assertEquals((object) [ 315 'id' => $course->id, 316 'category' => $course->category, 317 'visible' => $course->visible, 318 'shortname' => $course->shortname, 319 'fullname' => $course->fullname, 320 ], reset($courses)); 321 } 322 323 public function test_increment_revision_number() { 324 global $DB; 325 $this->resetAfterTest(); 326 327 // Use one of the fields that are used with increment_revision_number(). 328 $course1 = $this->getDataGenerator()->create_course(); 329 $course2 = $this->getDataGenerator()->create_course(); 330 $DB->set_field('course', 'cacherev', 1, array()); 331 332 $record1 = $DB->get_record('course', array('id'=>$course1->id)); 333 $record2 = $DB->get_record('course', array('id'=>$course2->id)); 334 $this->assertEquals(1, $record1->cacherev); 335 $this->assertEquals(1, $record2->cacherev); 336 337 // Incrementing some lower value. 338 $this->setCurrentTimeStart(); 339 increment_revision_number('course', 'cacherev', 'id = :id', array('id'=>$course1->id)); 340 $record1 = $DB->get_record('course', array('id'=>$course1->id)); 341 $record2 = $DB->get_record('course', array('id'=>$course2->id)); 342 $this->assertTimeCurrent($record1->cacherev); 343 $this->assertEquals(1, $record2->cacherev); 344 345 // Incrementing in the same second. 346 $rev1 = $DB->get_field('course', 'cacherev', array('id'=>$course1->id)); 347 $now = time(); 348 $DB->set_field('course', 'cacherev', $now, array('id'=>$course1->id)); 349 increment_revision_number('course', 'cacherev', 'id = :id', array('id'=>$course1->id)); 350 $rev2 = $DB->get_field('course', 'cacherev', array('id'=>$course1->id)); 351 $this->assertGreaterThan($rev1, $rev2); 352 increment_revision_number('course', 'cacherev', 'id = :id', array('id'=>$course1->id)); 353 $rev3 = $DB->get_field('course', 'cacherev', array('id'=>$course1->id)); 354 $this->assertGreaterThan($rev2, $rev3); 355 $this->assertGreaterThan($now+1, $rev3); 356 increment_revision_number('course', 'cacherev', 'id = :id', array('id'=>$course1->id)); 357 $rev4 = $DB->get_field('course', 'cacherev', array('id'=>$course1->id)); 358 $this->assertGreaterThan($rev3, $rev4); 359 $this->assertGreaterThan($now+2, $rev4); 360 361 // Recovering from runaway revision. 362 $DB->set_field('course', 'cacherev', time()+60*60*60, array('id'=>$course2->id)); 363 $record2 = $DB->get_record('course', array('id'=>$course2->id)); 364 $this->assertGreaterThan(time(), $record2->cacherev); 365 $this->setCurrentTimeStart(); 366 increment_revision_number('course', 'cacherev', 'id = :id', array('id'=>$course2->id)); 367 $record2b = $DB->get_record('course', array('id'=>$course2->id)); 368 $this->assertTimeCurrent($record2b->cacherev); 369 370 // Update all revisions. 371 $DB->set_field('course', 'cacherev', 1, array()); 372 $this->setCurrentTimeStart(); 373 increment_revision_number('course', 'cacherev', ''); 374 $record1 = $DB->get_record('course', array('id'=>$course1->id)); 375 $record2 = $DB->get_record('course', array('id'=>$course2->id)); 376 $this->assertTimeCurrent($record1->cacherev); 377 $this->assertEquals($record1->cacherev, $record2->cacherev); 378 } 379 380 public function test_get_coursemodule_from_id() { 381 global $CFG; 382 383 $this->resetAfterTest(); 384 $this->setAdminUser(); // Some generators have bogus access control. 385 386 $this->assertFileExists("$CFG->dirroot/mod/folder/lib.php"); 387 $this->assertFileExists("$CFG->dirroot/mod/glossary/lib.php"); 388 389 $course1 = $this->getDataGenerator()->create_course(); 390 $course2 = $this->getDataGenerator()->create_course(); 391 392 $folder1a = $this->getDataGenerator()->create_module('folder', array('course' => $course1, 'section' => 3)); 393 $folder1b = $this->getDataGenerator()->create_module('folder', array('course' => $course1)); 394 $glossary1 = $this->getDataGenerator()->create_module('glossary', array('course' => $course1)); 395 396 $folder2 = $this->getDataGenerator()->create_module('folder', array('course' => $course2)); 397 398 $cm = get_coursemodule_from_id('folder', $folder1a->cmid); 399 $this->assertInstanceOf('stdClass', $cm); 400 $this->assertSame('folder', $cm->modname); 401 $this->assertSame($folder1a->id, $cm->instance); 402 $this->assertSame($folder1a->course, $cm->course); 403 $this->assertObjectNotHasAttribute('sectionnum', $cm); 404 405 $this->assertEquals($cm, get_coursemodule_from_id('', $folder1a->cmid)); 406 $this->assertEquals($cm, get_coursemodule_from_id('folder', $folder1a->cmid, $course1->id)); 407 $this->assertEquals($cm, get_coursemodule_from_id('folder', $folder1a->cmid, 0)); 408 $this->assertFalse(get_coursemodule_from_id('folder', $folder1a->cmid, -10)); 409 410 $cm2 = get_coursemodule_from_id('folder', $folder1a->cmid, 0, true); 411 $this->assertEquals(3, $cm2->sectionnum); 412 unset($cm2->sectionnum); 413 $this->assertEquals($cm, $cm2); 414 415 $this->assertFalse(get_coursemodule_from_id('folder', -11)); 416 417 try { 418 get_coursemodule_from_id('folder', -11, 0, false, MUST_EXIST); 419 $this->fail('dml_missing_record_exception expected'); 420 } catch (moodle_exception $e) { 421 $this->assertInstanceOf('dml_missing_record_exception', $e); 422 } 423 424 try { 425 get_coursemodule_from_id('', -11, 0, false, MUST_EXIST); 426 $this->fail('dml_missing_record_exception expected'); 427 } catch (moodle_exception $e) { 428 $this->assertInstanceOf('dml_missing_record_exception', $e); 429 } 430 431 try { 432 get_coursemodule_from_id('a b', $folder1a->cmid, 0, false, MUST_EXIST); 433 $this->fail('coding_exception expected'); 434 } catch (moodle_exception $e) { 435 $this->assertInstanceOf('coding_exception', $e); 436 } 437 438 try { 439 get_coursemodule_from_id('abc', $folder1a->cmid, 0, false, MUST_EXIST); 440 $this->fail('dml_read_exception expected'); 441 } catch (moodle_exception $e) { 442 $this->assertInstanceOf('dml_read_exception', $e); 443 } 444 } 445 446 public function test_get_coursemodule_from_instance() { 447 global $CFG; 448 449 $this->resetAfterTest(); 450 $this->setAdminUser(); // Some generators have bogus access control. 451 452 $this->assertFileExists("$CFG->dirroot/mod/folder/lib.php"); 453 $this->assertFileExists("$CFG->dirroot/mod/glossary/lib.php"); 454 455 $course1 = $this->getDataGenerator()->create_course(); 456 $course2 = $this->getDataGenerator()->create_course(); 457 458 $folder1a = $this->getDataGenerator()->create_module('folder', array('course' => $course1, 'section' => 3)); 459 $folder1b = $this->getDataGenerator()->create_module('folder', array('course' => $course1)); 460 461 $folder2 = $this->getDataGenerator()->create_module('folder', array('course' => $course2)); 462 463 $cm = get_coursemodule_from_instance('folder', $folder1a->id); 464 $this->assertInstanceOf('stdClass', $cm); 465 $this->assertSame('folder', $cm->modname); 466 $this->assertSame($folder1a->id, $cm->instance); 467 $this->assertSame($folder1a->course, $cm->course); 468 $this->assertObjectNotHasAttribute('sectionnum', $cm); 469 470 $this->assertEquals($cm, get_coursemodule_from_instance('folder', $folder1a->id, $course1->id)); 471 $this->assertEquals($cm, get_coursemodule_from_instance('folder', $folder1a->id, 0)); 472 $this->assertFalse(get_coursemodule_from_instance('folder', $folder1a->id, -10)); 473 474 $cm2 = get_coursemodule_from_instance('folder', $folder1a->id, 0, true); 475 $this->assertEquals(3, $cm2->sectionnum); 476 unset($cm2->sectionnum); 477 $this->assertEquals($cm, $cm2); 478 479 $this->assertFalse(get_coursemodule_from_instance('folder', -11)); 480 481 try { 482 get_coursemodule_from_instance('folder', -11, 0, false, MUST_EXIST); 483 $this->fail('dml_missing_record_exception expected'); 484 } catch (moodle_exception $e) { 485 $this->assertInstanceOf('dml_missing_record_exception', $e); 486 } 487 488 try { 489 get_coursemodule_from_instance('a b', $folder1a->cmid, 0, false, MUST_EXIST); 490 $this->fail('coding_exception expected'); 491 } catch (moodle_exception $e) { 492 $this->assertInstanceOf('coding_exception', $e); 493 } 494 495 try { 496 get_coursemodule_from_instance('', $folder1a->cmid, 0, false, MUST_EXIST); 497 $this->fail('coding_exception expected'); 498 } catch (moodle_exception $e) { 499 $this->assertInstanceOf('coding_exception', $e); 500 } 501 502 try { 503 get_coursemodule_from_instance('abc', $folder1a->cmid, 0, false, MUST_EXIST); 504 $this->fail('dml_read_exception expected'); 505 } catch (moodle_exception $e) { 506 $this->assertInstanceOf('dml_read_exception', $e); 507 } 508 } 509 510 public function test_get_coursemodules_in_course() { 511 global $CFG; 512 513 $this->resetAfterTest(); 514 $this->setAdminUser(); // Some generators have bogus access control. 515 516 $this->assertFileExists("$CFG->dirroot/mod/folder/lib.php"); 517 $this->assertFileExists("$CFG->dirroot/mod/glossary/lib.php"); 518 $this->assertFileExists("$CFG->dirroot/mod/label/lib.php"); 519 520 $course1 = $this->getDataGenerator()->create_course(); 521 $course2 = $this->getDataGenerator()->create_course(); 522 523 $folder1a = $this->getDataGenerator()->create_module('folder', array('course' => $course1, 'section' => 3)); 524 $folder1b = $this->getDataGenerator()->create_module('folder', array('course' => $course1)); 525 $glossary1 = $this->getDataGenerator()->create_module('glossary', array('course' => $course1)); 526 527 $folder2 = $this->getDataGenerator()->create_module('folder', array('course' => $course2)); 528 $glossary2a = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 529 $glossary2b = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 530 531 $modules = get_coursemodules_in_course('folder', $course1->id); 532 $this->assertCount(2, $modules); 533 534 $cm = $modules[$folder1a->cmid]; 535 $this->assertSame('folder', $cm->modname); 536 $this->assertSame($folder1a->id, $cm->instance); 537 $this->assertSame($folder1a->course, $cm->course); 538 $this->assertObjectNotHasAttribute('sectionnum', $cm); 539 $this->assertObjectNotHasAttribute('revision', $cm); 540 $this->assertObjectNotHasAttribute('display', $cm); 541 542 $cm = $modules[$folder1b->cmid]; 543 $this->assertSame('folder', $cm->modname); 544 $this->assertSame($folder1b->id, $cm->instance); 545 $this->assertSame($folder1b->course, $cm->course); 546 $this->assertObjectNotHasAttribute('sectionnum', $cm); 547 $this->assertObjectNotHasAttribute('revision', $cm); 548 $this->assertObjectNotHasAttribute('display', $cm); 549 550 $modules = get_coursemodules_in_course('folder', $course1->id, 'revision, display'); 551 $this->assertCount(2, $modules); 552 553 $cm = $modules[$folder1a->cmid]; 554 $this->assertSame('folder', $cm->modname); 555 $this->assertSame($folder1a->id, $cm->instance); 556 $this->assertSame($folder1a->course, $cm->course); 557 $this->assertObjectNotHasAttribute('sectionnum', $cm); 558 $this->assertObjectHasAttribute('revision', $cm); 559 $this->assertObjectHasAttribute('display', $cm); 560 561 $modules = get_coursemodules_in_course('label', $course1->id); 562 $this->assertCount(0, $modules); 563 564 try { 565 get_coursemodules_in_course('a b', $course1->id); 566 $this->fail('coding_exception expected'); 567 } catch (moodle_exception $e) { 568 $this->assertInstanceOf('coding_exception', $e); 569 } 570 571 try { 572 get_coursemodules_in_course('abc', $course1->id); 573 $this->fail('dml_read_exception expected'); 574 } catch (moodle_exception $e) { 575 $this->assertInstanceOf('dml_read_exception', $e); 576 } 577 } 578 579 public function test_get_all_instances_in_courses() { 580 global $CFG; 581 582 $this->resetAfterTest(); 583 $this->setAdminUser(); // Some generators have bogus access control. 584 585 $this->assertFileExists("$CFG->dirroot/mod/folder/lib.php"); 586 $this->assertFileExists("$CFG->dirroot/mod/glossary/lib.php"); 587 588 $course1 = $this->getDataGenerator()->create_course(); 589 $course2 = $this->getDataGenerator()->create_course(); 590 $course3 = $this->getDataGenerator()->create_course(); 591 592 $folder1a = $this->getDataGenerator()->create_module('folder', array('course' => $course1, 'section' => 3)); 593 $folder1b = $this->getDataGenerator()->create_module('folder', array('course' => $course1)); 594 $glossary1 = $this->getDataGenerator()->create_module('glossary', array('course' => $course1)); 595 596 $folder2 = $this->getDataGenerator()->create_module('folder', array('course' => $course2)); 597 $glossary2a = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 598 $glossary2b = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 599 600 $folder3 = $this->getDataGenerator()->create_module('folder', array('course' => $course3)); 601 602 $modules = get_all_instances_in_courses('folder', array($course1->id => $course1, $course2->id => $course2)); 603 $this->assertCount(3, $modules); 604 605 foreach ($modules as $cm) { 606 if ($folder1a->cmid == $cm->coursemodule) { 607 $folder = $folder1a; 608 } else if ($folder1b->cmid == $cm->coursemodule) { 609 $folder = $folder1b; 610 } else if ($folder2->cmid == $cm->coursemodule) { 611 $folder = $folder2; 612 } else { 613 $this->fail('Unexpected cm'. $cm->coursemodule); 614 } 615 $this->assertSame($folder->name, $cm->name); 616 $this->assertSame($folder->course, $cm->course); 617 } 618 619 try { 620 get_all_instances_in_courses('a b', array($course1->id => $course1, $course2->id => $course2)); 621 $this->fail('coding_exception expected'); 622 } catch (moodle_exception $e) { 623 $this->assertInstanceOf('coding_exception', $e); 624 } 625 626 try { 627 get_all_instances_in_courses('', array($course1->id => $course1, $course2->id => $course2)); 628 $this->fail('coding_exception expected'); 629 } catch (moodle_exception $e) { 630 $this->assertInstanceOf('coding_exception', $e); 631 } 632 } 633 634 public function test_get_all_instances_in_course() { 635 global $CFG; 636 637 $this->resetAfterTest(); 638 $this->setAdminUser(); // Some generators have bogus access control. 639 640 $this->assertFileExists("$CFG->dirroot/mod/folder/lib.php"); 641 $this->assertFileExists("$CFG->dirroot/mod/glossary/lib.php"); 642 643 $course1 = $this->getDataGenerator()->create_course(); 644 $course2 = $this->getDataGenerator()->create_course(); 645 $course3 = $this->getDataGenerator()->create_course(); 646 647 $folder1a = $this->getDataGenerator()->create_module('folder', array('course' => $course1, 'section' => 3)); 648 $folder1b = $this->getDataGenerator()->create_module('folder', array('course' => $course1)); 649 $glossary1 = $this->getDataGenerator()->create_module('glossary', array('course' => $course1)); 650 651 $folder2 = $this->getDataGenerator()->create_module('folder', array('course' => $course2)); 652 $glossary2a = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 653 $glossary2b = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 654 655 $folder3 = $this->getDataGenerator()->create_module('folder', array('course' => $course3)); 656 657 $modules = get_all_instances_in_course('folder', $course1); 658 $this->assertCount(2, $modules); 659 660 foreach ($modules as $cm) { 661 if ($folder1a->cmid == $cm->coursemodule) { 662 $folder = $folder1a; 663 } else if ($folder1b->cmid == $cm->coursemodule) { 664 $folder = $folder1b; 665 } else { 666 $this->fail('Unexpected cm'. $cm->coursemodule); 667 } 668 $this->assertSame($folder->name, $cm->name); 669 $this->assertSame($folder->course, $cm->course); 670 } 671 672 try { 673 get_all_instances_in_course('a b', $course1); 674 $this->fail('coding_exception expected'); 675 } catch (moodle_exception $e) { 676 $this->assertInstanceOf('coding_exception', $e); 677 } 678 679 try { 680 get_all_instances_in_course('', $course1); 681 $this->fail('coding_exception expected'); 682 } catch (moodle_exception $e) { 683 $this->assertInstanceOf('coding_exception', $e); 684 } 685 } 686 687 /** 688 * Test max courses in category 689 */ 690 public function test_max_courses_in_category() { 691 global $CFG; 692 $this->resetAfterTest(); 693 694 // Default settings. 695 $this->assertEquals(MAX_COURSES_IN_CATEGORY, get_max_courses_in_category()); 696 697 // Misc category. 698 $misc = core_course_category::get_default(); 699 $this->assertEquals(MAX_COURSES_IN_CATEGORY, $misc->sortorder); 700 701 $category1 = $this->getDataGenerator()->create_category(); 702 $category2 = $this->getDataGenerator()->create_category(); 703 704 // Check category sort orders. 705 $this->assertEquals(MAX_COURSES_IN_CATEGORY, core_course_category::get($misc->id)->sortorder); 706 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 2, core_course_category::get($category1->id)->sortorder); 707 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 3, core_course_category::get($category2->id)->sortorder); 708 709 // Create courses. 710 $course1 = $this->getDataGenerator()->create_course(['category' => $category1->id]); 711 $course2 = $this->getDataGenerator()->create_course(['category' => $category2->id]); 712 $course3 = $this->getDataGenerator()->create_course(['category' => $category1->id]); 713 $course4 = $this->getDataGenerator()->create_course(['category' => $category2->id]); 714 715 // Check course sort orders. 716 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 2 + 2, get_course($course1->id)->sortorder); 717 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 3 + 2, get_course($course2->id)->sortorder); 718 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 2 + 1, get_course($course3->id)->sortorder); 719 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 3 + 1, get_course($course4->id)->sortorder); 720 721 // Increase max course in category. 722 $CFG->maxcoursesincategory = 20000; 723 $this->assertEquals(20000, get_max_courses_in_category()); 724 725 // The sort order has not yet fixed, these sort orders should be the same as before. 726 // Categories. 727 $this->assertEquals(MAX_COURSES_IN_CATEGORY, core_course_category::get($misc->id)->sortorder); 728 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 2, core_course_category::get($category1->id)->sortorder); 729 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 3, core_course_category::get($category2->id)->sortorder); 730 // Courses in category 1. 731 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 2 + 2, get_course($course1->id)->sortorder); 732 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 2 + 1, get_course($course3->id)->sortorder); 733 // Courses in category 2. 734 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 3 + 2, get_course($course2->id)->sortorder); 735 $this->assertEquals(MAX_COURSES_IN_CATEGORY * 3 + 1, get_course($course4->id)->sortorder); 736 737 // Create new category so that the sort orders are applied. 738 $category3 = $this->getDataGenerator()->create_category(); 739 // Categories. 740 $this->assertEquals(20000, core_course_category::get($misc->id)->sortorder); 741 $this->assertEquals(20000 * 2, core_course_category::get($category1->id)->sortorder); 742 $this->assertEquals(20000 * 3, core_course_category::get($category2->id)->sortorder); 743 $this->assertEquals(20000 * 4, core_course_category::get($category3->id)->sortorder); 744 // Courses in category 1. 745 $this->assertEquals(20000 * 2 + 2, get_course($course1->id)->sortorder); 746 $this->assertEquals(20000 * 2 + 1, get_course($course3->id)->sortorder); 747 // Courses in category 2. 748 $this->assertEquals(20000 * 3 + 2, get_course($course2->id)->sortorder); 749 $this->assertEquals(20000 * 3 + 1, get_course($course4->id)->sortorder); 750 } 751 752 /** 753 * Test debug message for max courses in category 754 */ 755 public function test_debug_max_courses_in_category() { 756 global $CFG; 757 $this->resetAfterTest(); 758 759 // Set to small value so that we can check the debug message. 760 $CFG->maxcoursesincategory = 3; 761 $this->assertEquals(3, get_max_courses_in_category()); 762 763 $category1 = $this->getDataGenerator()->create_category(); 764 765 // There is only one course, no debug message. 766 $this->getDataGenerator()->create_course(['category' => $category1->id]); 767 $this->assertDebuggingNotCalled(); 768 // There are two courses, no debug message. 769 $this->getDataGenerator()->create_course(['category' => $category1->id]); 770 $this->assertDebuggingNotCalled(); 771 // There is debug message when number of courses reaches the maximum number. 772 $this->getDataGenerator()->create_course(['category' => $category1->id]); 773 $this->assertDebuggingCalled("The number of courses (category id: $category1->id) has reached max number of courses " . 774 "in a category (" . get_max_courses_in_category() . "). It will cause a sorting performance issue. " . 775 "Please set higher value for \$CFG->maxcoursesincategory in config.php. " . 776 "Please also make sure \$CFG->maxcoursesincategory * MAX_COURSE_CATEGORIES less than max integer. " . 777 "See tracker issues: MDL-25669 and MDL-69573"); 778 } 779 780 /** 781 * Data provider for test_get_safe_orderby(). 782 * 783 * @return array 784 */ 785 public function get_safe_orderby_provider(): array { 786 $orderbymap = [ 787 'courseid' => 'c.id', 788 'somecustomvalue' => 'c.startdate, c.shortname', 789 'default' => 'c.fullname', 790 ]; 791 $orderbymapnodefault = [ 792 'courseid' => 'c.id', 793 'somecustomvalue' => 'c.startdate, c.shortname', 794 ]; 795 796 return [ 797 'Valid option, no direction specified' => [ 798 $orderbymap, 799 'somecustomvalue', 800 '', 801 ' ORDER BY c.startdate, c.shortname', 802 ], 803 'Valid option, valid direction specified' => [ 804 $orderbymap, 805 'courseid', 806 'DESC', 807 ' ORDER BY c.id DESC', 808 ], 809 'Valid option, valid lowercase direction specified' => [ 810 $orderbymap, 811 'courseid', 812 'asc', 813 ' ORDER BY c.id ASC', 814 ], 815 'Valid option, invalid direction specified' => [ 816 $orderbymap, 817 'courseid', 818 'BOOP', 819 ' ORDER BY c.id', 820 ], 821 'Valid option, invalid lowercase direction specified' => [ 822 $orderbymap, 823 'courseid', 824 'boop', 825 ' ORDER BY c.id', 826 ], 827 'Invalid option default fallback, with valid direction' => [ 828 $orderbymap, 829 'thisdoesnotexist', 830 'ASC', 831 ' ORDER BY c.fullname ASC', 832 ], 833 'Invalid option default fallback, with invalid direction' => [ 834 $orderbymap, 835 'thisdoesnotexist', 836 'BOOP', 837 ' ORDER BY c.fullname', 838 ], 839 'Invalid option without default, with valid direction' => [ 840 $orderbymapnodefault, 841 'thisdoesnotexist', 842 'ASC', 843 '', 844 ], 845 'Invalid option without default, with invalid direction' => [ 846 $orderbymapnodefault, 847 'thisdoesnotexist', 848 'NOPE', 849 '', 850 ], 851 ]; 852 } 853 854 /** 855 * Tests the get_safe_orderby function. 856 * 857 * @dataProvider get_safe_orderby_provider 858 * @param array $orderbymap The ORDER BY parameter mapping array. 859 * @param string $orderbykey The string key being provided, to check against the map. 860 * @param string $direction The optional direction to order by. 861 * @param string $expected The expected string output of the method. 862 */ 863 public function test_get_safe_orderby(array $orderbymap, string $orderbykey, string $direction, string $expected): void { 864 $actual = get_safe_orderby($orderbymap, $orderbykey, $direction); 865 $this->assertEquals($expected, $actual); 866 } 867 868 /** 869 * Data provider for test_get_safe_orderby_multiple(). 870 * 871 * @return array 872 */ 873 public function get_safe_orderby_multiple_provider(): array { 874 $orderbymap = [ 875 'courseid' => 'c.id', 876 'firstname' => 'u.firstname', 877 'default' => 'c.startdate', 878 ]; 879 $orderbymapnodefault = [ 880 'courseid' => 'c.id', 881 'firstname' => 'u.firstname', 882 ]; 883 884 return [ 885 'Valid options, no directions specified' => [ 886 $orderbymap, 887 ['courseid', 'firstname'], 888 [], 889 ' ORDER BY c.id, u.firstname', 890 ], 891 'Valid options, some direction specified' => [ 892 $orderbymap, 893 ['courseid', 'firstname'], 894 ['DESC'], 895 ' ORDER BY c.id DESC, u.firstname', 896 ], 897 'Valid options, all directions specified' => [ 898 $orderbymap, 899 ['courseid', 'firstname'], 900 ['ASC', 'desc'], 901 ' ORDER BY c.id ASC, u.firstname DESC', 902 ], 903 'Valid options, valid and invalid directions specified' => [ 904 $orderbymap, 905 ['courseid', 'firstname'], 906 ['BOOP', 'DESC'], 907 ' ORDER BY c.id, u.firstname DESC', 908 ], 909 'Valid options, all invalid directions specified' => [ 910 $orderbymap, 911 ['courseid', 'firstname'], 912 ['BOOP', 'SNOOT'], 913 ' ORDER BY c.id, u.firstname', 914 ], 915 'Valid and invalid option default fallback, with valid directions' => [ 916 $orderbymap, 917 ['thisdoesnotexist', 'courseid'], 918 ['asc', 'DESC'], 919 ' ORDER BY c.startdate ASC, c.id DESC', 920 ], 921 'Valid and invalid option default fallback, with invalid direction' => [ 922 $orderbymap, 923 ['courseid', 'thisdoesnotexist'], 924 ['BOOP', 'SNOOT'], 925 ' ORDER BY c.id, c.startdate', 926 ], 927 'Valid and invalid option without default, with valid direction' => [ 928 $orderbymapnodefault, 929 ['thisdoesnotexist', 'courseid'], 930 ['ASC', 'DESC'], 931 ' ORDER BY c.id DESC', 932 ], 933 'Valid and invalid option without default, with invalid direction' => [ 934 $orderbymapnodefault, 935 ['thisdoesnotexist', 'courseid'], 936 ['BOOP', 'SNOOT'], 937 ' ORDER BY c.id', 938 ], 939 'Invalid option only without default, with valid direction' => [ 940 $orderbymapnodefault, 941 ['thisdoesnotexist'], 942 ['ASC'], 943 '', 944 ], 945 'Invalid option only without default, with invalid direction' => [ 946 $orderbymapnodefault, 947 ['thisdoesnotexist'], 948 ['BOOP'], 949 '', 950 ], 951 'Single valid option, direction specified' => [ 952 $orderbymap, 953 ['firstname'], 954 ['ASC'], 955 ' ORDER BY u.firstname ASC', 956 ], 957 'Single valid option, direction not specified' => [ 958 $orderbymap, 959 ['firstname'], 960 [], 961 ' ORDER BY u.firstname', 962 ], 963 ]; 964 } 965 966 /** 967 * Tests the get_safe_orderby_multiple function. 968 * 969 * @dataProvider get_safe_orderby_multiple_provider 970 * @param array $orderbymap The ORDER BY parameter mapping array. 971 * @param array $orderbykeys The array of string keys being provided, to check against the map. 972 * @param array $directions The optional directions to order by. 973 * @param string $expected The expected string output of the method. 974 */ 975 public function test_get_safe_orderby_multiple(array $orderbymap, array $orderbykeys, array $directions, 976 string $expected): void { 977 $actual = get_safe_orderby_multiple($orderbymap, $orderbykeys, $directions); 978 $this->assertEquals($expected, $actual); 979 } 980 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body