MDL-49329 admin: Validate pluginfo service response
[moodle.git] / lib / tests / plugin_manager_test.php
CommitLineData
bde002b8
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 * Unit tests for plugin manager class.
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
361feecd
DM
28global $CFG;
29require_once($CFG->dirroot.'/lib/tests/fixtures/testable_plugin_manager.php');
5a92cd0b 30require_once($CFG->dirroot.'/lib/tests/fixtures/testable_plugininfo_base.php');
361feecd 31
bde002b8
PS
32/**
33 * Tests of the basic API of the plugin manager.
34 */
35class core_plugin_manager_testcase extends advanced_testcase {
36
37 public function test_instance() {
361feecd
DM
38 $pluginman1 = core_plugin_manager::instance();
39 $this->assertInstanceOf('core_plugin_manager', $pluginman1);
e87214bd 40 $pluginman2 = core_plugin_manager::instance();
361feecd
DM
41 $this->assertSame($pluginman1, $pluginman2);
42 $pluginman3 = testable_core_plugin_manager::instance();
43 $this->assertInstanceOf('core_plugin_manager', $pluginman3);
44 $this->assertInstanceOf('testable_core_plugin_manager', $pluginman3);
45 $pluginman4 = testable_core_plugin_manager::instance();
46 $this->assertSame($pluginman3, $pluginman4);
47 $this->assertNotSame($pluginman1, $pluginman3);
bde002b8
PS
48 }
49
50 public function test_reset_caches() {
51 // Make sure there are no warnings or errors.
e87214bd 52 core_plugin_manager::reset_caches();
361feecd 53 testable_core_plugin_manager::reset_caches();
bde002b8
PS
54 }
55
56 public function test_get_plugin_types() {
57 // Make sure there are no warnings or errors.
e87214bd 58 $types = core_plugin_manager::instance()->get_plugin_types();
bde002b8
PS
59 $this->assertInternalType('array', $types);
60 foreach ($types as $type => $fulldir) {
61 $this->assertFileExists($fulldir);
62 }
63 }
64
65 public function test_get_installed_plugins() {
e87214bd 66 $types = core_plugin_manager::instance()->get_plugin_types();
bde002b8 67 foreach ($types as $type => $fulldir) {
e87214bd 68 $installed = core_plugin_manager::instance()->get_installed_plugins($type);
bde002b8
PS
69 foreach ($installed as $plugin => $version) {
70 $this->assertRegExp('/^[a-z]+[a-z0-9_]*$/', $plugin);
71 $this->assertTrue(is_numeric($version), 'All plugins should have a version, plugin '.$type.'_'.$plugin.' does not have version info.');
72 }
73 }
74 }
75
76 public function test_get_enabled_plugins() {
e87214bd 77 $types = core_plugin_manager::instance()->get_plugin_types();
bde002b8 78 foreach ($types as $type => $fulldir) {
e87214bd 79 $enabled = core_plugin_manager::instance()->get_enabled_plugins($type);
bde002b8
PS
80 if (is_array($enabled)) {
81 foreach ($enabled as $key => $val) {
82 $this->assertRegExp('/^[a-z]+[a-z0-9_]*$/', $key);
83 $this->assertSame($key, $val);
84 }
85 } else {
86 $this->assertNull($enabled);
87 }
88 }
89 }
90
91 public function test_get_present_plugins() {
e87214bd 92 $types = core_plugin_manager::instance()->get_plugin_types();
bde002b8 93 foreach ($types as $type => $fulldir) {
e87214bd 94 $present = core_plugin_manager::instance()->get_present_plugins($type);
bde002b8
PS
95 if (is_array($present)) {
96 foreach ($present as $plugin => $version) {
97 $this->assertRegExp('/^[a-z]+[a-z0-9_]*$/', $plugin, 'All plugins are supposed to have version.php file.');
98 $this->assertInternalType('object', $version);
99 $this->assertTrue(is_numeric($version->version), 'All plugins should have a version, plugin '.$type.'_'.$plugin.' does not have version info.');
100 }
101 } else {
102 // No plugins of this type exist.
103 $this->assertNull($present);
104 }
105 }
106 }
107
108 public function test_get_plugins() {
361feecd
DM
109 $plugininfos1 = core_plugin_manager::instance()->get_plugins();
110 foreach ($plugininfos1 as $type => $infos) {
bde002b8 111 foreach ($infos as $name => $info) {
e87214bd 112 $this->assertInstanceOf('\core\plugininfo\base', $info);
bde002b8
PS
113 }
114 }
361feecd
DM
115
116 // The testable variant of the manager holds its own tree of the
117 // plugininfo objects.
118 $plugininfos2 = testable_core_plugin_manager::instance()->get_plugins();
119 $this->assertNotSame($plugininfos1['mod']['forum'], $plugininfos2['mod']['forum']);
120
121 // Singletons of each manager class share the same tree.
122 $plugininfos3 = core_plugin_manager::instance()->get_plugins();
123 $this->assertSame($plugininfos1['mod']['forum'], $plugininfos3['mod']['forum']);
124 $plugininfos4 = testable_core_plugin_manager::instance()->get_plugins();
125 $this->assertSame($plugininfos2['mod']['forum'], $plugininfos4['mod']['forum']);
bde002b8
PS
126 }
127
2d488c8f
DM
128 public function test_plugininfo_back_reference_to_the_plugin_manager() {
129 $plugman1 = core_plugin_manager::instance();
130 $plugman2 = testable_core_plugin_manager::instance();
131
132 foreach ($plugman1->get_plugins() as $type => $infos) {
133 foreach ($infos as $info) {
134 $this->assertSame($info->pluginman, $plugman1);
135 }
136 }
137
138 foreach ($plugman2->get_plugins() as $type => $infos) {
139 foreach ($infos as $info) {
140 $this->assertSame($info->pluginman, $plugman2);
141 }
142 }
143 }
144
bde002b8 145 public function test_get_plugins_of_type() {
e87214bd 146 $plugininfos = core_plugin_manager::instance()->get_plugins();
bde002b8 147 foreach ($plugininfos as $type => $infos) {
e87214bd 148 $this->assertSame($infos, core_plugin_manager::instance()->get_plugins_of_type($type));
bde002b8
PS
149 }
150 }
151
152 public function test_get_subplugins_of_plugin() {
153 global $CFG;
154
155 // Any standard plugin with subplugins is suitable.
156 $this->assertFileExists("$CFG->dirroot/lib/editor/tinymce", 'TinyMCE is not present.');
157
e87214bd 158 $subplugins = core_plugin_manager::instance()->get_subplugins_of_plugin('editor_tinymce');
bde002b8 159 foreach ($subplugins as $component => $info) {
e87214bd 160 $this->assertInstanceOf('\core\plugininfo\base', $info);
bde002b8
PS
161 }
162 }
163
164 public function test_get_subplugins() {
165 // Tested already indirectly from test_get_subplugins_of_plugin().
e87214bd 166 $subplugins = core_plugin_manager::instance()->get_subplugins();
bde002b8
PS
167 $this->assertInternalType('array', $subplugins);
168 }
169
170 public function test_get_parent_of_subplugin() {
171 global $CFG;
172
173 // Any standard plugin with subplugins is suitable.
174 $this->assertFileExists("$CFG->dirroot/lib/editor/tinymce", 'TinyMCE is not present.');
175
e87214bd 176 $parent = core_plugin_manager::instance()->get_parent_of_subplugin('tinymce');
bde002b8
PS
177 $this->assertSame('editor_tinymce', $parent);
178 }
179
180 public function test_plugin_name() {
181 global $CFG;
182
183 // Any standard plugin is suitable.
184 $this->assertFileExists("$CFG->dirroot/lib/editor/tinymce", 'TinyMCE is not present.');
185
e87214bd 186 $name = core_plugin_manager::instance()->plugin_name('editor_tinymce');
bde002b8
PS
187 $this->assertSame(get_string('pluginname', 'editor_tinymce'), $name);
188 }
189
190 public function test_plugintype_name() {
e87214bd 191 $name = core_plugin_manager::instance()->plugintype_name('editor');
bde002b8
PS
192 $this->assertSame(get_string('type_editor', 'core_plugin'), $name);
193 }
194
195 public function test_plugintype_name_plural() {
e87214bd 196 $name = core_plugin_manager::instance()->plugintype_name_plural('editor');
bde002b8
PS
197 $this->assertSame(get_string('type_editor_plural', 'core_plugin'), $name);
198 }
199
200 public function test_get_plugin_info() {
201 global $CFG;
202
203 // Any standard plugin is suitable.
204 $this->assertFileExists("$CFG->dirroot/lib/editor/tinymce", 'TinyMCE is not present.');
205
e87214bd
PS
206 $info = core_plugin_manager::instance()->get_plugin_info('editor_tinymce');
207 $this->assertInstanceOf('\core\plugininfo\editor', $info);
bde002b8
PS
208 }
209
210 public function test_can_uninstall_plugin() {
211 global $CFG;
212
213 // Any standard plugin that is required by some other standard plugin is ok.
214 $this->assertFileExists("$CFG->dirroot/$CFG->admin/tool/assignmentupgrade", 'assign upgrade tool is not present');
215 $this->assertFileExists("$CFG->dirroot/mod/assign", 'assign module is not present');
216
e87214bd
PS
217 $this->assertFalse(core_plugin_manager::instance()->can_uninstall_plugin('mod_assign'));
218 $this->assertTrue(core_plugin_manager::instance()->can_uninstall_plugin('tool_assignmentupgrade'));
bde002b8
PS
219 }
220
221 public function test_plugin_states() {
222 global $CFG;
223 $this->resetAfterTest();
224
225 // Any standard plugin that is ok.
226 $this->assertFileExists("$CFG->dirroot/mod/assign", 'assign module is not present');
227 $this->assertFileExists("$CFG->dirroot/mod/forum", 'forum module is not present');
228 $this->assertFileExists("$CFG->dirroot/$CFG->admin/tool/phpunit", 'phpunit tool is not present');
229 $this->assertFileNotExists("$CFG->dirroot/mod/xxxxxxx");
230 $this->assertFileNotExists("$CFG->dirroot/enrol/autorize");
231
232 // Ready for upgrade.
233 $assignversion = get_config('mod_assign', 'version');
234 set_config('version', $assignversion - 1, 'mod_assign');
235 // Downgrade problem.
236 $forumversion = get_config('mod_forum', 'version');
237 set_config('version', $forumversion + 1, 'mod_forum');
238 // Not installed yet.
239 unset_config('version', 'tool_phpunit');
240 // Missing already installed.
241 set_config('version', 2013091300, 'mod_xxxxxxx');
242 // Deleted present.
243 set_config('version', 2013091300, 'enrol_authorize');
244
e87214bd 245 core_plugin_manager::reset_caches();
bde002b8 246
e87214bd 247 $plugininfos = core_plugin_manager::instance()->get_plugins();
bde002b8
PS
248 foreach ($plugininfos as $type => $infos) {
249 foreach ($infos as $name => $info) {
6b61c2c4 250 /** @var core\plugininfo\base $info */
bde002b8 251 if ($info->component === 'mod_assign') {
e87214bd 252 $this->assertSame(core_plugin_manager::PLUGIN_STATUS_UPGRADE, $info->get_status(), 'Invalid '.$info->component.' state');
bde002b8 253 } else if ($info->component === 'mod_forum') {
e87214bd 254 $this->assertSame(core_plugin_manager::PLUGIN_STATUS_DOWNGRADE, $info->get_status(), 'Invalid '.$info->component.' state');
bde002b8 255 } else if ($info->component === 'tool_phpunit') {
e87214bd 256 $this->assertSame(core_plugin_manager::PLUGIN_STATUS_NEW, $info->get_status(), 'Invalid '.$info->component.' state');
bde002b8 257 } else if ($info->component === 'mod_xxxxxxx') {
e87214bd 258 $this->assertSame(core_plugin_manager::PLUGIN_STATUS_MISSING, $info->get_status(), 'Invalid '.$info->component.' state');
bde002b8 259 } else if ($info->component === 'enrol_authorize') {
e87214bd 260 $this->assertSame(core_plugin_manager::PLUGIN_STATUS_DELETE, $info->get_status(), 'Invalid '.$info->component.' state');
bde002b8 261 } else {
e87214bd 262 $this->assertSame(core_plugin_manager::PLUGIN_STATUS_UPTODATE, $info->get_status(), 'Invalid '.$info->component.' state');
bde002b8
PS
263 }
264 }
265 }
266 }
c44bbe35
DM
267
268 public function test_plugin_available_updates() {
269
270 $pluginman = testable_core_plugin_manager::instance();
271
272 foreach ($pluginman->get_plugins() as $type => $infos) {
273 foreach ($infos as $name => $info) {
274 $updates = $info->available_updates();
275 if ($info->component != 'mod_forum') {
276 $this->assertNull($updates);
277 } else {
278 $this->assertEquals(1, count($updates));
279 $update = array_shift($updates);
280 $this->assertInstanceOf('\core\update\info', $update);
281 $this->assertEquals('mod_forum', $update->component);
282 $this->assertEquals('2999122400', $update->version);
283 }
284 }
285 }
286 }
287
288 public function test_some_plugins_updatable() {
289 $pluginman = testable_core_plugin_manager::instance();
290 $this->assertTrue($pluginman->some_plugins_updatable());
291 }
5a92cd0b
DM
292
293 public function test_is_remote_plugin_available() {
294 // See {@link testable_core_plugin_manager::get_remote_plugin_info()}.
295 $pluginman = testable_core_plugin_manager::instance();
296 $this->assertTrue($pluginman->is_remote_plugin_available('foo_bar', ANY_VERSION));
297 $this->assertTrue($pluginman->is_remote_plugin_available('foo_bar', 2015010100));
298 $this->assertFalse($pluginman->is_remote_plugin_available('foo_bar', 2016010100));
299 $this->assertFalse($pluginman->is_remote_plugin_available('bar_bar', ANY_VERSION));
300 }
301
302 public function test_resolve_requirements() {
303
304 $pluginman = testable_core_plugin_manager::instance();
305
306 // Prepare a fake pluginfo instance.
307 $pluginfo = testable_plugininfo_base::fake_plugin_instance('fake', '/dev/null', 'one', '/dev/null/fake',
308 'testable_plugininfo_base', $pluginman);
309
310 // Test no $plugin->requires is specified in version.php.
311 $pluginfo->versionrequires = null;
312 $this->assertTrue($pluginfo->is_core_dependency_satisfied(2015100100));
313 $reqs = $pluginman->resolve_requirements($pluginfo, 2015100100, 29);
314 $this->assertEquals(2015100100, $reqs['core']->hasver);
315 $this->assertEquals(ANY_VERSION, $reqs['core']->reqver);
316 $this->assertEquals($pluginman::REQUIREMENT_STATUS_OK, $reqs['core']->status);
317
318 // Test plugin requires higher core version.
319 $pluginfo->versionrequires = 2015110900;
320 $this->assertFalse($pluginfo->is_core_dependency_satisfied(2015100100));
321 $reqs = $pluginman->resolve_requirements($pluginfo, 2015100100, 29);
322 $this->assertEquals(2015100100, $reqs['core']->hasver);
323 $this->assertEquals(2015110900, $reqs['core']->reqver);
324 $this->assertEquals($pluginman::REQUIREMENT_STATUS_OUTDATED, $reqs['core']->status);
325
326 // Test plugin requires current core version.
327 $pluginfo->versionrequires = 2015110900;
328 $this->assertTrue($pluginfo->is_core_dependency_satisfied(2015110900));
329 $reqs = $pluginman->resolve_requirements($pluginfo, 2015110900, 30);
330 $this->assertEquals(2015110900, $reqs['core']->hasver);
331 $this->assertEquals(2015110900, $reqs['core']->reqver);
332 $this->assertEquals($pluginman::REQUIREMENT_STATUS_OK, $reqs['core']->status);
333
334 // Test plugin requires lower core version.
335 $pluginfo->versionrequires = 2014122400;
336 $this->assertTrue($pluginfo->is_core_dependency_satisfied(2015100100));
337 $reqs = $pluginman->resolve_requirements($pluginfo, 2015100100, 29);
338 $this->assertEquals(2015100100, $reqs['core']->hasver);
339 $this->assertEquals(2014122400, $reqs['core']->reqver);
340 $this->assertEquals($pluginman::REQUIREMENT_STATUS_OK, $reqs['core']->status);
341
342 // Test plugin dependencies and their availability.
343 // See {@link testable_core_plugin_manager::get_remote_plugin_info()}.
344 $pluginfo->dependencies = array(
345 'foo_bar' => ANY_VERSION,
346 'foo_baz' => 2014010100,
347 'foo_crazy' => ANY_VERSION,
348 );
349 $reqs = $pluginman->resolve_requirements($pluginfo, 2015110900, 30);
350 $this->assertNull($reqs['foo_bar']->hasver);
351 $this->assertEquals(ANY_VERSION, $reqs['foo_bar']->reqver);
352 $this->assertEquals($pluginman::REQUIREMENT_STATUS_MISSING, $reqs['foo_bar']->status);
353 $this->assertEquals($pluginman::REQUIREMENT_AVAILABLE, $reqs['foo_bar']->availability);
354 $this->assertEquals($pluginman::REQUIREMENT_AVAILABLE, $reqs['foo_baz']->availability);
355 $this->assertEquals($pluginman::REQUIREMENT_UNAVAILABLE, $reqs['foo_crazy']->availability);
356
357 $pluginfo->dependencies = array(
358 'foo_baz' => 2015010100,
359 );
360 $reqs = $pluginman->resolve_requirements($pluginfo, 2015110900, 30);
361 $this->assertEquals($pluginman::REQUIREMENT_AVAILABLE, $reqs['foo_baz']->availability);
362
363 $pluginfo->dependencies = array(
364 'foo_baz' => 2015010101,
365 );
366 $reqs = $pluginman->resolve_requirements($pluginfo, 2015110900, 30);
367 $this->assertEquals($pluginman::REQUIREMENT_UNAVAILABLE, $reqs['foo_baz']->availability);
368 }
bde002b8 369}