See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 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 /** 18 * core_component related tests. 19 * 20 * @package core 21 * @category phpunit 22 * @copyright 2013 Petr Skoda {@link http://skodak.org} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 class component_test extends advanced_testcase { 26 27 /** 28 * To be changed if number of subsystems increases/decreases, 29 * this is defined here to annoy devs that try to add more without any thinking, 30 * always verify that it does not collide with any existing add-on modules and subplugins!!! 31 */ 32 const SUBSYSTEMCOUNT = 75; 33 34 public function setUp(): void { 35 $psr0namespaces = new ReflectionProperty('core_component', 'psr0namespaces'); 36 $psr0namespaces->setAccessible(true); 37 $this->oldpsr0namespaces = $psr0namespaces->getValue(null); 38 39 $psr4namespaces = new ReflectionProperty('core_component', 'psr4namespaces'); 40 $psr4namespaces->setAccessible(true); 41 $this->oldpsr4namespaces = $psr4namespaces->getValue(null); 42 } 43 public function tearDown(): void { 44 $psr0namespaces = new ReflectionProperty('core_component', 'psr0namespaces'); 45 $psr0namespaces->setAccessible(true); 46 $psr0namespaces->setValue(null, $this->oldpsr0namespaces); 47 48 $psr4namespaces = new ReflectionProperty('core_component', 'psr4namespaces'); 49 $psr4namespaces->setAccessible(true); 50 $psr4namespaces->setValue(null, $this->oldpsr4namespaces); 51 } 52 53 public function test_get_core_subsystems() { 54 global $CFG; 55 56 $subsystems = core_component::get_core_subsystems(); 57 58 $this->assertCount(self::SUBSYSTEMCOUNT, $subsystems, 'Oh, somebody added or removed a core subsystem, think twice before doing that!'); 59 60 // Make sure all paths are full/null, exist and are inside dirroot. 61 foreach ($subsystems as $subsystem => $fulldir) { 62 $this->assertFalse(strpos($subsystem, '_'), 'Core subsystems must be one work without underscores'); 63 if ($fulldir === null) { 64 if ($subsystem === 'filepicker' or $subsystem === 'help') { 65 // Arrgghh, let's not introduce more subsystems for no real reason... 66 } else { 67 // Lang strings. 68 $this->assertFileExists("$CFG->dirroot/lang/en/$subsystem.php", 'Core subsystems without fulldir are usually used for lang strings.'); 69 } 70 continue; 71 } 72 $this->assertFileExists($fulldir); 73 // Check that base uses realpath() separators and "/" in the subdirs. 74 $this->assertStringStartsWith($CFG->dirroot.'/', $fulldir); 75 $reldir = substr($fulldir, strlen($CFG->dirroot)+1); 76 $this->assertFalse(strpos($reldir, '\\')); 77 } 78 79 // Make sure all core language files are also subsystems! 80 $items = new DirectoryIterator("$CFG->dirroot/lang/en"); 81 foreach ($items as $item) { 82 if ($item->isDot() or $item->isDir()) { 83 continue; 84 } 85 $file = $item->getFilename(); 86 if ($file === 'moodle.php') { 87 // Do not add new lang strings unless really necessary!!! 88 continue; 89 } 90 91 if (substr($file, -4) !== '.php') { 92 continue; 93 } 94 $file = substr($file, 0, strlen($file)-4); 95 $this->assertArrayHasKey($file, $subsystems, 'All core lang files should be subsystems, think twice before adding anything!'); 96 } 97 unset($item); 98 unset($items); 99 100 } 101 102 public function test_deprecated_get_core_subsystems() { 103 global $CFG; 104 105 $subsystems = core_component::get_core_subsystems(); 106 107 $this->assertSame($subsystems, get_core_subsystems(true)); 108 109 $realsubsystems = get_core_subsystems(); 110 $this->assertDebuggingCalled(); 111 $this->assertSame($realsubsystems, get_core_subsystems(false)); 112 $this->assertDebuggingCalled(); 113 114 $this->assertEquals(count($subsystems), count($realsubsystems)); 115 116 foreach ($subsystems as $subsystem => $fulldir) { 117 $this->assertArrayHasKey($subsystem, $realsubsystems); 118 if ($fulldir === null) { 119 $this->assertNull($realsubsystems[$subsystem]); 120 continue; 121 } 122 $this->assertSame($fulldir, $CFG->dirroot.'/'.$realsubsystems[$subsystem]); 123 } 124 } 125 126 public function test_get_plugin_types() { 127 global $CFG; 128 129 $this->assertTrue(empty($CFG->themedir), 'Non-empty $CFG->themedir is not covered by any tests yet, you need to disable it.'); 130 131 $plugintypes = core_component::get_plugin_types(); 132 133 foreach ($plugintypes as $plugintype => $fulldir) { 134 $this->assertStringStartsWith("$CFG->dirroot/", $fulldir); 135 } 136 } 137 138 public function test_deprecated_get_plugin_types() { 139 global $CFG; 140 141 $plugintypes = core_component::get_plugin_types(); 142 143 $this->assertSame($plugintypes, get_plugin_types()); 144 $this->assertSame($plugintypes, get_plugin_types(true)); 145 146 $realplugintypes = get_plugin_types(false); 147 $this->assertDebuggingCalled(); 148 149 foreach ($plugintypes as $plugintype => $fulldir) { 150 $this->assertSame($fulldir, $CFG->dirroot.'/'.$realplugintypes[$plugintype]); 151 } 152 } 153 154 public function test_get_plugin_list() { 155 global $CFG; 156 157 $plugintypes = core_component::get_plugin_types(); 158 159 foreach ($plugintypes as $plugintype => $fulldir) { 160 $plugins = core_component::get_plugin_list($plugintype); 161 foreach ($plugins as $pluginname => $plugindir) { 162 $this->assertStringStartsWith("$CFG->dirroot/", $plugindir); 163 } 164 if ($plugintype !== 'auth') { 165 // Let's crosscheck it with independent implementation (auth/db is an exception). 166 $reldir = substr($fulldir, strlen($CFG->dirroot)+1); 167 $dirs = get_list_of_plugins($reldir); 168 $dirs = array_values($dirs); 169 $this->assertDebuggingCalled(); 170 $this->assertSame($dirs, array_keys($plugins)); 171 } 172 } 173 } 174 175 public function test_deprecated_get_plugin_list() { 176 $plugintypes = core_component::get_plugin_types(); 177 178 foreach ($plugintypes as $plugintype => $fulldir) { 179 $plugins = core_component::get_plugin_list($plugintype); 180 $this->assertSame($plugins, get_plugin_list($plugintype)); 181 } 182 } 183 184 public function test_get_plugin_directory() { 185 $plugintypes = core_component::get_plugin_types(); 186 187 foreach ($plugintypes as $plugintype => $fulldir) { 188 $plugins = core_component::get_plugin_list($plugintype); 189 foreach ($plugins as $pluginname => $plugindir) { 190 $this->assertSame($plugindir, core_component::get_plugin_directory($plugintype, $pluginname)); 191 } 192 } 193 } 194 195 public function test_deprecated_get_plugin_directory() { 196 $plugintypes = core_component::get_plugin_types(); 197 198 foreach ($plugintypes as $plugintype => $fulldir) { 199 $plugins = core_component::get_plugin_list($plugintype); 200 foreach ($plugins as $pluginname => $plugindir) { 201 $this->assertSame(core_component::get_plugin_directory($plugintype, $pluginname), get_plugin_directory($plugintype, $pluginname)); 202 } 203 } 204 } 205 206 public function test_get_subsystem_directory() { 207 $subsystems = core_component::get_core_subsystems(); 208 foreach ($subsystems as $subsystem => $fulldir) { 209 $this->assertSame($fulldir, core_component::get_subsystem_directory($subsystem)); 210 } 211 } 212 213 /** 214 * Test that the get_plugin_list_with_file() function returns the correct list of plugins. 215 * 216 * @covers \core_component::is_valid_plugin_name 217 * @dataProvider is_valid_plugin_name_provider 218 * @param array $arguments 219 * @param bool $expected 220 */ 221 public function test_is_valid_plugin_name(array $arguments, bool $expected): void { 222 $this->assertEquals($expected, core_component::is_valid_plugin_name(...$arguments)); 223 } 224 225 /** 226 * Data provider for the is_valid_plugin_name function. 227 * 228 * @return array 229 */ 230 public function is_valid_plugin_name_provider(): array { 231 return [ 232 [['mod', 'example1'], true], 233 [['mod', 'feedback360'], true], 234 [['mod', 'feedback_360'], false], 235 [['mod', '2feedback'], false], 236 [['mod', '1example'], false], 237 [['mod', 'example.xx'], false], 238 [['mod', '.example'], false], 239 [['mod', '_example'], false], 240 [['mod', 'example_'], false], 241 [['mod', 'example_x1'], false], 242 [['mod', 'example-x1'], false], 243 [['mod', 'role'], false], 244 245 [['tool', 'example1'], true], 246 [['tool', 'example_x1'], true], 247 [['tool', 'example_x1_xxx'], true], 248 [['tool', 'feedback360'], true], 249 [['tool', 'feed_back360'], true], 250 [['tool', 'role'], true], 251 [['tool', '1example'], false], 252 [['tool', 'example.xx'], false], 253 [['tool', 'example-xx'], false], 254 [['tool', '.example'], false], 255 [['tool', '_example'], false], 256 [['tool', 'example_'], false], 257 [['tool', 'example__x1'], false], 258 259 // Some invalid cases. 260 [['mod', null], false], 261 [['mod', ''], false], 262 [['tool', null], false], 263 [['tool', ''], false], 264 ]; 265 } 266 267 public function test_normalize_componentname() { 268 // Moodle core. 269 $this->assertSame('core', core_component::normalize_componentname('core')); 270 $this->assertSame('core', core_component::normalize_componentname('moodle')); 271 $this->assertSame('core', core_component::normalize_componentname('')); 272 273 // Moodle core subsystems. 274 $this->assertSame('core_admin', core_component::normalize_componentname('admin')); 275 $this->assertSame('core_admin', core_component::normalize_componentname('core_admin')); 276 $this->assertSame('core_admin', core_component::normalize_componentname('moodle_admin')); 277 278 // Activity modules and their subplugins. 279 $this->assertSame('mod_workshop', core_component::normalize_componentname('workshop')); 280 $this->assertSame('mod_workshop', core_component::normalize_componentname('mod_workshop')); 281 $this->assertSame('workshopform_accumulative', core_component::normalize_componentname('workshopform_accumulative')); 282 $this->assertSame('mod_quiz', core_component::normalize_componentname('quiz')); 283 $this->assertSame('quiz_grading', core_component::normalize_componentname('quiz_grading')); 284 $this->assertSame('mod_data', core_component::normalize_componentname('data')); 285 $this->assertSame('datafield_checkbox', core_component::normalize_componentname('datafield_checkbox')); 286 287 // Other plugin types. 288 $this->assertSame('auth_mnet', core_component::normalize_componentname('auth_mnet')); 289 $this->assertSame('enrol_self', core_component::normalize_componentname('enrol_self')); 290 $this->assertSame('block_html', core_component::normalize_componentname('block_html')); 291 $this->assertSame('block_mnet_hosts', core_component::normalize_componentname('block_mnet_hosts')); 292 $this->assertSame('local_amos', core_component::normalize_componentname('local_amos')); 293 $this->assertSame('local_admin', core_component::normalize_componentname('local_admin')); 294 295 // Unknown words without underscore are supposed to be activity modules. 296 $this->assertSame('mod_whoonearthwouldcomewithsuchastupidnameofcomponent', 297 core_component::normalize_componentname('whoonearthwouldcomewithsuchastupidnameofcomponent')); 298 // Module names can not contain underscores, this must be a subplugin. 299 $this->assertSame('whoonearth_wouldcomewithsuchastupidnameofcomponent', 300 core_component::normalize_componentname('whoonearth_wouldcomewithsuchastupidnameofcomponent')); 301 $this->assertSame('whoonearth_would_come_withsuchastupidnameofcomponent', 302 core_component::normalize_componentname('whoonearth_would_come_withsuchastupidnameofcomponent')); 303 } 304 305 public function test_normalize_component() { 306 // Moodle core. 307 $this->assertSame(array('core', null), core_component::normalize_component('core')); 308 $this->assertSame(array('core', null), core_component::normalize_component('moodle')); 309 $this->assertSame(array('core', null), core_component::normalize_component('')); 310 311 // Moodle core subsystems. 312 $this->assertSame(array('core', 'admin'), core_component::normalize_component('admin')); 313 $this->assertSame(array('core', 'admin'), core_component::normalize_component('core_admin')); 314 $this->assertSame(array('core', 'admin'), core_component::normalize_component('moodle_admin')); 315 316 // Activity modules and their subplugins. 317 $this->assertSame(array('mod', 'workshop'), core_component::normalize_component('workshop')); 318 $this->assertSame(array('mod', 'workshop'), core_component::normalize_component('mod_workshop')); 319 $this->assertSame(array('workshopform', 'accumulative'), core_component::normalize_component('workshopform_accumulative')); 320 $this->assertSame(array('mod', 'quiz'), core_component::normalize_component('quiz')); 321 $this->assertSame(array('quiz', 'grading'), core_component::normalize_component('quiz_grading')); 322 $this->assertSame(array('mod', 'data'), core_component::normalize_component('data')); 323 $this->assertSame(array('datafield', 'checkbox'), core_component::normalize_component('datafield_checkbox')); 324 325 // Other plugin types. 326 $this->assertSame(array('auth', 'mnet'), core_component::normalize_component('auth_mnet')); 327 $this->assertSame(array('enrol', 'self'), core_component::normalize_component('enrol_self')); 328 $this->assertSame(array('block', 'html'), core_component::normalize_component('block_html')); 329 $this->assertSame(array('block', 'mnet_hosts'), core_component::normalize_component('block_mnet_hosts')); 330 $this->assertSame(array('local', 'amos'), core_component::normalize_component('local_amos')); 331 $this->assertSame(array('local', 'admin'), core_component::normalize_component('local_admin')); 332 333 // Unknown words without underscore are supposed to be activity modules. 334 $this->assertSame(array('mod', 'whoonearthwouldcomewithsuchastupidnameofcomponent'), 335 core_component::normalize_component('whoonearthwouldcomewithsuchastupidnameofcomponent')); 336 // Module names can not contain underscores, this must be a subplugin. 337 $this->assertSame(array('whoonearth', 'wouldcomewithsuchastupidnameofcomponent'), 338 core_component::normalize_component('whoonearth_wouldcomewithsuchastupidnameofcomponent')); 339 $this->assertSame(array('whoonearth', 'would_come_withsuchastupidnameofcomponent'), 340 core_component::normalize_component('whoonearth_would_come_withsuchastupidnameofcomponent')); 341 } 342 343 public function test_deprecated_normalize_component() { 344 // Moodle core. 345 $this->assertSame(array('core', null), normalize_component('core')); 346 $this->assertSame(array('core', null), normalize_component('')); 347 $this->assertSame(array('core', null), normalize_component('moodle')); 348 349 // Moodle core subsystems. 350 $this->assertSame(array('core', 'admin'), normalize_component('admin')); 351 $this->assertSame(array('core', 'admin'), normalize_component('core_admin')); 352 $this->assertSame(array('core', 'admin'), normalize_component('moodle_admin')); 353 354 // Activity modules and their subplugins. 355 $this->assertSame(array('mod', 'workshop'), normalize_component('workshop')); 356 $this->assertSame(array('mod', 'workshop'), normalize_component('mod_workshop')); 357 $this->assertSame(array('workshopform', 'accumulative'), normalize_component('workshopform_accumulative')); 358 $this->assertSame(array('mod', 'quiz'), normalize_component('quiz')); 359 $this->assertSame(array('quiz', 'grading'), normalize_component('quiz_grading')); 360 $this->assertSame(array('mod', 'data'), normalize_component('data')); 361 $this->assertSame(array('datafield', 'checkbox'), normalize_component('datafield_checkbox')); 362 363 // Other plugin types. 364 $this->assertSame(array('auth', 'mnet'), normalize_component('auth_mnet')); 365 $this->assertSame(array('enrol', 'self'), normalize_component('enrol_self')); 366 $this->assertSame(array('block', 'html'), normalize_component('block_html')); 367 $this->assertSame(array('block', 'mnet_hosts'), normalize_component('block_mnet_hosts')); 368 $this->assertSame(array('local', 'amos'), normalize_component('local_amos')); 369 $this->assertSame(array('local', 'admin'), normalize_component('local_admin')); 370 371 // Unknown words without underscore are supposed to be activity modules. 372 $this->assertSame(array('mod', 'whoonearthwouldcomewithsuchastupidnameofcomponent'), 373 normalize_component('whoonearthwouldcomewithsuchastupidnameofcomponent')); 374 // Module names can not contain underscores, this must be a subplugin. 375 $this->assertSame(array('whoonearth', 'wouldcomewithsuchastupidnameofcomponent'), 376 normalize_component('whoonearth_wouldcomewithsuchastupidnameofcomponent')); 377 $this->assertSame(array('whoonearth', 'would_come_withsuchastupidnameofcomponent'), 378 normalize_component('whoonearth_would_come_withsuchastupidnameofcomponent')); 379 } 380 381 public function test_get_component_directory() { 382 $plugintypes = core_component::get_plugin_types(); 383 foreach ($plugintypes as $plugintype => $fulldir) { 384 $plugins = core_component::get_plugin_list($plugintype); 385 foreach ($plugins as $pluginname => $plugindir) { 386 $this->assertSame($plugindir, core_component::get_component_directory(($plugintype.'_'.$pluginname))); 387 } 388 } 389 390 $subsystems = core_component::get_core_subsystems(); 391 foreach ($subsystems as $subsystem => $fulldir) { 392 $this->assertSame($fulldir, core_component::get_component_directory(('core_'.$subsystem))); 393 } 394 } 395 396 public function test_deprecated_get_component_directory() { 397 $plugintypes = core_component::get_plugin_types(); 398 foreach ($plugintypes as $plugintype => $fulldir) { 399 $plugins = core_component::get_plugin_list($plugintype); 400 foreach ($plugins as $pluginname => $plugindir) { 401 $this->assertSame($plugindir, get_component_directory(($plugintype.'_'.$pluginname))); 402 } 403 } 404 405 $subsystems = core_component::get_core_subsystems(); 406 foreach ($subsystems as $subsystem => $fulldir) { 407 $this->assertSame($fulldir, get_component_directory(('core_'.$subsystem))); 408 } 409 } 410 411 public function test_get_subtype_parent() { 412 global $CFG; 413 414 $this->assertNull(core_component::get_subtype_parent('mod')); 415 416 // Any plugin with more subtypes is ok here. 417 $this->assertFileExists("$CFG->dirroot/mod/assign/db/subplugins.json"); 418 $this->assertSame('mod_assign', core_component::get_subtype_parent('assignsubmission')); 419 $this->assertSame('mod_assign', core_component::get_subtype_parent('assignfeedback')); 420 $this->assertNull(core_component::get_subtype_parent('assignxxxxx')); 421 } 422 423 public function test_get_subplugins() { 424 global $CFG; 425 426 // Any plugin with more subtypes is ok here. 427 $this->assertFileExists("$CFG->dirroot/mod/assign/db/subplugins.json"); 428 429 $subplugins = core_component::get_subplugins('mod_assign'); 430 $this->assertSame(array('assignsubmission', 'assignfeedback'), array_keys($subplugins)); 431 432 $subs = core_component::get_plugin_list('assignsubmission'); 433 $feeds = core_component::get_plugin_list('assignfeedback'); 434 435 $this->assertSame(array_keys($subs), $subplugins['assignsubmission']); 436 $this->assertSame(array_keys($feeds), $subplugins['assignfeedback']); 437 438 // Any plugin without subtypes is ok here. 439 $this->assertFileExists("$CFG->dirroot/mod/choice"); 440 $this->assertFileDoesNotExist("$CFG->dirroot/mod/choice/db/subplugins.json"); 441 442 $this->assertNull(core_component::get_subplugins('mod_choice')); 443 444 $this->assertNull(core_component::get_subplugins('xxxx_yyyy')); 445 } 446 447 public function test_get_plugin_types_with_subplugins() { 448 global $CFG; 449 450 $types = core_component::get_plugin_types_with_subplugins(); 451 452 // Hardcode it here to detect if anybody hacks the code to include more subplugin types. 453 $expected = array( 454 'mod' => "$CFG->dirroot/mod", 455 'editor' => "$CFG->dirroot/lib/editor", 456 'tool' => "$CFG->dirroot/$CFG->admin/tool", 457 'local' => "$CFG->dirroot/local", 458 ); 459 460 $this->assertSame($expected, $types); 461 462 } 463 464 public function test_get_plugin_list_with_file() { 465 $this->resetAfterTest(true); 466 467 // No extra reset here because core_component reset automatically. 468 469 $expected = array(); 470 $reports = core_component::get_plugin_list('report'); 471 foreach ($reports as $name => $fulldir) { 472 if (file_exists("$fulldir/lib.php")) { 473 $expected[] = $name; 474 } 475 } 476 477 // Test cold. 478 $list = core_component::get_plugin_list_with_file('report', 'lib.php', false); 479 $this->assertEquals($expected, array_keys($list)); 480 481 // Test hot. 482 $list = core_component::get_plugin_list_with_file('report', 'lib.php', false); 483 $this->assertEquals($expected, array_keys($list)); 484 485 // Test with include. 486 $list = core_component::get_plugin_list_with_file('report', 'lib.php', true); 487 $this->assertEquals($expected, array_keys($list)); 488 489 // Test missing. 490 $list = core_component::get_plugin_list_with_file('report', 'idontexist.php', true); 491 $this->assertEquals(array(), array_keys($list)); 492 } 493 494 public function test_get_component_classes_in_namespace() { 495 496 // Unexisting. 497 $this->assertCount(0, core_component::get_component_classes_in_namespace('core_unexistingcomponent', 'something')); 498 $this->assertCount(0, core_component::get_component_classes_in_namespace('auth_cas', 'something')); 499 500 // Matches the last namespace level name not partials. 501 $this->assertCount(0, core_component::get_component_classes_in_namespace('auth_cas', 'tas')); 502 $this->assertCount(0, core_component::get_component_classes_in_namespace('core_user', 'course')); 503 $this->assertCount(0, core_component::get_component_classes_in_namespace('mod_forum', 'output\\emaildigest')); 504 $this->assertCount(0, core_component::get_component_classes_in_namespace('mod_forum', '\\output\\emaildigest')); 505 $this->assertCount(2, core_component::get_component_classes_in_namespace('mod_forum', 'output\\email')); 506 $this->assertCount(2, core_component::get_component_classes_in_namespace('mod_forum', '\\output\\email')); 507 $this->assertCount(2, core_component::get_component_classes_in_namespace('mod_forum', 'output\\email\\')); 508 $this->assertCount(2, core_component::get_component_classes_in_namespace('mod_forum', '\\output\\email\\')); 509 510 // Prefix with backslash if it doesn\'t come prefixed. 511 $this->assertCount(1, core_component::get_component_classes_in_namespace('auth_cas', 'task')); 512 $this->assertCount(1, core_component::get_component_classes_in_namespace('auth_cas', '\\task')); 513 514 // Core as a component works, the function can normalise the component name. 515 $this->assertCount(7, core_component::get_component_classes_in_namespace('core', 'update')); 516 $this->assertCount(7, core_component::get_component_classes_in_namespace('', 'update')); 517 $this->assertCount(7, core_component::get_component_classes_in_namespace('moodle', 'update')); 518 519 // Multiple levels. 520 $this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', '\\output\\myprofile\\')); 521 $this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', 'output\\myprofile\\')); 522 $this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', '\\output\\myprofile')); 523 $this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', 'output\\myprofile')); 524 525 // Without namespace it returns classes/ classes. 526 $this->assertCount(5, core_component::get_component_classes_in_namespace('tool_mobile', '')); 527 $this->assertCount(2, core_component::get_component_classes_in_namespace('tool_filetypes')); 528 529 // When no component is specified, classes are returned for the namespace in all components. 530 // (We don't assert exact amounts here as the count of `output` classes will change depending on plugins installed). 531 $this->assertGreaterThan( 532 count(\core_component::get_component_classes_in_namespace('core', 'output')), 533 count(\core_component::get_component_classes_in_namespace(null, 'output'))); 534 535 // Without either a component or namespace it returns an empty array. 536 $this->assertEmpty(\core_component::get_component_classes_in_namespace()); 537 $this->assertEmpty(\core_component::get_component_classes_in_namespace(null)); 538 $this->assertEmpty(\core_component::get_component_classes_in_namespace(null, '')); 539 } 540 541 /** 542 * Data provider for classloader test 543 */ 544 public function classloader_provider() { 545 global $CFG; 546 547 // As part of these tests, we Check that there are no unexpected problems with overlapping PSR namespaces. 548 // This is not in the spec, but may come up in some libraries using both namespaces and PEAR-style class names. 549 // If problems arise we can remove this test, but will need to add a warning. 550 // Normalise to forward slash for testing purposes. 551 $directory = str_replace('\\', '/', $CFG->dirroot) . "/lib/tests/fixtures/component/"; 552 553 $psr0 = [ 554 'psr0' => 'lib/tests/fixtures/component/psr0', 555 'overlap' => 'lib/tests/fixtures/component/overlap' 556 ]; 557 $psr4 = [ 558 'psr4' => 'lib/tests/fixtures/component/psr4', 559 'overlap' => 'lib/tests/fixtures/component/overlap' 560 ]; 561 return [ 562 'PSR-0 Classloading - Root' => [ 563 'psr0' => $psr0, 564 'psr4' => $psr4, 565 'classname' => 'psr0_main', 566 'includedfiles' => "{$directory}psr0/main.php", 567 ], 568 'PSR-0 Classloading - Sub namespace - underscores' => [ 569 'psr0' => $psr0, 570 'psr4' => $psr4, 571 'classname' => 'psr0_subnamespace_example', 572 'includedfiles' => "{$directory}psr0/subnamespace/example.php", 573 ], 574 'PSR-0 Classloading - Sub namespace - slashes' => [ 575 'psr0' => $psr0, 576 'psr4' => $psr4, 577 'classname' => 'psr0\\subnamespace\\slashes', 578 'includedfiles' => "{$directory}psr0/subnamespace/slashes.php", 579 ], 580 'PSR-4 Classloading - Root' => [ 581 'psr0' => $psr0, 582 'psr4' => $psr4, 583 'classname' => 'psr4\\main', 584 'includedfiles' => "{$directory}psr4/main.php", 585 ], 586 'PSR-4 Classloading - Sub namespace' => [ 587 'psr0' => $psr0, 588 'psr4' => $psr4, 589 'classname' => 'psr4\\subnamespace\\example', 590 'includedfiles' => "{$directory}psr4/subnamespace/example.php", 591 ], 592 'PSR-4 Classloading - Ensure underscores are not converted to paths' => [ 593 'psr0' => $psr0, 594 'psr4' => $psr4, 595 'classname' => 'psr4\\subnamespace\\underscore_example', 596 'includedfiles' => "{$directory}psr4/subnamespace/underscore_example.php", 597 ], 598 'Overlap - Ensure no unexpected problems with PSR-4 when overlapping namespaces.' => [ 599 'psr0' => $psr0, 600 'psr4' => $psr4, 601 'classname' => 'overlap\\subnamespace\\example', 602 'includedfiles' => "{$directory}overlap/subnamespace/example.php", 603 ], 604 'Overlap - Ensure no unexpected problems with PSR-0 overlapping namespaces.' => [ 605 'psr0' => $psr0, 606 'psr4' => $psr4, 607 'classname' => 'overlap_subnamespace_example2', 608 'includedfiles' => "{$directory}overlap/subnamespace/example2.php", 609 ], 610 ]; 611 } 612 613 /** 614 * Test the classloader. 615 * 616 * @dataProvider classloader_provider 617 * @param array $psr0 The PSR-0 namespaces to be used in the test. 618 * @param array $psr4 The PSR-4 namespaces to be used in the test. 619 * @param string $classname The name of the class to attempt to load. 620 * @param string $includedfiles The file expected to be loaded. 621 */ 622 public function test_classloader($psr0, $psr4, $classname, $includedfiles) { 623 $psr0namespaces = new ReflectionProperty('core_component', 'psr0namespaces'); 624 $psr0namespaces->setAccessible(true); 625 $psr0namespaces->setValue(null, $psr0); 626 627 $psr4namespaces = new ReflectionProperty('core_component', 'psr4namespaces'); 628 $psr4namespaces->setAccessible(true); 629 $psr4namespaces->setValue(null, $psr4); 630 631 core_component::classloader($classname); 632 if (DIRECTORY_SEPARATOR != '/') { 633 // Denormalise the expected path so that we can quickly compare with get_included_files. 634 $includedfiles = str_replace('/', DIRECTORY_SEPARATOR, $includedfiles); 635 } 636 $this->assertContains($includedfiles, get_included_files()); 637 $this->assertTrue(class_exists($classname, false)); 638 } 639 640 /** 641 * Data provider for psr_classloader test 642 */ 643 public function psr_classloader_provider() { 644 global $CFG; 645 646 // As part of these tests, we Check that there are no unexpected problems with overlapping PSR namespaces. 647 // This is not in the spec, but may come up in some libraries using both namespaces and PEAR-style class names. 648 // If problems arise we can remove this test, but will need to add a warning. 649 // Normalise to forward slash for testing purposes. 650 $directory = str_replace('\\', '/', $CFG->dirroot) . "/lib/tests/fixtures/component/"; 651 652 $psr0 = [ 653 'psr0' => 'lib/tests/fixtures/component/psr0', 654 'overlap' => 'lib/tests/fixtures/component/overlap' 655 ]; 656 $psr4 = [ 657 'psr4' => 'lib/tests/fixtures/component/psr4', 658 'overlap' => 'lib/tests/fixtures/component/overlap' 659 ]; 660 return [ 661 'PSR-0 Classloading - Root' => [ 662 'psr0' => $psr0, 663 'psr4' => $psr4, 664 'classname' => 'psr0_main', 665 'file' => "{$directory}psr0/main.php", 666 ], 667 'PSR-0 Classloading - Sub namespace - underscores' => [ 668 'psr0' => $psr0, 669 'psr4' => $psr4, 670 'classname' => 'psr0_subnamespace_example', 671 'file' => "{$directory}psr0/subnamespace/example.php", 672 ], 673 'PSR-0 Classloading - Sub namespace - slashes' => [ 674 'psr0' => $psr0, 675 'psr4' => $psr4, 676 'classname' => 'psr0\\subnamespace\\slashes', 677 'file' => "{$directory}psr0/subnamespace/slashes.php", 678 ], 679 'PSR-0 Classloading - non-existant file' => [ 680 'psr0' => $psr0, 681 'psr4' => $psr4, 682 'classname' => 'psr0_subnamespace_nonexistant_file', 683 'file' => false, 684 ], 685 'PSR-4 Classloading - Root' => [ 686 'psr0' => $psr0, 687 'psr4' => $psr4, 688 'classname' => 'psr4\\main', 689 'file' => "{$directory}psr4/main.php", 690 ], 691 'PSR-4 Classloading - Sub namespace' => [ 692 'psr0' => $psr0, 693 'psr4' => $psr4, 694 'classname' => 'psr4\\subnamespace\\example', 695 'file' => "{$directory}psr4/subnamespace/example.php", 696 ], 697 'PSR-4 Classloading - Ensure underscores are not converted to paths' => [ 698 'psr0' => $psr0, 699 'psr4' => $psr4, 700 'classname' => 'psr4\\subnamespace\\underscore_example', 701 'file' => "{$directory}psr4/subnamespace/underscore_example.php", 702 ], 703 'PSR-4 Classloading - non-existant file' => [ 704 'psr0' => $psr0, 705 'psr4' => $psr4, 706 'classname' => 'psr4\\subnamespace\\nonexistant', 707 'file' => false, 708 ], 709 'Overlap - Ensure no unexpected problems with PSR-4 when overlapping namespaces.' => [ 710 'psr0' => $psr0, 711 'psr4' => $psr4, 712 'classname' => 'overlap\\subnamespace\\example', 713 'file' => "{$directory}overlap/subnamespace/example.php", 714 ], 715 'Overlap - Ensure no unexpected problems with PSR-0 overlapping namespaces.' => [ 716 'psr0' => $psr0, 717 'psr4' => $psr4, 718 'classname' => 'overlap_subnamespace_example2', 719 'file' => "{$directory}overlap/subnamespace/example2.php", 720 ], 721 ]; 722 } 723 724 /** 725 * Test the PSR classloader. 726 * 727 * @dataProvider psr_classloader_provider 728 * @param array $psr0 The PSR-0 namespaces to be used in the test. 729 * @param array $psr4 The PSR-4 namespaces to be used in the test. 730 * @param string $classname The name of the class to attempt to load. 731 * @param string|bool $file The expected file corresponding to the class or false for nonexistant. 732 */ 733 public function test_psr_classloader($psr0, $psr4, $classname, $file) { 734 $psr0namespaces = new ReflectionProperty('core_component', 'psr0namespaces'); 735 $psr0namespaces->setAccessible(true); 736 $psr0namespaces->setValue(null, $psr0); 737 738 $psr4namespaces = new ReflectionProperty('core_component', 'psr4namespaces'); 739 $psr4namespaces->setAccessible(true); 740 $oldpsr4namespaces = $psr4namespaces->getValue(null); 741 $psr4namespaces->setValue(null, $psr4); 742 743 $component = new ReflectionClass('core_component'); 744 $psrclassloader = $component->getMethod('psr_classloader'); 745 $psrclassloader->setAccessible(true); 746 747 $returnvalue = $psrclassloader->invokeArgs(null, array($classname)); 748 // Normalise to forward slashes for testing comparison. 749 if ($returnvalue) { 750 $returnvalue = str_replace('\\', '/', $returnvalue); 751 } 752 $this->assertEquals($file, $returnvalue); 753 } 754 755 /** 756 * Data provider for get_class_file test 757 */ 758 public function get_class_file_provider() { 759 global $CFG; 760 761 return [ 762 'Getting a file with underscores' => [ 763 'classname' => 'Test_With_Underscores', 764 'prefix' => "Test", 765 'path' => 'test/src', 766 'separators' => ['_'], 767 'result' => $CFG->dirroot . "/test/src/With/Underscores.php", 768 ], 769 'Getting a file with slashes' => [ 770 'classname' => 'Test\\With\\Slashes', 771 'prefix' => "Test", 772 'path' => 'test/src', 773 'separators' => ['\\'], 774 'result' => $CFG->dirroot . "/test/src/With/Slashes.php", 775 ], 776 'Getting a file with multiple namespaces' => [ 777 'classname' => 'Test\\With\\Multiple\\Namespaces', 778 'prefix' => "Test\\With", 779 'path' => 'test/src', 780 'separators' => ['\\'], 781 'result' => $CFG->dirroot . "/test/src/Multiple/Namespaces.php", 782 ], 783 'Getting a file with multiple namespaces (non-existent)' => [ 784 'classname' => 'Nonexistant\\Namespace\\Test', 785 'prefix' => "Test", 786 'path' => 'test/src', 787 'separators' => ['\\'], 788 'result' => false, 789 ], 790 ]; 791 } 792 793 /** 794 * Test the PSR classloader. 795 * 796 * @dataProvider get_class_file_provider 797 * @param string $classname the name of the class. 798 * @param string $prefix The namespace prefix used to identify the base directory of the source files. 799 * @param string $path The relative path to the base directory of the source files. 800 * @param string[] $separators The characters that should be used for separating. 801 * @param string|bool $result The expected result to be returned from get_class_file. 802 */ 803 public function test_get_class_file($classname, $prefix, $path, $separators, $result) { 804 $component = new ReflectionClass('core_component'); 805 $psrclassloader = $component->getMethod('get_class_file'); 806 $psrclassloader->setAccessible(true); 807 808 $file = $psrclassloader->invokeArgs(null, array($classname, $prefix, $path, $separators)); 809 $this->assertEquals($result, $file); 810 } 811 812 /** 813 * Confirm the get_component_list method contains an entry for every component. 814 */ 815 public function test_get_component_list_contains_all_components() { 816 global $CFG; 817 $componentslist = \core_component::get_component_list(); 818 819 // We should have an entry for each plugin type, and one additional for 'core'. 820 $plugintypes = \core_component::get_plugin_types(); 821 $numelementsexpected = count($plugintypes) + 1; 822 $this->assertEquals($numelementsexpected, count($componentslist)); 823 824 // And an entry for each of the plugin types. 825 foreach (array_keys($plugintypes) as $plugintype) { 826 $this->assertArrayHasKey($plugintype, $componentslist); 827 } 828 829 // And finally, one for 'core'. 830 $this->assertArrayHasKey('core', $componentslist); 831 832 // Check a few of the known plugin types to confirm their presence at their respective type index. 833 $this->assertEquals($componentslist['core']['core_comment'], $CFG->dirroot . '/comment'); 834 $this->assertEquals($componentslist['mod']['mod_forum'], $CFG->dirroot . '/mod/forum'); 835 $this->assertEquals($componentslist['tool']['tool_usertours'], $CFG->dirroot . '/' . $CFG->admin . '/tool/usertours'); 836 } 837 838 /** 839 * Test the get_component_names() method. 840 */ 841 public function test_get_component_names() { 842 global $CFG; 843 $componentnames = \core_component::get_component_names(); 844 845 // We should have an entry for each plugin type. 846 $plugintypes = \core_component::get_plugin_types(); 847 $numplugintypes = 0; 848 foreach ($plugintypes as $type => $typedir) { 849 foreach (\core_component::get_plugin_list($type) as $plugin) { 850 $numplugintypes++; 851 } 852 } 853 // And an entry for each core subsystem. 854 $numcomponents = $numplugintypes + count(\core_component::get_core_subsystems()); 855 856 $this->assertEquals($numcomponents, count($componentnames)); 857 858 // Check a few of the known plugin types to confirm their presence at their respective type index. 859 $this->assertContains('core_comment', $componentnames); 860 $this->assertContains('mod_forum', $componentnames); 861 $this->assertContains('tool_usertours', $componentnames); 862 $this->assertContains('core_favourites', $componentnames); 863 } 864 865 /** 866 * Test for monologo icons check in plugins. 867 * 868 * @covers core_component::has_monologo_icon 869 * @return void 870 */ 871 public function test_has_monologo_icon(): void { 872 // The Forum activity plugin has monologo icons. 873 $this->assertTrue(core_component::has_monologo_icon('mod', 'forum')); 874 // The core H5P subsystem doesn't have monologo icons. 875 $this->assertFalse(core_component::has_monologo_icon('core', 'h5p')); 876 // The function will return false for a non-existent component. 877 $this->assertFalse(core_component::has_monologo_icon('randomcomponent', 'h5p')); 878 } 879 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body