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