175b56488587af62afa30a3a957d774f9c4b2468
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Package / PackageManagerTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Package;
3
4 /* *
5 * This script belongs to the TYPO3 Flow framework. *
6 * *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU Lesser General Public License, either version 3 *
9 * of the License, or (at your option) any later version. *
10 * *
11 * The TYPO3 project - inspiring people to share! *
12 * */
13
14 use TYPO3\CMS\Core\Package\DependencyResolver;
15 use org\bovigo\vfs\vfsStream;
16 use TYPO3\CMS\Core\Package\PackageManager;
17 use TYPO3\CMS\Core\Package\Package;
18
19 /**
20 * Testcase for the default package manager
21 *
22 */
23 class PackageManagerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
24 {
25 /**
26 * @var PackageManager
27 */
28 protected $packageManager;
29
30 /**
31 * Sets up this test case
32 *
33 */
34 protected function setUp()
35 {
36 vfsStream::setup('Test');
37 $mockBootstrap = $this->getMock(\TYPO3\CMS\Core\Core\Bootstrap::class, array(), array(), '', false);
38 $mockCache = $this->getMock(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend::class, array('has', 'set', 'getBackend'), array(), '', false);
39 $mockCacheBackend = $this->getMock(\TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend::class, array('has', 'set', 'getBackend'), array(), '', false);
40 $mockCache->expects($this->any())->method('has')->will($this->returnValue(false));
41 $mockCache->expects($this->any())->method('set')->will($this->returnValue(true));
42 $mockCache->expects($this->any())->method('getBackend')->will($this->returnValue($mockCacheBackend));
43 $mockCacheBackend->expects($this->any())->method('getCacheDirectory')->will($this->returnValue('vfs://Test/Cache'));
44 $this->packageManager = $this->getAccessibleMock(PackageManager::class, array('sortAndSavePackageStates', 'sortAvailablePackagesByDependencies', 'registerAutoloadInformationInClassLoader'));
45
46 mkdir('vfs://Test/Packages/Application', 0700, true);
47 mkdir('vfs://Test/Configuration');
48 file_put_contents('vfs://Test/Configuration/PackageStates.php', "<?php return array ('packages' => array(), 'version' => 4); ");
49
50 $composerNameToPackageKeyMap = array(
51 'typo3/flow' => 'TYPO3.Flow'
52 );
53
54 $this->packageManager->injectCoreCache($mockCache);
55 $this->inject($this->packageManager, 'composerNameToPackageKeyMap', $composerNameToPackageKeyMap);
56 $this->packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
57 $this->packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
58 $this->packageManager->initialize($mockBootstrap);
59 }
60
61 /**
62 * @param string $packageKey
63 * @return Package
64 */
65 protected function createPackage($packageKey)
66 {
67 $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
68 mkdir($packagePath, 0770, true);
69 file_put_contents($packagePath . 'ext_emconf.php', '');
70 file_put_contents($packagePath . 'composer.json', '{}');
71 $package = new Package($this->packageManager, $packageKey, $packagePath);
72 $this->packageManager->registerPackage($package);
73 $this->packageManager->activatePackage($packageKey);
74
75 return $package;
76 }
77
78 /**
79 * @test
80 */
81 public function getPackageReturnsTheSpecifiedPackage()
82 {
83 $this->createPackage('TYPO3.MyPackage');
84 $package = $this->packageManager->getPackage('TYPO3.MyPackage');
85
86 $this->assertInstanceOf(Package::class, $package, 'The result of getPackage() was no valid package object.');
87 }
88
89 /**
90 * @test
91 * @expectedException \TYPO3\CMS\Core\Package\Exception\UnknownPackageException
92 */
93 public function getPackageThrowsExceptionOnUnknownPackage()
94 {
95 $this->packageManager->getPackage('PrettyUnlikelyThatThisPackageExists');
96 }
97
98
99 /**
100 * @test
101 */
102 public function scanAvailablePackagesTraversesThePackagesDirectoryAndRegistersPackagesItFinds()
103 {
104 $expectedPackageKeys = array(
105 $this->getUniqueId('TYPO3.CMS'),
106 $this->getUniqueId('TYPO3.CMS.Test'),
107 $this->getUniqueId('TYPO3.YetAnotherTestPackage'),
108 $this->getUniqueId('Lolli.Pop.NothingElse')
109 );
110
111 foreach ($expectedPackageKeys as $packageKey) {
112 $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
113
114 mkdir($packagePath, 0770, true);
115 file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "typo3-test"}');
116 }
117
118 $packageManager = $this->getAccessibleMock(PackageManager::class, array('sortAndSavePackageStates'));
119 $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
120 $packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
121
122 $packageManager->_set('packages', array());
123 $packageManager->_call('scanAvailablePackages');
124
125 $packageStates = require('vfs://Test/Configuration/PackageStates.php');
126 $actualPackageKeys = array_keys($packageStates['packages']);
127 $this->assertEquals(sort($expectedPackageKeys), sort($actualPackageKeys));
128 }
129
130 /**
131 * @test
132 */
133 public function scanAvailablePackagesKeepsExistingPackageConfiguration()
134 {
135 $expectedPackageKeys = array(
136 $this->getUniqueId('TYPO3.CMS'),
137 $this->getUniqueId('TYPO3.CMS.Test'),
138 $this->getUniqueId('TYPO3.YetAnotherTestPackage'),
139 $this->getUniqueId('Lolli.Pop.NothingElse')
140 );
141
142 foreach ($expectedPackageKeys as $packageKey) {
143 $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
144
145 mkdir($packagePath, 0770, true);
146 file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "typo3-cms-test"}');
147 file_put_contents($packagePath . 'ext_emconf.php', '');
148 }
149
150 /** @var PackageManager|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $packageManager */
151 $packageManager = $this->getAccessibleMock(PackageManager::class, array('dummy'));
152 $packageManager->_set('packagesBasePaths', array('local' => 'vfs://Test/Packages/Application'));
153 $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
154 $packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
155
156 /** @var DependencyResolver|\PHPUnit_Framework_MockObject_MockObject $dependencyResolver */
157 $dependencyResolver = $this->getMock(DependencyResolver::class);
158 $dependencyResolver
159 ->expects($this->any())
160 ->method('sortPackageStatesConfigurationByDependency')
161 ->willReturnArgument(0);
162
163 $packageManager->injectDependencyResolver($dependencyResolver);
164
165 $packageKey = $expectedPackageKeys[0];
166 $packageManager->_set('packageStatesConfiguration', array(
167 'packages' => array(
168 $packageKey => array(
169 'state' => 'inactive',
170 'packagePath' => 'Application/' . $packageKey . '/'
171 )
172 ),
173 'version' => 4
174 ));
175 $packageManager->_call('scanAvailablePackages');
176 $packageManager->_call('sortAndSavePackageStates');
177
178 $packageStates = require('vfs://Test/Configuration/PackageStates.php');
179 $this->assertEquals('inactive', $packageStates['packages'][$packageKey]['state']);
180 }
181
182
183 /**
184 * @test
185 */
186 public function packageStatesConfigurationContainsRelativePaths()
187 {
188 $packageKeys = array(
189 $this->getUniqueId('Lolli.Pop.NothingElse'),
190 $this->getUniqueId('TYPO3.Package'),
191 $this->getUniqueId('TYPO3.YetAnotherTestPackage')
192 );
193
194 foreach ($packageKeys as $packageKey) {
195 $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
196
197 mkdir($packagePath, 0770, true);
198 file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "typo3-cms-test"}');
199 file_put_contents($packagePath . 'ext_emconf.php', '');
200 }
201
202 $packageManager = $this->getAccessibleMock(PackageManager::class, array('dummy'));
203 $packageManager->_set('packagesBasePaths', array('local' => 'vfs://Test/Packages/Application'));
204 $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
205 $packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
206
207 /** @var DependencyResolver|\PHPUnit_Framework_MockObject_MockObject $dependencyResolver */
208 $dependencyResolver = $this->getMock(DependencyResolver::class);
209 $dependencyResolver
210 ->expects($this->any())
211 ->method('sortPackageStatesConfigurationByDependency')
212 ->willReturnArgument(0);
213
214 $packageManager->injectDependencyResolver($dependencyResolver);
215
216 $packageManager->_set('packages', array());
217 $packageManager->_call('scanAvailablePackages');
218
219 $expectedPackageStatesConfiguration = array();
220 foreach ($packageKeys as $packageKey) {
221 $expectedPackageStatesConfiguration[$packageKey] = array(
222 'state' => 'inactive',
223 'packagePath' => 'Application/' . $packageKey . '/',
224 'composerName' => $packageKey,
225 'suggestions' => array(),
226 );
227 }
228
229 $actualPackageStatesConfiguration = $packageManager->_get('packageStatesConfiguration');
230 $this->assertEquals($expectedPackageStatesConfiguration, $actualPackageStatesConfiguration['packages']);
231 }
232
233 /**
234 * Data Provider returning valid package keys and the corresponding path
235 *
236 * @return array
237 */
238 public function packageKeysAndPaths()
239 {
240 return array(
241 array('TYPO3.YetAnotherTestPackage', 'vfs://Test/Packages/Application/TYPO3.YetAnotherTestPackage/'),
242 array('Lolli.Pop.NothingElse', 'vfs://Test/Packages/Application/Lolli.Pop.NothingElse/')
243 );
244 }
245
246 /**
247 * @test
248 * @dataProvider packageKeysAndPaths
249 */
250 public function createPackageCreatesPackageFolderAndReturnsPackage($packageKey, $expectedPackagePath)
251 {
252 $actualPackage = $this->createPackage($packageKey);
253 $actualPackagePath = $actualPackage->getPackagePath();
254
255 $this->assertEquals($expectedPackagePath, $actualPackagePath);
256 $this->assertTrue(is_dir($actualPackagePath), 'Package path should exist after createPackage()');
257 $this->assertEquals($packageKey, $actualPackage->getPackageKey());
258 $this->assertTrue($this->packageManager->isPackageAvailable($packageKey));
259 }
260
261 /**
262 * @test
263 */
264 public function activatePackageAndDeactivatePackageActivateAndDeactivateTheGivenPackage()
265 {
266 $packageKey = 'Acme.YetAnotherTestPackage';
267
268 $this->createPackage($packageKey);
269
270 $this->packageManager->deactivatePackage($packageKey);
271 $this->assertFalse($this->packageManager->isPackageActive($packageKey));
272
273 $this->packageManager->activatePackage($packageKey);
274 $this->assertTrue($this->packageManager->isPackageActive($packageKey));
275 }
276
277 /**
278 * @test
279 * @expectedException \TYPO3\CMS\Core\Package\Exception\ProtectedPackageKeyException
280 */
281 public function deactivatePackageThrowsAnExceptionIfPackageIsProtected()
282 {
283 $package = $this->createPackage('Acme.YetAnotherTestPackage');
284 $package->setProtected(true);
285 $this->packageManager->deactivatePackage('Acme.YetAnotherTestPackage');
286 }
287
288 /**
289 * @test
290 * @expectedException \TYPO3\CMS\Core\Package\Exception\UnknownPackageException
291 */
292 public function deletePackageThrowsErrorIfPackageIsNotAvailable()
293 {
294 $this->packageManager->deletePackage('PrettyUnlikelyThatThisPackageExists');
295 }
296
297 /**
298 * @test
299 * @expectedException \TYPO3\CMS\Core\Package\Exception\ProtectedPackageKeyException
300 */
301 public function deletePackageThrowsAnExceptionIfPackageIsProtected()
302 {
303 $package = $this->createPackage('Acme.YetAnotherTestPackage');
304 $package->setProtected(true);
305 $this->packageManager->deletePackage('Acme.YetAnotherTestPackage');
306 }
307
308 /**
309 * @test
310 */
311 public function deletePackageRemovesPackageFromAvailableAndActivePackagesAndDeletesThePackageDirectory()
312 {
313 $this->createPackage('Acme.YetAnotherTestPackage');
314
315 $this->assertTrue($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage'));
316 $this->assertTrue($this->packageManager->isPackageAvailable('Acme.YetAnotherTestPackage'));
317
318 $this->packageManager->deletePackage('Acme.YetAnotherTestPackage');
319
320 $this->assertFalse($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage'));
321 $this->assertFalse($this->packageManager->isPackageAvailable('Acme.YetAnotherTestPackage'));
322 }
323
324 /**
325 * @return array
326 */
327 public function composerNamesAndPackageKeys()
328 {
329 return array(
330 array('imagine/Imagine', 'imagine.Imagine'),
331 array('imagine/imagine', 'imagine.Imagine'),
332 array('typo3/cms', 'TYPO3.CMS'),
333 array('TYPO3/CMS', 'TYPO3.CMS')
334 );
335 }
336
337 /**
338 * @test
339 * @dataProvider composerNamesAndPackageKeys
340 */
341 public function getPackageKeyFromComposerNameIgnoresCaseDifferences($composerName, $packageKey)
342 {
343 $packageStatesConfiguration = array('packages' =>
344 array(
345 'TYPO3.CMS' => array(
346 'composerName' => 'typo3/cms'
347 ),
348 'imagine.Imagine' => array(
349 'composerName' => 'imagine/Imagine'
350 )
351 )
352 );
353
354 $packageManager = $this->getAccessibleMock(PackageManager::class, array('resolvePackageDependencies'));
355 $packageManager->_set('packageStatesConfiguration', $packageStatesConfiguration);
356
357 $this->assertEquals($packageKey, $packageManager->_call('getPackageKeyFromComposerName', $composerName));
358 }
359 }