Merge pull request #2292 from davecivicrm/CRM-14047
[civicrm-core.git] / tests / phpunit / CRM / Extension / ManagerTest.php
CommitLineData
6a488035 1<?php
b6708aeb 2/*
3 +--------------------------------------------------------------------+
232624b1 4| CiviCRM version 4.4 |
b6708aeb 5+--------------------------------------------------------------------+
6| Copyright CiviCRM LLC (c) 2004-2013 |
7+--------------------------------------------------------------------+
8| This file is a part of CiviCRM. |
9| |
10| CiviCRM is free software; you can copy, modify, and distribute it |
11| under the terms of the GNU Affero General Public License |
12| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13| |
14| CiviCRM is distributed in the hope that it will be useful, but |
15| WITHOUT ANY WARRANTY; without even the implied warranty of |
16| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17| See the GNU Affero General Public License for more details. |
18| |
19| You should have received a copy of the GNU Affero General Public |
20| License and the CiviCRM Licensing Exception along |
21| with this program; if not, contact CiviCRM LLC |
22| at info[AT]civicrm[DOT]org. If you have questions about the |
23| GNU Affero General Public License or the licensing of CiviCRM, |
24| see the CiviCRM license FAQ at http://civicrm.org/licensing |
25+--------------------------------------------------------------------+
26*/
6a488035
TO
27require_once 'CiviTest/CiviUnitTestCase.php';
28
29class CRM_Extension_ManagerTest extends CiviUnitTestCase {
30 const TESTING_TYPE = 'report';
31 const OTHER_TESTING_TYPE = 'module';
32
33 function setUp() {
34 parent::setUp();
35 list ($this->basedir, $this->container) = $this->_createContainer();
36 $this->mapper = new CRM_Extension_Mapper($this->container);
37 }
38
39 function tearDown() {
40 parent::tearDown();
41 }
42
43 /**
44 * Install an extension with an invalid type name
45 *
46 * @expectedException CRM_Extension_Exception
47 */
48 function testInstallInvalidType() {
49 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
50 $testingTypeManager->expects($this->never())
51 ->method('onPreInstall');
52 $manager = $this->_createManager(array(
53 self::OTHER_TESTING_TYPE => $testingTypeManager,
54 ));
55 $manager->install(array('test.foo.bar'));
56 }
57
58 /**
59 * Install an extension with a valid type name
60 *
61 * Note: We initially install two extensions but then toggle only
62 * the second. This controls for bad SQL queries which hit either
63 * "the first row" or "all rows".
64 */
65 function testInstall_Disable_Uninstall() {
66 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
67 $manager = $this->_createManager(array(
68 self::TESTING_TYPE => $testingTypeManager,
69 ));
70 $this->assertEquals('uninstalled', $manager->getStatus('test.foo.bar'));
71 $this->assertEquals('uninstalled', $manager->getStatus('test.whiz.bang'));
72
73 $testingTypeManager
74 ->expects($this->exactly(2))
75 ->method('onPreInstall');
76 $testingTypeManager
77 ->expects($this->exactly(2))
78 ->method('onPostInstall');
79 $manager->install(array('test.whiz.bang', 'test.foo.bar'));
80 $this->assertEquals('installed', $manager->getStatus('test.foo.bar'));
81 $this->assertEquals('installed', $manager->getStatus('test.whiz.bang'));
82
83 $testingTypeManager
84 ->expects($this->once())
85 ->method('onPreDisable');
86 $testingTypeManager
87 ->expects($this->once())
88 ->method('onPostDisable');
89 $manager->disable(array('test.foo.bar'));
90 $this->assertEquals('disabled', $manager->getStatus('test.foo.bar'));
91 $this->assertEquals('installed', $manager->getStatus('test.whiz.bang')); // no side-effect
92
93 $testingTypeManager
94 ->expects($this->once())
95 ->method('onPreUninstall');
96 $testingTypeManager
97 ->expects($this->once())
98 ->method('onPostUninstall');
99 $manager->uninstall(array('test.foo.bar'));
100 $this->assertEquals('uninstalled', $manager->getStatus('test.foo.bar'));
101 $this->assertEquals('installed', $manager->getStatus('test.whiz.bang')); // no side-effect
102 }
103
104 /**
105 * Install an extension and then harshly remove the underlying source.
106 * Subseuently disable and uninstall.
107 */
108 function testInstall_DirtyRemove_Disable_Uninstall() {
109 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
110 $manager = $this->_createManager(array(
111 self::TESTING_TYPE => $testingTypeManager,
112 ));
113 $this->assertEquals('uninstalled', $manager->getStatus('test.foo.bar'));
114
115 $manager->install(array('test.foo.bar'));
116 $this->assertEquals('installed', $manager->getStatus('test.foo.bar'));
117
118 $this->assertTrue(file_exists("{$this->basedir}/weird/foobar/info.xml"));
119 CRM_Utils_File::cleanDir("{$this->basedir}/weird/foobar", TRUE, FALSE);
120 $this->assertFalse(file_exists("{$this->basedir}/weird/foobar/info.xml"));
121 $manager->refresh();
122 $this->assertEquals('installed-missing', $manager->getStatus('test.foo.bar'));
123
124 $testingTypeManager
125 ->expects($this->once())
126 ->method('onPreDisable');
127 $testingTypeManager
128 ->expects($this->once())
129 ->method('onPostDisable');
130 $manager->disable(array('test.foo.bar'));
131 $this->assertEquals('disabled-missing', $manager->getStatus('test.foo.bar'));
132
133 $testingTypeManager
134 ->expects($this->once())
135 ->method('onPreUninstall');
136 $testingTypeManager
137 ->expects($this->once())
138 ->method('onPostUninstall');
139 $manager->uninstall(array('test.foo.bar'));
140 $this->assertEquals('unknown', $manager->getStatus('test.foo.bar'));
141 }
142
143 /**
144 * Install an extension with a valid type name
145 */
146 function testInstall_Disable_Enable() {
147 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
148 $manager = $this->_createManager(array(
149 self::TESTING_TYPE => $testingTypeManager,
150 ));
151 $this->assertEquals('uninstalled', $manager->getStatus('test.foo.bar'));
152 $this->assertEquals('uninstalled', $manager->getStatus('test.whiz.bang'));
153
154 $testingTypeManager
155 ->expects($this->exactly(2))
156 ->method('onPreInstall');
157 $testingTypeManager
158 ->expects($this->exactly(2))
159 ->method('onPostInstall');
160 $manager->install(array('test.whiz.bang', 'test.foo.bar'));
161 $this->assertEquals('installed', $manager->getStatus('test.foo.bar'));
162 $this->assertEquals('installed', $manager->getStatus('test.whiz.bang'));
163
164 $testingTypeManager
165 ->expects($this->once())
166 ->method('onPreDisable');
167 $testingTypeManager
168 ->expects($this->once())
169 ->method('onPostDisable');
170 $manager->disable(array('test.foo.bar'));
171 $this->assertEquals('disabled', $manager->getStatus('test.foo.bar'));
172 $this->assertEquals('installed', $manager->getStatus('test.whiz.bang'));
173
174 $testingTypeManager
175 ->expects($this->once())
176 ->method('onPreEnable');
177 $testingTypeManager
178 ->expects($this->once())
179 ->method('onPostEnable');
180 $manager->enable(array('test.foo.bar'));
181 $this->assertEquals('installed', $manager->getStatus('test.foo.bar'));
182 $this->assertEquals('installed', $manager->getStatus('test.whiz.bang'));
183 }
184
185 /**
186 * Performing 'install' on a 'disabled' extension performs an 'enable'
187 */
188 function testInstall_Disable_Install() {
189 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
190 $manager = $this->_createManager(array(
191 self::TESTING_TYPE => $testingTypeManager,
192 ));
193 $this->assertEquals('uninstalled', $manager->getStatus('test.foo.bar'));
194
195 $testingTypeManager
196 ->expects($this->once())
197 ->method('onPreInstall');
198 $testingTypeManager
199 ->expects($this->once())
200 ->method('onPostInstall');
201 $manager->install(array('test.foo.bar'));
202 $this->assertEquals('installed', $manager->getStatus('test.foo.bar'));
203
204 $testingTypeManager
205 ->expects($this->once())
206 ->method('onPreDisable');
207 $testingTypeManager
208 ->expects($this->once())
209 ->method('onPostDisable');
210 $manager->disable(array('test.foo.bar'));
211 $this->assertEquals('disabled', $manager->getStatus('test.foo.bar'));
212
213 $testingTypeManager
214 ->expects($this->once())
215 ->method('onPreEnable');
216 $testingTypeManager
217 ->expects($this->once())
218 ->method('onPostEnable');
219 $manager->install(array('test.foo.bar')); // install() instead of enable()
220 $this->assertEquals('installed', $manager->getStatus('test.foo.bar'));
221 }
222
223 /**
224 * Install an extension with a valid type name
225 */
226 function testEnableBare() {
227 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
228 $manager = $this->_createManager(array(
229 self::TESTING_TYPE => $testingTypeManager,
230 ));
231 $this->assertEquals('uninstalled', $manager->getStatus('test.foo.bar'));
232
233 $testingTypeManager
234 ->expects($this->once())
235 ->method('onPreInstall');
236 $testingTypeManager
237 ->expects($this->once())
238 ->method('onPostInstall');
239 $testingTypeManager
240 ->expects($this->never())
241 ->method('onPreEnable');
242 $testingTypeManager
243 ->expects($this->never())
244 ->method('onPostEnable');
245 $manager->enable(array('test.foo.bar')); // enable not install
246 $this->assertEquals('installed', $manager->getStatus('test.foo.bar'));
247 }
248
249 /**
250 * Get the status of an unknown extension
251 */
252 function testStatusUnknownKey() {
253 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
254 $testingTypeManager->expects($this->never())
255 ->method('onPreInstall');
256 $manager = $this->_createManager(array(
257 self::TESTING_TYPE => $testingTypeManager,
258 ));
259 $this->assertEquals('unknown', $manager->getStatus('test.foo.bar.whiz.bang'));
260 }
261
262 /**
263 * Replace code for an extension that doesn't exist in the container
264 */
265 function testReplace_Unknown() {
266 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
267 $manager = $this->_createManager(array(
268 self::TESTING_TYPE => $testingTypeManager,
269 ));
270 $this->assertEquals('unknown', $manager->getStatus('test.newextension'));
271
272 $this->download = $this->_createDownload('test.newextension', 'newextension');
273
274 $testingTypeManager
275 ->expects($this->never()) // no data to replace
276 ->method('onPreReplace');
277 $testingTypeManager
278 ->expects($this->never()) // no data to replace
279 ->method('onPostReplace');
280 $manager->replace($this->download);
281 $this->assertEquals('uninstalled', $manager->getStatus('test.newextension'));
282 $this->assertTrue(file_exists("{$this->basedir}/test.newextension/info.xml"));
283 $this->assertTrue(file_exists("{$this->basedir}/test.newextension/newextension.php"));
284 $this->assertEquals(self::TESTING_TYPE, $this->mapper->keyToInfo('test.newextension')->type);
285 $this->assertEquals('newextension', $this->mapper->keyToInfo('test.newextension')->file);
286 }
287
288 /**
289 * Replace code for an extension that doesn't exist in the container
290 */
291 function testReplace_Uninstalled() {
292 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
293 $manager = $this->_createManager(array(
294 self::TESTING_TYPE => $testingTypeManager,
295 ));
296 $this->assertEquals('uninstalled', $manager->getStatus('test.whiz.bang'));
297 $this->assertEquals('oddball', $this->mapper->keyToInfo('test.whiz.bang')->file);
298
299 $this->download = $this->_createDownload('test.whiz.bang', 'newextension');
300
301 $testingTypeManager
302 ->expects($this->never()) // no data to replace
303 ->method('onPreReplace');
304 $testingTypeManager
305 ->expects($this->never()) // no data to replace
306 ->method('onPostReplace');
307 $manager->replace($this->download);
308 $this->assertEquals('uninstalled', $manager->getStatus('test.whiz.bang'));
309 $this->assertTrue(file_exists("{$this->basedir}/weird/whizbang/info.xml"));
310 $this->assertTrue(file_exists("{$this->basedir}/weird/whizbang/newextension.php"));
311 $this->assertFalse(file_exists("{$this->basedir}/weird/whizbang/oddball.php"));
312 $this->assertEquals(self::TESTING_TYPE, $this->mapper->keyToInfo('test.whiz.bang')->type);
313 $this->assertEquals('newextension', $this->mapper->keyToInfo('test.whiz.bang')->file);
314 }
315
316 /**
317 * Install a module and then replace it with new code
318 *
319 * Note that some metadata changes between versions -- the original has
320 * file="oddball", and the upgrade has file="newextension".
321 */
322 function testReplace_Installed() {
323 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
324 $manager = $this->_createManager(array(
325 self::TESTING_TYPE => $testingTypeManager,
326 ));
327 $this->assertEquals('uninstalled', $manager->getStatus('test.whiz.bang'));
328 $this->assertEquals('oddball', $this->mapper->keyToInfo('test.whiz.bang')->file);
329
330 $manager->install(array('test.whiz.bang'));
331 $this->assertEquals('installed', $manager->getStatus('test.whiz.bang'));
332 $this->assertEquals('oddball', $this->mapper->keyToInfo('test.whiz.bang')->file);
333 $this->assertDBQuery('oddball', 'SELECT file FROM civicrm_extension WHERE full_name ="test.whiz.bang"');
334
335 $this->download = $this->_createDownload('test.whiz.bang', 'newextension');
336
337 $testingTypeManager
338 ->expects($this->once())
339 ->method('onPreReplace');
340 $testingTypeManager
341 ->expects($this->once())
342 ->method('onPostReplace');
343 $manager->replace($this->download);
344 $this->assertEquals('installed', $manager->getStatus('test.whiz.bang'));
345 $this->assertTrue(file_exists("{$this->basedir}/weird/whizbang/info.xml"));
346 $this->assertTrue(file_exists("{$this->basedir}/weird/whizbang/newextension.php"));
347 $this->assertFalse(file_exists("{$this->basedir}/weird/whizbang/oddball.php"));
348 $this->assertEquals('newextension', $this->mapper->keyToInfo('test.whiz.bang')->file);
349 $this->assertDBQuery('newextension', 'SELECT file FROM civicrm_extension WHERE full_name ="test.whiz.bang"');
350 }
351
352 /**
353 * Install a module and then delete (leaving stale DB info); restore
354 * the module by downloading new code.
355 *
356 * Note that some metadata changes between versions -- the original has
357 * file="oddball", and the upgrade has file="newextension".
358 */
359 function testReplace_InstalledMissing() {
360 $testingTypeManager = $this->getMock('CRM_Extension_Manager_Interface');
361 $manager = $this->_createManager(array(
362 self::TESTING_TYPE => $testingTypeManager,
363 ));
b6708aeb 364
6a488035
TO
365 // initial installation
366 $this->assertEquals('uninstalled', $manager->getStatus('test.whiz.bang'));
367 $manager->install(array('test.whiz.bang'));
368 $this->assertEquals('installed', $manager->getStatus('test.whiz.bang'));
369
370 // dirty remove
371 $this->assertTrue(file_exists("{$this->basedir}/weird/whizbang/info.xml"));
372 CRM_Utils_File::cleanDir("{$this->basedir}/weird/whizbang", TRUE, FALSE);
373 $this->assertFalse(file_exists("{$this->basedir}/weird/whizbang/info.xml"));
374 $manager->refresh();
375 $this->assertEquals('installed-missing', $manager->getStatus('test.whiz.bang'));
376
377 // download and reinstall
378 $this->download = $this->_createDownload('test.whiz.bang', 'newextension');
379
380 $testingTypeManager
381 ->expects($this->once())
382 ->method('onPreReplace');
383 $testingTypeManager
384 ->expects($this->once())
385 ->method('onPostReplace');
386 $manager->replace($this->download);
387 $this->assertEquals('installed', $manager->getStatus('test.whiz.bang'));
388 $this->assertTrue(file_exists("{$this->basedir}/test.whiz.bang/info.xml"));
389 $this->assertTrue(file_exists("{$this->basedir}/test.whiz.bang/newextension.php"));
390 $this->assertEquals('newextension', $this->mapper->keyToInfo('test.whiz.bang')->file);
391 $this->assertDBQuery('newextension', 'SELECT file FROM civicrm_extension WHERE full_name ="test.whiz.bang"');
392 }
393
394 function _createManager($typeManagers) {
395 //list ($basedir, $c) = $this->_createContainer();
396 $mapper = new CRM_Extension_Mapper($this->container);
397 return new CRM_Extension_Manager($this->container, $this->container, $this->mapper, $typeManagers);
398 }
399
400 function _createContainer(CRM_Utils_Cache_Interface $cache = NULL, $cacheKey = NULL) {
401 $basedir = $this->createTempDir('ext-');
402 mkdir("$basedir/weird");
403 mkdir("$basedir/weird/foobar");
404 file_put_contents("$basedir/weird/foobar/info.xml", "<extension key='test.foo.bar' type='".self::TESTING_TYPE."'><file>oddball</file></extension>");
405 // not needed for now // file_put_contents("$basedir/weird/bar/oddball.php", "<?php\n");
406 mkdir("$basedir/weird/whizbang");
407 file_put_contents("$basedir/weird/whizbang/info.xml", "<extension key='test.whiz.bang' type='".self::TESTING_TYPE."'><file>oddball</file></extension>");
408 // not needed for now // file_put_contents("$basedir/weird/whizbang/oddball.php", "<?php\n");
409 $c = new CRM_Extension_Container_Basic($basedir, 'http://example/basedir', $cache, $cacheKey);
410 return array($basedir, $c);
411 }
412
413 function _createDownload($key, $file) {
414 $basedir = $this->createTempDir('ext-dl-');
415 file_put_contents("$basedir/info.xml", "<extension key='$key' type='".self::TESTING_TYPE."'><file>$file</file></extension>");
416 file_put_contents("$basedir/$file.php", "<?php\n");
417 return $basedir;
418 }
419}