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