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