MDL-61944 tool_mobile: Implement Privacy API
[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
413f19bc
DM
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 */
dcc16e15 39 const SUBSYSTEMCOUNT = 68;
9e19a0f0 40
64f631c1
JO
41 public function setUp() {
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() {
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
9e19a0f0
PS
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.
2058e797 68 foreach ($subsystems as $subsystem => $fulldir) {
9e19a0f0
PS
69 $this->assertFalse(strpos($subsystem, '_'), 'Core subsystems must be one work without underscores');
70 if ($fulldir === null) {
84192d78 71 if ($subsystem === 'filepicker' or $subsystem === 'help') {
9e19a0f0
PS
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
2058e797 140 foreach ($plugintypes as $plugintype => $fulldir) {
9e19a0f0
PS
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
2058e797 156 foreach ($plugintypes as $plugintype => $fulldir) {
9e19a0f0
PS
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);
2058e797 168 foreach ($plugins as $pluginname => $plugindir) {
9e19a0f0
PS
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);
402a974e 175 $dirs = array_values($dirs);
9e19a0f0
PS
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);
2058e797 196 foreach ($plugins as $pluginname => $plugindir) {
9e19a0f0
PS
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);
2058e797 207 foreach ($plugins as $pluginname => $plugindir) {
9e19a0f0
PS
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'));
a41d1ca0
PS
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'));
9e19a0f0
PS
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'));
b5486ce8 229 $this->assertFalse(core_component::is_valid_plugin_name('mod', 'example_'));
9e19a0f0
PS
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'));
a41d1ca0
PS
237 $this->assertTrue(core_component::is_valid_plugin_name('tool', 'feedback360'));
238 $this->assertTrue(core_component::is_valid_plugin_name('tool', 'feed_back360'));
9e19a0f0
PS
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'));
b5486ce8
PS
245 $this->assertFalse(core_component::is_valid_plugin_name('tool', 'example_'));
246 $this->assertFalse(core_component::is_valid_plugin_name('tool', 'example__x1'));
9e19a0f0
PS
247 }
248
7ace3287
AN
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
9e19a0f0 287 public function test_normalize_component() {
9e19a0f0
PS
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.
96671c8c
AN
316 $this->assertSame(array('mod', 'whoonearthwouldcomewithsuchastupidnameofcomponent'),
317 core_component::normalize_component('whoonearthwouldcomewithsuchastupidnameofcomponent'));
9e19a0f0 318 // Module names can not contain underscores, this must be a subplugin.
96671c8c
AN
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'));
9e19a0f0
PS
323 }
324
325 public function test_deprecated_normalize_component() {
9e19a0f0
PS
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.
96671c8c
AN
354 $this->assertSame(array('mod', 'whoonearthwouldcomewithsuchastupidnameofcomponent'),
355 normalize_component('whoonearthwouldcomewithsuchastupidnameofcomponent'));
9e19a0f0 356 // Module names can not contain underscores, this must be a subplugin.
96671c8c
AN
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'));
9e19a0f0
PS
361 }
362
9e19a0f0
PS
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);
2058e797 367 foreach ($plugins as $pluginname => $plugindir) {
9e19a0f0
PS
368 $this->assertSame($plugindir, core_component::get_component_directory(($plugintype.'_'.$pluginname)));
369 }
370 }
371
372 $subsystems = core_component::get_core_subsystems();
2058e797 373 foreach ($subsystems as $subsystem => $fulldir) {
9e19a0f0
PS
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);
2058e797 382 foreach ($plugins as $pluginname => $plugindir) {
9e19a0f0
PS
383 $this->assertSame($plugindir, get_component_directory(($plugintype.'_'.$pluginname)));
384 }
385 }
386
387 $subsystems = core_component::get_core_subsystems();
2058e797 388 foreach ($subsystems as $subsystem => $fulldir) {
9e19a0f0
PS
389 $this->assertSame($fulldir, get_component_directory(('core_'.$subsystem)));
390 }
391 }
3601c5f0 392
e87214bd
PS
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.php");
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.php");
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->assertFileNotExists("$CFG->dirroot/mod/choice/db/subplugins.php");
423
424 $this->assertNull(core_component::get_subplugins('mod_choice'));
425
426 $this->assertNull(core_component::get_subplugins('xxxx_yyyy'));
427 }
428
3601c5f0
PS
429 public function test_get_plugin_types_with_subplugins() {
430 global $CFG;
431
432 $types = core_component::get_plugin_types_with_subplugins();
433
2058e797 434 // Hardcode it here to detect if anybody hacks the code to include more subplugin types.
3601c5f0
PS
435 $expected = array(
436 'mod' => "$CFG->dirroot/mod",
437 'editor' => "$CFG->dirroot/lib/editor",
4e6f1992 438 'tool' => "$CFG->dirroot/$CFG->admin/tool",
3601c5f0
PS
439 'local' => "$CFG->dirroot/local",
440 );
441
442 $this->assertSame($expected, $types);
d26ec8a5
FM
443
444 }
445
446 public function test_get_plugin_list_with_file() {
d26ec8a5
FM
447 $this->resetAfterTest(true);
448
9ebd40ed
PS
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 }
d26ec8a5
FM
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));
3601c5f0 474 }
f900b2b6
DM
475
476 public function test_get_component_classes_int_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
8bee2d8d
DM
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
f900b2b6
DM
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
8bee2d8d 496 // Core as a component works, the funcion can normalise the component name.
f900b2b6 497 $this->assertCount(7, core_component::get_component_classes_in_namespace('core', 'update'));
8bee2d8d
DM
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'));
f900b2b6
DM
500
501 // Multiple levels.
502 $this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', '\\output\\myprofile\\'));
8bee2d8d 503 $this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', 'output\\myprofile\\'));
f900b2b6 504 $this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', '\\output\\myprofile'));
8bee2d8d
DM
505 $this->assertCount(5, core_component::get_component_classes_in_namespace('core_user', 'output\\myprofile'));
506
507 // Without namespace it returns classes/ classes.
30e44bfc 508 $this->assertCount(3, core_component::get_component_classes_in_namespace('tool_mobile', ''));
de33cd0c 509 $this->assertCount(2, core_component::get_component_classes_in_namespace('tool_filetypes'));
f900b2b6 510 }
64f631c1
JO
511
512 /**
513 * Data provider for classloader test
514 */
515 public function classloader_provider() {
516 global $CFG;
517
518 // As part of these tests, we Check that there are no unexpected problems with overlapping PSR namespaces.
519 // This is not in the spec, but may come up in some libraries using both namespaces and PEAR-style class names.
520 // If problems arise we can remove this test, but will need to add a warning.
521 // Normalise to forward slash for testing purposes.
522 $directory = str_replace('\\', '/', $CFG->dirroot) . "/lib/tests/fixtures/component/";
523
524 $psr0 = [
525 'psr0' => 'lib/tests/fixtures/component/psr0',
526 'overlap' => 'lib/tests/fixtures/component/overlap'
527 ];
528 $psr4 = [
529 'psr4' => 'lib/tests/fixtures/component/psr4',
530 'overlap' => 'lib/tests/fixtures/component/overlap'
531 ];
532 return [
533 'PSR-0 Classloading - Root' => [
534 'psr0' => $psr0,
535 'psr4' => $psr4,
536 'classname' => 'psr0_main',
537 'includedfiles' => "{$directory}psr0/main.php",
538 ],
539 'PSR-0 Classloading - Sub namespace - underscores' => [
540 'psr0' => $psr0,
541 'psr4' => $psr4,
542 'classname' => 'psr0_subnamespace_example',
543 'includedfiles' => "{$directory}psr0/subnamespace/example.php",
544 ],
545 'PSR-0 Classloading - Sub namespace - slashes' => [
546 'psr0' => $psr0,
547 'psr4' => $psr4,
548 'classname' => 'psr0\\subnamespace\\slashes',
549 'includedfiles' => "{$directory}psr0/subnamespace/slashes.php",
550 ],
551 'PSR-4 Classloading - Root' => [
552 'psr0' => $psr0,
553 'psr4' => $psr4,
554 'classname' => 'psr4\\main',
555 'includedfiles' => "{$directory}psr4/main.php",
556 ],
557 'PSR-4 Classloading - Sub namespace' => [
558 'psr0' => $psr0,
559 'psr4' => $psr4,
560 'classname' => 'psr4\\subnamespace\\example',
561 'includedfiles' => "{$directory}psr4/subnamespace/example.php",
562 ],
563 'PSR-4 Classloading - Ensure underscores are not converted to paths' => [
564 'psr0' => $psr0,
565 'psr4' => $psr4,
566 'classname' => 'psr4\\subnamespace\\underscore_example',
567 'includedfiles' => "{$directory}psr4/subnamespace/underscore_example.php",
568 ],
569 'Overlap - Ensure no unexpected problems with PSR-4 when overlapping namespaces.' => [
570 'psr0' => $psr0,
571 'psr4' => $psr4,
572 'classname' => 'overlap\\subnamespace\\example',
573 'includedfiles' => "{$directory}overlap/subnamespace/example.php",
574 ],
575 'Overlap - Ensure no unexpected problems with PSR-0 overlapping namespaces.' => [
576 'psr0' => $psr0,
577 'psr4' => $psr4,
578 'classname' => 'overlap_subnamespace_example2',
579 'includedfiles' => "{$directory}overlap/subnamespace/example2.php",
580 ],
581 ];
582 }
583
584 /**
585 * Test the classloader.
586 *
587 * @dataProvider classloader_provider
588 * @param array $psr0 The PSR-0 namespaces to be used in the test.
589 * @param array $psr4 The PSR-4 namespaces to be used in the test.
590 * @param string $classname The name of the class to attempt to load.
591 * @param string $includedfiles The file expected to be loaded.
592 */
593 public function test_classloader($psr0, $psr4, $classname, $includedfiles) {
594 $psr0namespaces = new ReflectionProperty('core_component', 'psr0namespaces');
595 $psr0namespaces->setAccessible(true);
596 $psr0namespaces->setValue(null, $psr0);
597
598 $psr4namespaces = new ReflectionProperty('core_component', 'psr4namespaces');
599 $psr4namespaces->setAccessible(true);
600 $psr4namespaces->setValue(null, $psr4);
601
602 core_component::classloader($classname);
603 if (DIRECTORY_SEPARATOR != '/') {
604 // Denormalise the expected path so that we can quickly compare with get_included_files.
605 $includedfiles = str_replace('/', DIRECTORY_SEPARATOR, $includedfiles);
606 }
607 $this->assertContains($includedfiles, get_included_files());
608 $this->assertTrue(class_exists($classname, false));
609 }
610
611 /**
612 * Data provider for psr_classloader test
613 */
614 public function psr_classloader_provider() {
615 global $CFG;
616
617 // As part of these tests, we Check that there are no unexpected problems with overlapping PSR namespaces.
618 // This is not in the spec, but may come up in some libraries using both namespaces and PEAR-style class names.
619 // If problems arise we can remove this test, but will need to add a warning.
620 // Normalise to forward slash for testing purposes.
621 $directory = str_replace('\\', '/', $CFG->dirroot) . "/lib/tests/fixtures/component/";
622
623 $psr0 = [
624 'psr0' => 'lib/tests/fixtures/component/psr0',
625 'overlap' => 'lib/tests/fixtures/component/overlap'
626 ];
627 $psr4 = [
628 'psr4' => 'lib/tests/fixtures/component/psr4',
629 'overlap' => 'lib/tests/fixtures/component/overlap'
630 ];
631 return [
632 'PSR-0 Classloading - Root' => [
633 'psr0' => $psr0,
634 'psr4' => $psr4,
635 'classname' => 'psr0_main',
636 'file' => "{$directory}psr0/main.php",
637 ],
638 'PSR-0 Classloading - Sub namespace - underscores' => [
639 'psr0' => $psr0,
640 'psr4' => $psr4,
641 'classname' => 'psr0_subnamespace_example',
642 'file' => "{$directory}psr0/subnamespace/example.php",
643 ],
644 'PSR-0 Classloading - Sub namespace - slashes' => [
645 'psr0' => $psr0,
646 'psr4' => $psr4,
647 'classname' => 'psr0\\subnamespace\\slashes',
648 'file' => "{$directory}psr0/subnamespace/slashes.php",
649 ],
650 'PSR-0 Classloading - non-existant file' => [
651 'psr0' => $psr0,
652 'psr4' => $psr4,
653 'classname' => 'psr0_subnamespace_nonexistant_file',
654 'file' => false,
655 ],
656 'PSR-4 Classloading - Root' => [
657 'psr0' => $psr0,
658 'psr4' => $psr4,
659 'classname' => 'psr4\\main',
660 'file' => "{$directory}psr4/main.php",
661 ],
662 'PSR-4 Classloading - Sub namespace' => [
663 'psr0' => $psr0,
664 'psr4' => $psr4,
665 'classname' => 'psr4\\subnamespace\\example',
666 'file' => "{$directory}psr4/subnamespace/example.php",
667 ],
668 'PSR-4 Classloading - Ensure underscores are not converted to paths' => [
669 'psr0' => $psr0,
670 'psr4' => $psr4,
671 'classname' => 'psr4\\subnamespace\\underscore_example',
672 'file' => "{$directory}psr4/subnamespace/underscore_example.php",
673 ],
674 'PSR-4 Classloading - non-existant file' => [
675 'psr0' => $psr0,
676 'psr4' => $psr4,
677 'classname' => 'psr4\\subnamespace\\nonexistant',
678 'file' => false,
679 ],
680 'Overlap - Ensure no unexpected problems with PSR-4 when overlapping namespaces.' => [
681 'psr0' => $psr0,
682 'psr4' => $psr4,
683 'classname' => 'overlap\\subnamespace\\example',
684 'file' => "{$directory}overlap/subnamespace/example.php",
685 ],
686 'Overlap - Ensure no unexpected problems with PSR-0 overlapping namespaces.' => [
687 'psr0' => $psr0,
688 'psr4' => $psr4,
689 'classname' => 'overlap_subnamespace_example2',
690 'file' => "{$directory}overlap/subnamespace/example2.php",
691 ],
692 ];
693 }
694
695 /**
696 * Test the PSR classloader.
697 *
698 * @dataProvider psr_classloader_provider
699 * @param array $psr0 The PSR-0 namespaces to be used in the test.
700 * @param array $psr4 The PSR-4 namespaces to be used in the test.
701 * @param string $classname The name of the class to attempt to load.
702 * @param string|bool $file The expected file corresponding to the class or false for nonexistant.
703 */
704 public function test_psr_classloader($psr0, $psr4, $classname, $file) {
705 $psr0namespaces = new ReflectionProperty('core_component', 'psr0namespaces');
706 $psr0namespaces->setAccessible(true);
707 $psr0namespaces->setValue(null, $psr0);
708
709 $psr4namespaces = new ReflectionProperty('core_component', 'psr4namespaces');
710 $psr4namespaces->setAccessible(true);
711 $oldpsr4namespaces = $psr4namespaces->getValue(null);
712 $psr4namespaces->setValue(null, $psr4);
713
714 $component = new ReflectionClass('core_component');
715 $psrclassloader = $component->getMethod('psr_classloader');
716 $psrclassloader->setAccessible(true);
717
718 $returnvalue = $psrclassloader->invokeArgs(null, array($classname));
719 // Normalise to forward slashes for testing comparison.
720 if ($returnvalue) {
721 $returnvalue = str_replace('\\', '/', $returnvalue);
722 }
723 $this->assertEquals($file, $returnvalue);
724 }
725
726 /**
727 * Data provider for get_class_file test
728 */
729 public function get_class_file_provider() {
730 global $CFG;
731
732 return [
733 'Getting a file with underscores' => [
734 'classname' => 'Test_With_Underscores',
735 'prefix' => "Test",
736 'path' => 'test/src',
737 'separators' => ['_'],
738 'result' => $CFG->dirroot . "/test/src/With/Underscores.php",
739 ],
740 'Getting a file with slashes' => [
741 'classname' => 'Test\\With\\Slashes',
742 'prefix' => "Test",
743 'path' => 'test/src',
744 'separators' => ['\\'],
745 'result' => $CFG->dirroot . "/test/src/With/Slashes.php",
746 ],
747 'Getting a file with multiple namespaces' => [
748 'classname' => 'Test\\With\\Multiple\\Namespaces',
749 'prefix' => "Test\\With",
750 'path' => 'test/src',
751 'separators' => ['\\'],
752 'result' => $CFG->dirroot . "/test/src/Multiple/Namespaces.php",
753 ],
754 'Getting a file with multiple namespaces' => [
755 'classname' => 'Nonexistant\\Namespace\\Test',
756 'prefix' => "Test",
757 'path' => 'test/src',
758 'separators' => ['\\'],
759 'result' => false,
760 ],
761 ];
762 }
763
764 /**
765 * Test the PSR classloader.
766 *
767 * @dataProvider get_class_file_provider
768 * @param string $classname the name of the class.
769 * @param string $prefix The namespace prefix used to identify the base directory of the source files.
770 * @param string $path The relative path to the base directory of the source files.
771 * @param string[] $separators The characters that should be used for separating.
772 * @param string|bool $result The expected result to be returned from get_class_file.
773 */
774 public function test_get_class_file($classname, $prefix, $path, $separators, $result) {
775 $component = new ReflectionClass('core_component');
776 $psrclassloader = $component->getMethod('get_class_file');
777 $psrclassloader->setAccessible(true);
778
779 $file = $psrclassloader->invokeArgs(null, array($classname, $prefix, $path, $separators));
780 $this->assertEquals($result, $file);
781 }
5749f8a4
JD
782
783 /**
784 * Confirm the get_component_list method contains an entry for every component.
785 */
786 public function test_get_component_list_contains_all_components() {
787 global $CFG;
788 $componentslist = \core_component::get_component_list();
789
790 // We should have an entry for each plugin type, and one additional for 'core'.
791 $plugintypes = \core_component::get_plugin_types();
792 $numelementsexpected = count($plugintypes) + 1;
793 $this->assertEquals($numelementsexpected, count($componentslist));
794
795 // And an entry for each of the plugin types.
796 foreach (array_keys($plugintypes) as $plugintype) {
797 $this->assertArrayHasKey($plugintype, $componentslist);
798 }
799
800 // And finally, one for 'core'.
801 $this->assertArrayHasKey('core', $componentslist);
802
803 // Check a few of the known plugin types to confirm their presence at their respective type index.
804 $this->assertEquals($componentslist['core']['core_comment'], $CFG->dirroot . '/comment');
805 $this->assertEquals($componentslist['mod']['mod_forum'], $CFG->dirroot . '/mod/forum');
a706fd79 806 $this->assertEquals($componentslist['tool']['tool_usertours'], $CFG->dirroot . '/' . $CFG->admin . '/tool/usertours');
5749f8a4 807 }
9e19a0f0 808}