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