[BUGFIX] Sorting of packages by dependency
[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\Flow\Package\PackageInterface;
15 use org\bovigo\vfs\vfsStream;
16
17 /**
18 * Testcase for the default package manager
19 *
20 */
21 class PackageManagerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
22
23 /**
24 * @var \TYPO3\Flow\Package\PackageManager
25 */
26 protected $packageManager;
27
28 /**
29 * Sets up this test case
30 *
31 */
32 protected function setUp() {
33 vfsStream::setup('Test');
34 $mockBootstrap = $this->getMock('TYPO3\CMS\Core\Core\Bootstrap', array(), array(), '', FALSE);
35 $mockCache = $this->getMock('TYPO3\CMS\Core\Cache\Frontend\PhpFrontend', array('has', 'set', 'getBackend'), array(), '', FALSE);
36 $mockCacheBackend = $this->getMock('TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend', array('has', 'set', 'getBackend'), array(), '', FALSE);
37 $mockCache->expects($this->any())->method('has')->will($this->returnValue(FALSE));
38 $mockCache->expects($this->any())->method('set')->will($this->returnValue(TRUE));
39 $mockCache->expects($this->any())->method('getBackend')->will($this->returnValue($mockCacheBackend));
40 $mockCacheBackend->expects($this->any())->method('getCacheDirectory')->will($this->returnValue('vfs://Test/Cache'));
41 $this->packageManager = $this->getMock('TYPO3\\CMS\\Core\\Package\\PackageManager', array('sortAndSavePackageStates'));
42
43 mkdir('vfs://Test/Packages/Application', 0700, TRUE);
44 mkdir('vfs://Test/Configuration');
45 file_put_contents('vfs://Test/Configuration/PackageStates.php', "<?php return array ('packages' => array(), 'version' => 4); ");
46
47 $mockClassLoader = $this->getMock('TYPO3\CMS\Core\Core\ClassLoader');
48 $mockClassLoader->expects($this->any())->method('setCacheIdentifier')->will($this->returnSelf());
49
50 $composerNameToPackageKeyMap = array(
51 'typo3/flow' => 'TYPO3.Flow'
52 );
53
54 $this->packageManager->injectClassLoader($mockClassLoader);
55 $this->packageManager->injectCoreCache($mockCache);
56 $this->inject($this->packageManager, 'composerNameToPackageKeyMap', $composerNameToPackageKeyMap);
57 $this->packageManager->initialize($mockBootstrap, 'vfs://Test/Packages/', 'vfs://Test/Configuration/PackageStates.php');
58 }
59
60 /**
61 * @test
62 */
63 public function getPackageReturnsTheSpecifiedPackage() {
64 $this->packageManager->createPackage('TYPO3.Flow');
65
66 $package = $this->packageManager->getPackage('TYPO3.Flow');
67 $this->assertInstanceOf('TYPO3\Flow\Package\PackageInterface', $package, 'The result of getPackage() was no valid package object.');
68 }
69
70 /**
71 * @test
72 * @expectedException \TYPO3\Flow\Package\Exception\UnknownPackageException
73 */
74 public function getPackageThrowsExceptionOnUnknownPackage() {
75 $this->packageManager->getPackage('PrettyUnlikelyThatThisPackageExists');
76 }
77
78 /**
79 * @test
80 */
81 public function getCaseSensitivePackageKeyReturnsTheUpperCamelCaseVersionOfAGivenPackageKeyIfThePackageIsRegistered() {
82 $packageManager = $this->getAccessibleMock('TYPO3\Flow\Package\PackageManager', array('dummy'));
83 $packageManager->_set('packageKeys', array('acme.testpackage' => 'Acme.TestPackage'));
84 $this->assertEquals('Acme.TestPackage', $packageManager->getCaseSensitivePackageKey('acme.testpackage'));
85 }
86
87 /**
88 * @test
89 */
90 public function scanAvailablePackagesTraversesThePackagesDirectoryAndRegistersPackagesItFinds() {
91 $expectedPackageKeys = array(
92 'TYPO3.Flow' . md5(uniqid(mt_rand(), TRUE)),
93 'TYPO3.Flow.Test' . md5(uniqid(mt_rand(), TRUE)),
94 'TYPO3.YetAnotherTestPackage' . md5(uniqid(mt_rand(), TRUE)),
95 'RobertLemke.Flow.NothingElse' . md5(uniqid(mt_rand(), TRUE))
96 );
97
98 foreach ($expectedPackageKeys as $packageKey) {
99 $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
100
101 mkdir($packagePath, 0770, TRUE);
102 mkdir($packagePath . 'Classes');
103 file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "flow-test"}');
104 }
105
106 $packageManager = $this->getAccessibleMock('TYPO3\Flow\Package\PackageManager', array('dummy'));
107 $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
108 $packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
109
110 $packageFactory = new \TYPO3\Flow\Package\PackageFactory($packageManager);
111 $this->inject($packageManager, 'packageFactory', $packageFactory);
112
113 $packageManager->_set('packages', array());
114 $packageManager->_call('scanAvailablePackages');
115
116 $packageStates = require('vfs://Test/Configuration/PackageStates.php');
117 $actualPackageKeys = array_keys($packageStates['packages']);
118 $this->assertEquals(sort($expectedPackageKeys), sort($actualPackageKeys));
119 }
120
121 /**
122 * @test
123 */
124 public function scanAvailablePackagesKeepsExistingPackageConfiguration() {
125 $expectedPackageKeys = array(
126 'TYPO3.Flow' . md5(uniqid(mt_rand(), TRUE)),
127 'TYPO3.Flow.Test' . md5(uniqid(mt_rand(), TRUE)),
128 'TYPO3.YetAnotherTestPackage' . md5(uniqid(mt_rand(), TRUE)),
129 'RobertLemke.Flow.NothingElse' . md5(uniqid(mt_rand(), TRUE))
130 );
131
132 foreach ($expectedPackageKeys as $packageKey) {
133 $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
134
135 mkdir($packagePath, 0770, TRUE);
136 mkdir($packagePath . 'Classes');
137 file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "flow-test"}');
138 }
139
140 $packageManager = $this->getAccessibleMock('TYPO3\Flow\Package\PackageManager', array('dummy'));
141 $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
142 $packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
143
144 $packageFactory = new \TYPO3\Flow\Package\PackageFactory($packageManager);
145 $this->inject($packageManager, 'packageFactory', $packageFactory);
146
147 $packageManager->_set('packageStatesConfiguration', array(
148 'packages' => array(
149 $packageKey => array(
150 'state' => 'inactive',
151 'frozen' => FALSE,
152 'packagePath' => 'Application/' . $packageKey . '/',
153 'classesPath' => 'Classes/'
154 )
155 ),
156 'version' => 2
157 ));
158 $packageManager->_call('scanAvailablePackages');
159 $packageManager->_call('sortAndsavePackageStates');
160
161 $packageStates = require('vfs://Test/Configuration/PackageStates.php');
162 $this->assertEquals('inactive', $packageStates['packages'][$packageKey]['state']);
163 }
164
165
166 /**
167 * @test
168 */
169 public function packageStatesConfigurationContainsRelativePaths() {
170 $packageKeys = array(
171 'RobertLemke.Flow.NothingElse' . md5(uniqid(mt_rand(), TRUE)),
172 'TYPO3.Flow' . md5(uniqid(mt_rand(), TRUE)),
173 'TYPO3.YetAnotherTestPackage' . md5(uniqid(mt_rand(), TRUE)),
174 );
175
176 foreach ($packageKeys as $packageKey) {
177 $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
178
179 mkdir($packagePath, 0770, TRUE);
180 mkdir($packagePath . 'Classes');
181 file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "flow-test"}');
182 }
183
184 $packageManager = $this->getAccessibleMock('TYPO3\Flow\Package\PackageManager', array('updateShortcuts'), array(), '', FALSE);
185 $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
186 $packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
187
188 $packageFactory = new \TYPO3\Flow\Package\PackageFactory($packageManager);
189 $this->inject($packageManager, 'packageFactory', $packageFactory);
190
191 $packageManager->_set('packages', array());
192 $packageManager->_call('scanAvailablePackages');
193
194 $expectedPackageStatesConfiguration = array();
195 foreach ($packageKeys as $packageKey) {
196 $expectedPackageStatesConfiguration[$packageKey] = array(
197 'state' => 'active',
198 'packagePath' => 'Application/' . $packageKey . '/',
199 'classesPath' => 'Classes/',
200 'manifestPath' => '',
201 'composerName' => $packageKey
202 );
203 }
204
205 $actualPackageStatesConfiguration = $packageManager->_get('packageStatesConfiguration');
206 $this->assertEquals($expectedPackageStatesConfiguration, $actualPackageStatesConfiguration['packages']);
207 }
208
209 /**
210 * Data Provider returning valid package keys and the corresponding path
211 *
212 * @return array
213 */
214 public function packageKeysAndPaths() {
215 return array(
216 array('TYPO3.YetAnotherTestPackage', 'vfs://Test/Packages/Application/TYPO3.YetAnotherTestPackage/'),
217 array('RobertLemke.Flow.NothingElse', 'vfs://Test/Packages/Application/RobertLemke.Flow.NothingElse/')
218 );
219 }
220
221 /**
222 * @test
223 * @dataProvider packageKeysAndPaths
224 */
225 public function createPackageCreatesPackageFolderAndReturnsPackage($packageKey, $expectedPackagePath) {
226 $actualPackage = $this->packageManager->createPackage($packageKey);
227 $actualPackagePath = $actualPackage->getPackagePath();
228
229 $this->assertEquals($expectedPackagePath, $actualPackagePath);
230 $this->assertTrue(is_dir($actualPackagePath), 'Package path should exist after createPackage()');
231 $this->assertEquals($packageKey, $actualPackage->getPackageKey());
232 $this->assertTrue($this->packageManager->isPackageAvailable($packageKey));
233 }
234
235 /**
236 * @test
237 */
238 public function createPackageWritesAComposerManifestUsingTheGivenMetaObject() {
239 $metaData = new \TYPO3\Flow\Package\MetaData('Acme.YetAnotherTestPackage');
240 $metaData->setDescription('Yet Another Test Package');
241
242 $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage', $metaData);
243
244 $json = file_get_contents($package->getPackagePath() . '/composer.json');
245 $composerManifest = json_decode($json);
246
247 $this->assertEquals('acme/yetanothertestpackage', $composerManifest->name);
248 $this->assertEquals('Yet Another Test Package', $composerManifest->description);
249 }
250
251 /**
252 * @test
253 */
254 public function createPackageCanChangePackageTypeInComposerManifest() {
255 $metaData = new \TYPO3\Flow\Package\MetaData('Acme.YetAnotherTestPackage2');
256 $metaData->setDescription('Yet Another Test Package');
257 $metaData->setPackageType('flow-custom-package');
258
259 $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage2', $metaData);
260
261 $json = file_get_contents($package->getPackagePath() . '/composer.json');
262 $composerManifest = json_decode($json);
263
264 $this->assertEquals('flow-custom-package', $composerManifest->type);
265 }
266
267 /**
268 * Checks if createPackage() creates the folders for classes, configuration, documentation, resources and tests.
269 *
270 * @test
271 */
272 public function createPackageCreatesCommonFolders() {
273 $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
274 $packagePath = $package->getPackagePath();
275
276 $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_CLASSES), "Classes directory was not created");
277 $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_CONFIGURATION), "Configuration directory was not created");
278 $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_DOCUMENTATION), "Documentation directory was not created");
279 $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_RESOURCES), "Resources directory was not created");
280 $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_TESTS_UNIT), "Tests/Unit directory was not created");
281 $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_TESTS_FUNCTIONAL), "Tests/Functional directory was not created");
282 $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_METADATA), "Metadata directory was not created");
283 }
284
285 /**
286 * Makes sure that an exception is thrown and no directory is created on passing invalid package keys.
287 *
288 * @test
289 */
290 public function createPackageThrowsExceptionOnInvalidPackageKey() {
291 try {
292 $this->packageManager->createPackage('Invalid_PackageKey');
293 } catch (\TYPO3\Flow\Package\Exception\InvalidPackageKeyException $exception) {
294 }
295 $this->assertFalse(is_dir('vfs://Test/Packages/Application/Invalid_PackageKey'), 'Package folder with invalid package key was created');
296 }
297
298 /**
299 * Makes sure that duplicate package keys are detected.
300 *
301 * @test
302 * @expectedException \TYPO3\Flow\Package\Exception\PackageKeyAlreadyExistsException
303 */
304 public function createPackageThrowsExceptionForExistingPackageKey() {
305 $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
306 $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
307 }
308
309 /**
310 * @test
311 */
312 public function createPackageActivatesTheNewlyCreatedPackage() {
313 $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
314 $this->assertTrue($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage'));
315 }
316
317 /**
318 * @test
319 */
320 public function activatePackageAndDeactivatePackageActivateAndDeactivateTheGivenPackage() {
321 $packageKey = 'Acme.YetAnotherTestPackage';
322
323 $this->packageManager->createPackage($packageKey);
324
325 $this->packageManager->deactivatePackage($packageKey);
326 $this->assertFalse($this->packageManager->isPackageActive($packageKey));
327
328 $this->packageManager->activatePackage($packageKey);
329 $this->assertTrue($this->packageManager->isPackageActive($packageKey));
330 }
331
332 /**
333 * @test
334 * @expectedException \TYPO3\Flow\Package\Exception\ProtectedPackageKeyException
335 */
336 public function deactivatePackageThrowsAnExceptionIfPackageIsProtected() {
337 $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
338 $package->setProtected(TRUE);
339 $this->packageManager->deactivatePackage('Acme.YetAnotherTestPackage');
340 }
341
342 /**
343 * @test
344 * @expectedException \TYPO3\Flow\Package\Exception\UnknownPackageException
345 */
346 public function deletePackageThrowsErrorIfPackageIsNotAvailable() {
347 $this->packageManager->deletePackage('PrettyUnlikelyThatThisPackageExists');
348 }
349
350 /**
351 * @test
352 * @expectedException \TYPO3\Flow\Package\Exception\ProtectedPackageKeyException
353 */
354 public function deletePackageThrowsAnExceptionIfPackageIsProtected() {
355 $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
356 $package->setProtected(TRUE);
357 $this->packageManager->deletePackage('Acme.YetAnotherTestPackage');
358 }
359
360 /**
361 * @test
362 */
363 public function deletePackageRemovesPackageFromAvailableAndActivePackagesAndDeletesThePackageDirectory() {
364 $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
365 $packagePath = $package->getPackagePath();
366
367 $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_METADATA));
368 $this->assertTrue($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage'));
369 $this->assertTrue($this->packageManager->isPackageAvailable('Acme.YetAnotherTestPackage'));
370
371 $this->packageManager->deletePackage('Acme.YetAnotherTestPackage');
372
373 $this->assertFalse(is_dir($packagePath . PackageInterface::DIRECTORY_METADATA));
374 $this->assertFalse($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage'));
375 $this->assertFalse($this->packageManager->isPackageAvailable('Acme.YetAnotherTestPackage'));
376 }
377
378 /**
379 * @test
380 */
381 public function getDependencyArrayForPackageReturnsCorrectResult() {
382 $mockFlowMetadata = $this->getMock('TYPO3\Flow\Package\MetaDataInterface');
383 $mockFlowMetadata->expects($this->any())->method('getConstraintsByType')->will($this->returnValue(array(
384 new \TYPO3\Flow\Package\MetaData\PackageConstraint('depends', 'TYPO3.Fluid'),
385 new \TYPO3\Flow\Package\MetaData\PackageConstraint('depends', 'Doctrine.ORM')
386 )));
387 $mockFlowPackage = $this->getMock('TYPO3\Flow\Package\PackageInterface');
388 $mockFlowPackage->expects($this->any())->method('getPackageMetaData')->will($this->returnValue($mockFlowMetadata));
389
390 $mockFluidMetadata = $this->getMock('TYPO3\Flow\Package\MetaDataInterface');
391 $mockFluidMetadata->expects($this->any())->method('getConstraintsByType')->will($this->returnValue(array(
392 new \TYPO3\Flow\Package\MetaData\PackageConstraint('depends', 'TYPO3.Flow')
393 )));
394 $mockFluidPackage = $this->getMock('TYPO3\Flow\Package\PackageInterface');
395 $mockFluidPackage->expects($this->any())->method('getPackageMetaData')->will($this->returnValue($mockFluidMetadata));
396
397 $mockOrmMetadata = $this->getMock('TYPO3\Flow\Package\MetaDataInterface');
398 $mockOrmMetadata->expects($this->any())->method('getConstraintsByType')->will($this->returnValue(array(
399 new \TYPO3\Flow\Package\MetaData\PackageConstraint('depends', 'Doctrine.DBAL')
400 )));
401 $mockOrmPackage = $this->getMock('TYPO3\Flow\Package\PackageInterface');
402 $mockOrmPackage->expects($this->any())->method('getPackageMetaData')->will($this->returnValue($mockOrmMetadata));
403
404 $mockDbalMetadata = $this->getMock('TYPO3\Flow\Package\MetaDataInterface');
405 $mockDbalMetadata->expects($this->any())->method('getConstraintsByType')->will($this->returnValue(array(
406 new \TYPO3\Flow\Package\MetaData\PackageConstraint('depends', 'Doctrine.Common')
407 )));
408 $mockDbalPackage = $this->getMock('TYPO3\Flow\Package\PackageInterface');
409 $mockDbalPackage->expects($this->any())->method('getPackageMetaData')->will($this->returnValue($mockDbalMetadata));
410
411 $mockCommonMetadata = $this->getMock('TYPO3\Flow\Package\MetaDataInterface');
412 $mockCommonMetadata->expects($this->any())->method('getConstraintsByType')->will($this->returnValue(array()));
413 $mockCommonPackage = $this->getMock('TYPO3\Flow\Package\PackageInterface');
414 $mockCommonPackage->expects($this->any())->method('getPackageMetaData')->will($this->returnValue($mockCommonMetadata));
415
416 $packages = array(
417 'TYPO3.Flow' => $mockFlowPackage,
418 'TYPO3.Fluid' => $mockFluidPackage,
419 'Doctrine.ORM' => $mockOrmPackage,
420 'Doctrine.DBAL' => $mockDbalPackage,
421 'Doctrine.Common' => $mockCommonPackage
422 );
423
424 $packageManager = $this->getAccessibleMock('\TYPO3\Flow\Package\PackageManager', array('dummy'));
425 $packageManager->_set('packages', $packages);
426 $dependencyArray = $packageManager->_call('getDependencyArrayForPackage', 'TYPO3.Flow');
427
428 $this->assertEquals(array('Doctrine.Common', 'Doctrine.DBAL', 'Doctrine.ORM', 'TYPO3.Fluid'), $dependencyArray);
429 }
430
431 /**
432 * @return array
433 */
434 public function buildDependencyGraphBuildsCorrectGraphDataProvider() {
435 return array(
436 'TYPO3 Flow Packages' => array(
437 array(
438 'TYPO3.Flow' => array(
439 'dependencies' => array('Symfony.Component.Yaml', 'Doctrine.Common', 'Doctrine.DBAL', 'Doctrine.ORM')
440 ),
441 'Doctrine.ORM' => array(
442 'dependencies' => array('Doctrine.Common', 'Doctrine.DBAL')
443 ),
444 'Doctrine.Common' => array(
445 'dependencies' => array()
446 ),
447 'Doctrine.DBAL' => array(
448 'dependencies' => array('Doctrine.Common')
449 ),
450 'Symfony.Component.Yaml' => array(
451 'dependencies' => array()
452 ),
453 ),
454 array(
455 'Doctrine.Common'
456 ),
457 array(
458 'TYPO3.Flow' => array(
459 'TYPO3.Flow' => FALSE,
460 'Doctrine.ORM' => TRUE,
461 'Doctrine.Common' => TRUE,
462 'Doctrine.DBAL' => TRUE,
463 'Symfony.Component.Yaml' => TRUE,
464 ),
465 'Doctrine.ORM' => array(
466 'TYPO3.Flow' => FALSE,
467 'Doctrine.ORM' => FALSE,
468 'Doctrine.Common' => TRUE,
469 'Doctrine.DBAL' => TRUE,
470 'Symfony.Component.Yaml' => FALSE,
471 ),
472 'Doctrine.Common' => array(
473 'TYPO3.Flow' => FALSE,
474 'Doctrine.ORM' => FALSE,
475 'Doctrine.Common' => FALSE,
476 'Doctrine.DBAL' => FALSE,
477 'Symfony.Component.Yaml' => FALSE,
478 ),
479 'Doctrine.DBAL' => array(
480 'TYPO3.Flow' => FALSE,
481 'Doctrine.ORM' => FALSE,
482 'Doctrine.Common' => TRUE,
483 'Doctrine.DBAL' => FALSE,
484 'Symfony.Component.Yaml' => FALSE,
485 ),
486 'Symfony.Component.Yaml' => array(
487 'TYPO3.Flow' => FALSE,
488 'Doctrine.ORM' => FALSE,
489 'Doctrine.Common' => TRUE,
490 'Doctrine.DBAL' => FALSE,
491 'Symfony.Component.Yaml' => FALSE,
492 ),
493 ),
494 ),
495 'TYPO3 CMS Extensions' => array(
496 array(
497 'core' => array(
498 'dependencies' => array(),
499 ),
500 'setup' => array (
501 'dependencies' => array('core'),
502 ),
503 'openid' => array(
504 'dependencies' => array('core', 'setup')
505 ),
506 'news' => array (
507 'dependencies' => array('extbase'),
508 ),
509 'extbase' => array (
510 'dependencies' => array('core'),
511 ),
512 'pt_extbase' => array (
513 'dependencies' => array('extbase'),
514 ),
515 'foo' => array (
516 'dependencies' => array(),
517 ),
518 ),
519 array(
520 'core', 'setup', 'openid', 'extbase'
521 ),
522 array(
523 'core' => array(
524 'core' => FALSE,
525 'setup' => FALSE,
526 'openid' => FALSE,
527 'news' => FALSE,
528 'extbase' => FALSE,
529 'pt_extbase' => FALSE,
530 'foo' => FALSE
531 ),
532 'setup' => array(
533 'core' => TRUE,
534 'setup' => FALSE,
535 'openid' => FALSE,
536 'news' => FALSE,
537 'extbase' => FALSE,
538 'pt_extbase' => FALSE,
539 'foo' => FALSE
540 ),
541 'openid' => array (
542 'core' => TRUE,
543 'setup' => TRUE,
544 'openid' => FALSE,
545 'news' => FALSE,
546 'extbase' => FALSE,
547 'pt_extbase' => FALSE,
548 'foo' => FALSE
549 ),
550 'news' => array (
551 'core' => FALSE,
552 'setup' => FALSE,
553 'openid' => TRUE,
554 'news' => FALSE,
555 'extbase' => TRUE,
556 'pt_extbase' => FALSE,
557 'foo' => FALSE
558 ),
559 'extbase' => array (
560 'core' => TRUE,
561 'setup' => FALSE,
562 'openid' => FALSE,
563 'news' => FALSE,
564 'extbase' => FALSE,
565 'pt_extbase' => FALSE,
566 'foo' => FALSE
567 ),
568 'pt_extbase' => array(
569 'core' => FALSE,
570 'setup' => FALSE,
571 'openid' => TRUE,
572 'news' => FALSE,
573 'extbase' => TRUE,
574 'pt_extbase' => FALSE,
575 'foo' => FALSE
576 ),
577 'foo' => array(
578 'core' => FALSE,
579 'setup' => FALSE,
580 'openid' => TRUE,
581 'news' => FALSE,
582 'extbase' => TRUE,
583 'pt_extbase' => FALSE,
584 'foo' => FALSE
585 ),
586 ),
587 ),
588 'Dummy Packages' => array(
589 array(
590 'A' => array(
591 'dependencies' => array('B', 'D', 'C'),
592 ),
593 'B' => array(
594 'dependencies' => array()
595 ),
596 'C' => array(
597 'dependencies' => array('E')
598 ),
599 'D' => array (
600 'dependencies' => array('E'),
601 ),
602 'E' => array (
603 'dependencies' => array(),
604 ),
605 'F' => array (
606 'dependencies' => array(),
607 ),
608 ),
609 array(
610 'B', 'C', 'E'
611 ),
612 array(
613 'A' => array(
614 'A' => FALSE,
615 'B' => TRUE,
616 'C' => TRUE,
617 'D' => TRUE,
618 'E' => FALSE,
619 'F' => FALSE,
620 ),
621 'B' => array(
622 'A' => FALSE,
623 'B' => FALSE,
624 'C' => FALSE,
625 'D' => FALSE,
626 'E' => FALSE,
627 'F' => FALSE,
628 ),
629 'C' => array(
630 'A' => FALSE,
631 'B' => FALSE,
632 'C' => FALSE,
633 'D' => FALSE,
634 'E' => TRUE,
635 'F' => FALSE,
636 ),
637 'D' => array (
638 'A' => FALSE,
639 'B' => TRUE,
640 'C' => TRUE,
641 'D' => FALSE,
642 'E' => FALSE,
643 'F' => FALSE,
644 ),
645 'E' => array (
646 'A' => FALSE,
647 'B' => FALSE,
648 'C' => FALSE,
649 'D' => FALSE,
650 'E' => FALSE,
651 'F' => FALSE,
652 ),
653 'F' => array (
654 'A' => FALSE,
655 'B' => TRUE,
656 'C' => TRUE,
657 'D' => FALSE,
658 'E' => FALSE,
659 'F' => FALSE,
660 ),
661 ),
662 ),
663 );
664 }
665
666 /**
667 * @test
668 * @param array $unsorted
669 * @param array $frameworkPackageKeys
670 * @param array $expectedGraph
671 * @dataProvider buildDependencyGraphBuildsCorrectGraphDataProvider
672 */
673 public function buildDependencyGraphBuildsCorrectGraph(array $unsorted, array $frameworkPackageKeys, array $expectedGraph) {
674 $unsortedPackageStatesConfiguration = array('packages' => $unsorted);
675
676 $packageKeys = array_keys($unsorted);
677 $unsortedPackages = array();
678 foreach ($packageKeys as $packageKey) {
679 $packageMock = $this->getMock('\TYPO3\Flow\Package\PackageInterface');
680 $packageMock->expects($this->any())->method('getPackageKey')->will($this->returnValue($packageKey));
681 $unsortedPackages[$packageKey] = $packageMock;
682 }
683
684 $typeAssignment = array(
685 array('', array('typo3-cms-framework'), array_diff($packageKeys, $frameworkPackageKeys)),
686 array('typo3-cms-framework', array(), $frameworkPackageKeys),
687 );
688
689 $packageManager = $this->getAccessibleMock('\TYPO3\CMS\Core\Package\PackageManager', array('resolvePackageDependencies','getPackageKeysOfType'));
690 $packageManager->expects($this->any())->method('getPackageKeysOfType')->will($this->returnValueMap($typeAssignment));
691 $packageManager->_set('packages', $unsortedPackages);
692 $packageManager->_set('packageStatesConfiguration', $unsortedPackageStatesConfiguration);
693 $packageManager->_call('buildDependencyGraph');
694
695 $this->assertEquals($expectedGraph, $packageManager->_get('dependencyGraph'));
696 }
697
698 /**
699 * @return array
700 */
701 public function packageSortingDataProvider() {
702 return array(
703 'TYPO3 Flow Packages' => array(
704 array(
705 'TYPO3.Flow' => array(
706 'dependencies' => array('Symfony.Component.Yaml', 'Doctrine.Common', 'Doctrine.DBAL', 'Doctrine.ORM')
707 ),
708 'Doctrine.ORM' => array(
709 'dependencies' => array('Doctrine.Common', 'Doctrine.DBAL')
710 ),
711 'Doctrine.Common' => array(
712 'dependencies' => array()
713 ),
714 'Doctrine.DBAL' => array(
715 'dependencies' => array('Doctrine.Common')
716 ),
717 'Symfony.Component.Yaml' => array(
718 'dependencies' => array()
719 ),
720 ),
721 array(
722 'Doctrine.Common'
723 ),
724 array(
725 'Doctrine.Common' => array(
726 'dependencies' => array()
727 ),
728 'Doctrine.DBAL' => array(
729 'dependencies' => array('Doctrine.Common')
730 ),
731 'Doctrine.ORM' => array(
732 'dependencies' => array('Doctrine.Common', 'Doctrine.DBAL')
733 ),
734 'Symfony.Component.Yaml' => array(
735 'dependencies' => array('Doctrine.Common')
736 ),
737 'TYPO3.Flow' => array(
738 'dependencies' => array('Doctrine.Common', 'Symfony.Component.Yaml', 'Doctrine.DBAL', 'Doctrine.ORM')
739 ),
740 ),
741 ),
742 'TYPO3 CMS Extensions' => array(
743 array(
744 'core' => array(
745 'dependencies' => array(),
746 ),
747 'setup' => array (
748 'dependencies' => array('core'),
749 ),
750 'openid' => array(
751 'dependencies' => array('core', 'setup')
752 ),
753 'news' => array (
754 'dependencies' => array('extbase'),
755 ),
756 'extbase' => array (
757 'dependencies' => array('core'),
758 ),
759 'pt_extbase' => array (
760 'dependencies' => array('extbase'),
761 ),
762 'foo' => array (
763 'dependencies' => array(),
764 ),
765 ),
766 array(
767 'core', 'setup', 'openid', 'extbase'
768 ),
769 array(
770 'core' => array(
771 'dependencies' => array(),
772 ),
773 'setup' => array (
774 'dependencies' => array('core'),
775 ),
776 'openid' => array(
777 'dependencies' => array('core', 'setup')
778 ),
779 'extbase' => array (
780 'dependencies' => array('core'),
781 ),
782 'foo' => array (
783 'dependencies' => array('openid', 'extbase'),
784 ),
785 'pt_extbase' => array (
786 'dependencies' => array('openid', 'extbase'),
787 ),
788 'news' => array (
789 'dependencies' => array('openid', 'extbase'),
790 ),
791 ),
792 ),
793 'Dummy Packages' => array(
794 array(
795 'A' => array(
796 'dependencies' => array('B', 'D', 'C'),
797 ),
798 'B' => array(
799 'dependencies' => array()
800 ),
801 'C' => array(
802 'dependencies' => array('E')
803 ),
804 'D' => array (
805 'dependencies' => array('E'),
806 ),
807 'E' => array (
808 'dependencies' => array(),
809 ),
810 'F' => array (
811 'dependencies' => array(),
812 ),
813 ),
814 array(
815 'B', 'C', 'E'
816 ),
817 array(
818 'B' => array(
819 'dependencies' => array(),
820 ),
821 'E' => array (
822 'dependencies' => array(),
823 ),
824 'C' => array (
825 'dependencies' => array('E'),
826 ),
827 'F' => array (
828 'dependencies' => array('B', 'C'),
829 ),
830 'D' => array(
831 'dependencies' => array('B', 'C'),
832 ),
833 'A' => array(
834 'dependencies' => array('B', 'C', 'D'),
835 ),
836 ),
837 ),
838 );
839 }
840
841 /**
842 * @test
843 * @dataProvider packageSortingDataProvider
844 */
845 public function sortAvailablePackagesByDependenciesMakesSureThatDependantPackagesAreStandingBeforeAPackageInTheInternalPackagesAndPackagesConfigurationArrays($unsorted, $frameworkPackageKeys, $expected) {
846 $unsortedPackageStatesConfiguration = array('packages' => $unsorted);
847 $expectedSortedPackageStatesConfiguration = array('packages' => $expected);
848
849 $unsortedPackages = array();
850 $packageKeys = array_keys($unsorted);
851 foreach ($packageKeys as $packageKey) {
852 $packageMock = $this->getMock('\TYPO3\Flow\Package\PackageInterface');
853 $packageMock->expects($this->any())->method('getPackageKey')->will($this->returnValue($packageKey));
854 $unsortedPackages[$packageKey] = $packageMock;
855 }
856
857 $typeAssignment = array(
858 array('', array('typo3-cms-framework'), array_diff($packageKeys, $frameworkPackageKeys)),
859 array('typo3-cms-framework', array(), $frameworkPackageKeys),
860 );
861
862 $packageManager = $this->getAccessibleMock('\TYPO3\CMS\Core\Package\PackageManager', array('resolvePackageDependencies','getPackageKeysOfType'));
863 $packageManager->expects($this->any())->method('getPackageKeysOfType')->will($this->returnValueMap($typeAssignment));
864 $packageManager->_set('packages', $unsortedPackages);
865 $packageManager->_set('packageStatesConfiguration', $unsortedPackageStatesConfiguration);
866 $packageManager->_call('sortAvailablePackagesByDependencies');
867
868 $expectedSortedPackageKeys = array_keys($expected);
869
870 $this->assertEquals($expectedSortedPackageKeys, array_keys($packageManager->_get('packages')), 'The packages have not been ordered according to their dependencies!');
871 $this->assertEquals($expectedSortedPackageStatesConfiguration, $packageManager->_get('packageStatesConfiguration'), 'The package states configurations have not been ordered according to their dependencies!');
872 }
873
874 /**
875 * @return array
876 */
877 public function buildDependencyGraphForPackagesBuildsCorrectGraphDataProvider() {
878 return array(
879 'TYPO3 Flow Packages' => array(
880 array(
881 'TYPO3.Flow' => array(
882 'dependencies' => array('Symfony.Component.Yaml', 'Doctrine.Common', 'Doctrine.DBAL', 'Doctrine.ORM')
883 ),
884 'Doctrine.ORM' => array(
885 'dependencies' => array('Doctrine.Common', 'Doctrine.DBAL')
886 ),
887 'Doctrine.Common' => array(
888 'dependencies' => array()
889 ),
890 'Doctrine.DBAL' => array(
891 'dependencies' => array('Doctrine.Common')
892 ),
893 'Symfony.Component.Yaml' => array(
894 'dependencies' => array()
895 ),
896 ),
897 array(
898 'TYPO3.Flow' => array(
899 'TYPO3.Flow' => FALSE,
900 'Doctrine.ORM' => TRUE,
901 'Doctrine.Common' => TRUE,
902 'Doctrine.DBAL' => TRUE,
903 'Symfony.Component.Yaml' => TRUE,
904 ),
905 'Doctrine.ORM' => array(
906 'TYPO3.Flow' => FALSE,
907 'Doctrine.ORM' => FALSE,
908 'Doctrine.Common' => TRUE,
909 'Doctrine.DBAL' => TRUE,
910 'Symfony.Component.Yaml' => FALSE,
911 ),
912 'Doctrine.Common' => array(
913 'TYPO3.Flow' => FALSE,
914 'Doctrine.ORM' => FALSE,
915 'Doctrine.Common' => FALSE,
916 'Doctrine.DBAL' => FALSE,
917 'Symfony.Component.Yaml' => FALSE,
918 ),
919 'Doctrine.DBAL' => array(
920 'TYPO3.Flow' => FALSE,
921 'Doctrine.ORM' => FALSE,
922 'Doctrine.Common' => TRUE,
923 'Doctrine.DBAL' => FALSE,
924 'Symfony.Component.Yaml' => FALSE,
925 ),
926 'Symfony.Component.Yaml' => array(
927 'TYPO3.Flow' => FALSE,
928 'Doctrine.ORM' => FALSE,
929 'Doctrine.Common' => FALSE,
930 'Doctrine.DBAL' => FALSE,
931 'Symfony.Component.Yaml' => FALSE,
932 ),
933 ),
934 ),
935 'TYPO3 CMS Extensions' => array(
936 array(
937 'core' => array(
938 'dependencies' => array(),
939 ),
940 'openid' => array(
941 'dependencies' => array('core', 'setup')
942 ),
943 'scheduler' => array (
944 'dependencies' => array('core'),
945 ),
946 'setup' => array (
947 'dependencies' => array('core'),
948 ),
949 'sv' => array (
950 'dependencies' => array('core'),
951 ),
952 ),
953 array(
954 'core' => array(
955 'core' => FALSE,
956 'setup' => FALSE,
957 'sv' => FALSE,
958 'scheduler' => FALSE,
959 'openid' => FALSE,
960 ),
961 'openid' => array(
962 'core' => TRUE,
963 'setup' => TRUE,
964 'sv' => FALSE,
965 'scheduler' => FALSE,
966 'openid' => FALSE,
967 ),
968 'scheduler' => array (
969 'core' => TRUE,
970 'setup' => FALSE,
971 'sv' => FALSE,
972 'scheduler' => FALSE,
973 'openid' => FALSE,
974 ),
975 'setup' => array (
976 'core' => TRUE,
977 'setup' => FALSE,
978 'sv' => FALSE,
979 'scheduler' => FALSE,
980 'openid' => FALSE,
981 ),
982 'sv' => array (
983 'core' => TRUE,
984 'setup' => FALSE,
985 'sv' => FALSE,
986 'scheduler' => FALSE,
987 'openid' => FALSE,
988 ),
989 ),
990 ),
991 'Dummy Packages' => array(
992 array(
993 'A' => array(
994 'dependencies' => array('B', 'D', 'C'),
995 ),
996 'B' => array(
997 'dependencies' => array()
998 ),
999 'C' => array(
1000 'dependencies' => array('E')
1001 ),
1002 'D' => array (
1003 'dependencies' => array('E'),
1004 ),
1005 'E' => array (
1006 'dependencies' => array(),
1007 ),
1008 'F' => array (
1009 'dependencies' => array(),
1010 ),
1011 ),
1012 array(
1013 'A' => array(
1014 'A' => FALSE,
1015 'B' => TRUE,
1016 'C' => TRUE,
1017 'D' => TRUE,
1018 'E' => FALSE,
1019 'F' => FALSE,
1020 ),
1021 'B' => array(
1022 'A' => FALSE,
1023 'B' => FALSE,
1024 'C' => FALSE,
1025 'D' => FALSE,
1026 'E' => FALSE,
1027 'F' => FALSE,
1028 ),
1029 'C' => array(
1030 'A' => FALSE,
1031 'B' => FALSE,
1032 'C' => FALSE,
1033 'D' => FALSE,
1034 'E' => TRUE,
1035 'F' => FALSE,
1036 ),
1037 'D' => array (
1038 'A' => FALSE,
1039 'B' => FALSE,
1040 'C' => FALSE,
1041 'D' => FALSE,
1042 'E' => TRUE,
1043 'F' => FALSE,
1044 ),
1045 'E' => array (
1046 'A' => FALSE,
1047 'B' => FALSE,
1048 'C' => FALSE,
1049 'D' => FALSE,
1050 'E' => FALSE,
1051 'F' => FALSE,
1052 ),
1053 'F' => array (
1054 'A' => FALSE,
1055 'B' => FALSE,
1056 'C' => FALSE,
1057 'D' => FALSE,
1058 'E' => FALSE,
1059 'F' => FALSE,
1060 ),
1061 ),
1062 ),
1063 );
1064 }
1065
1066 /**
1067 * @test
1068 * @dataProvider buildDependencyGraphForPackagesBuildsCorrectGraphDataProvider
1069 */
1070 public function buildDependencyGraphForPackagesBuildsCorrectGraph($packages, $expectedGraph) {
1071 $packageManager = $this->getAccessibleMock('\TYPO3\CMS\Core\Package\PackageManager', array('dummy'));
1072 $packageManager->_set('packageStatesConfiguration', array('packages' => $packages));
1073 $packageManager->_call('buildDependencyGraphForPackages', array_keys($packages));
1074
1075 $this->assertEquals($expectedGraph, $packageManager->_get('dependencyGraph'));
1076 }
1077
1078
1079 /**
1080 * @test
1081 * @expectedException \UnexpectedValueException
1082 */
1083 public function getAvailablePackageLoadingOrderThrowsExceptionWhenCycleDetected() {
1084 $unsorted = array(
1085 'A' => array(
1086 'dependencies' => array('B'),
1087 ),
1088 'B' => array(
1089 'dependencies' => array('A')
1090 ),
1091 );
1092 $unsortedPackageStatesConfiguration = array('packages' => $unsorted);
1093
1094 $unsortedPackages = array();
1095 $packageKeys = array_keys($unsorted);
1096 foreach ($packageKeys as $packageKey) {
1097 $packageMock = $this->getMock('\TYPO3\Flow\Package\PackageInterface');
1098 $packageMock->expects($this->any())->method('getPackageKey')->will($this->returnValue($packageKey));
1099 $unsortedPackages[$packageKey] = $packageMock;
1100 }
1101
1102 $typeAssignment = array(
1103 array('', array('typo3-cms-framework'), $packageKeys),
1104 array('typo3-cms-framework', array(), array()),
1105 );
1106
1107 $packageManager = $this->getAccessibleMock('\TYPO3\CMS\Core\Package\PackageManager', array('resolvePackageDependencies','getPackageKeysOfType'));
1108 $packageManager->expects($this->any())->method('getPackageKeysOfType')->will($this->returnValueMap($typeAssignment));
1109 $packageManager->_set('packages', $unsortedPackages);
1110 $packageManager->_set('packageStatesConfiguration', $unsortedPackageStatesConfiguration);
1111 $packageManager->_call('getAvailablePackageLoadingOrder');
1112 }
1113
1114 /**
1115 * @test
1116 * @expectedException \UnexpectedValueException
1117 */
1118 public function buildDependencyGraphForPackagesThrowsExceptionWhenDependencyOnUnavailablePackageDetected() {
1119 $packages = array(
1120 'A' => array(
1121 'dependencies' => array('B'),
1122 )
1123 );
1124 $packageManager = $this->getAccessibleMock('\TYPO3\CMS\Core\Package\PackageManager', array('dummy'));
1125 $packageManager->_set('packageStatesConfiguration', array('packages' => $packages));
1126 $packageManager->_call('buildDependencyGraphForPackages', array_keys($packages));
1127 }
1128
1129 /**
1130 * @return array
1131 */
1132 public function composerNamesAndPackageKeys() {
1133 return array(
1134 array('imagine/Imagine', 'imagine.Imagine'),
1135 array('imagine/imagine', 'imagine.Imagine'),
1136 array('typo3/flow', 'TYPO3.Flow'),
1137 array('TYPO3/Flow', 'TYPO3.Flow')
1138 );
1139 }
1140
1141 /**
1142 * @test
1143 * @dataProvider composerNamesAndPackageKeys
1144 */
1145 public function getPackageKeyFromComposerNameIgnoresCaseDifferences($composerName, $packageKey) {
1146 $packageStatesConfiguration = array('packages' =>
1147 array(
1148 'TYPO3.Flow' => array(
1149 'composerName' => 'typo3/flow'
1150 ),
1151 'imagine.Imagine' => array(
1152 'composerName' => 'imagine/Imagine'
1153 )
1154 )
1155 );
1156
1157 $packageManager = $this->getAccessibleMock('\TYPO3\Flow\Package\PackageManager', array('resolvePackageDependencies'));
1158 $packageManager->_set('packageStatesConfiguration', $packageStatesConfiguration);
1159
1160 $this->assertEquals($packageKey, $packageManager->_call('getPackageKeyFromComposerName', $composerName));
1161 }
1162 }