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