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