[BUGFIX] Load test classes in non composer mode 76/43876/4
authorHelmut Hummel <helmut.hummel@typo3.org>
Tue, 6 Oct 2015 22:49:52 +0000 (00:49 +0200)
committerWouter Wolters <typo3@wouterwolters.nl>
Wed, 7 Oct 2015 17:40:48 +0000 (19:40 +0200)
In non composer mode it has been impossible to register
classes for tests to be loaded (e.g. fixture classes) as long
as at least one autoload information was registered in the extension.

With this change, we will now evaluate an autoload-dev section
as well and also do not skip the scan of test classes when no autoload
section is defined in the extension for better backwards compatibility.

Resolves: #70472
Releases: master
Change-Id: I05559e2d28e4262583ea3cf95dca52573ac4c18f
Reviewed-on: http://review.typo3.org/43876
Reviewed-by: Nicole Cordes <typo3@cordes.co>
Tested-by: Nicole Cordes <typo3@cordes.co>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
typo3/sysext/core/Classes/Core/ClassLoadingInformation.php
typo3/sysext/core/Classes/Core/ClassLoadingInformationGenerator.php
typo3/sysext/core/Tests/Unit/Core/ClassLoadingInformationGeneratorTest.php

index f55c0d2..768dd9d 100644 (file)
@@ -74,7 +74,7 @@ class ClassLoadingInformation {
                $activeExtensionPackages = static::getActiveExtensionPackages();
 
                /** @var ClassLoadingInformationGenerator  $generator */
-               $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class, $composerClassLoader, $activeExtensionPackages, PATH_site);
+               $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class, $composerClassLoader, $activeExtensionPackages, PATH_site, self::isTestingContext());
                $classInfoFiles = $generator->buildAutoloadInformationFiles();
                GeneralUtility::writeFile(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSMAP_FILENAME, $classInfoFiles['classMapFile']);
                GeneralUtility::writeFile(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_PSR4_FILENAME, $classInfoFiles['psr-4File']);
@@ -128,7 +128,7 @@ class ClassLoadingInformation {
                $activeExtensionPackages = static::getActiveExtensionPackages();
 
                /** @var ClassLoadingInformationGenerator  $generator */
-               $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class, $composerClassLoader, $activeExtensionPackages, PATH_site);
+               $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class, $composerClassLoader, $activeExtensionPackages, PATH_site, self::isTestingContext());
 
                $classInformation = $generator->buildClassLoadingInformationForPackage($package);
                $composerClassLoader->addClassMap($classInformation['classMap']);
@@ -145,7 +145,7 @@ class ClassLoadingInformation {
         * @return string
         */
        static protected function getClassLoadingInformationDirectory() {
-               if (Bootstrap::getInstance()->getApplicationContext()->isTesting()) {
+               if (self::isTestingContext()) {
                        return PATH_site . self::AUTOLOAD_INFO_DIR_TESTS;
                } else {
                        return PATH_site . self::AUTOLOAD_INFO_DIR;
@@ -170,7 +170,7 @@ class ClassLoadingInformation {
                $autoloadInfoDir = self::getClassLoadingInformationDirectory();
                if (!file_exists($autoloadInfoDir)) {
                        GeneralUtility::mkdir_deep($autoloadInfoDir);
-               } elseif (Bootstrap::getInstance()->getApplicationContext()->isTesting()) {
+               } elseif (self::isTestingContext()) {
                        GeneralUtility::flushDirectory($autoloadInfoDir, TRUE);
                }
        }
@@ -186,6 +186,16 @@ class ClassLoadingInformation {
        }
 
        /**
+        * Internal method calling the bootstrap to get application context information
+        *
+        * @return bool
+        * @throws \TYPO3\CMS\Core\Exception
+        */
+       static protected function isTestingContext() {
+               return Bootstrap::getInstance()->getApplicationContext()->isTesting();
+       }
+
+       /**
         * Get all packages except the protected ones, as they are covered already
         *
         * @return PackageInterface[]
index cb8a226..b4642dc 100644 (file)
@@ -42,14 +42,21 @@ class ClassLoadingInformationGenerator {
        protected $installationRoot;
 
        /**
+        * @var bool
+        */
+       protected $isDevMode;
+
+       /**
         * @param ClassLoader $classLoader
         * @param array $activeExtensionPackages
         * @param string $installationRoot
+        * @param bool $isDevMode
         */
-       public function __construct(ClassLoader $classLoader, array $activeExtensionPackages = array(), $installationRoot) {
+       public function __construct(ClassLoader $classLoader, array $activeExtensionPackages, $installationRoot, $isDevMode = FALSE) {
                $this->classLoader = $classLoader;
                $this->activeExtensionPackages = $activeExtensionPackages;
                $this->installationRoot = $installationRoot;
+               $this->isDevMode = $isDevMode;
        }
 
        /**
@@ -67,12 +74,12 @@ class ClassLoadingInformationGenerator {
 
                if (empty($manifest->autoload)) {
                        // Legacy mode: Scan the complete extension directory for class files
-                       $classMap = $this->createClassMap($packagePath, $useRelativePaths, TRUE);
+                       $classMap = $this->createClassMap($packagePath, $useRelativePaths, !$this->isDevMode);
                } else {
-                       $autoloadDefinition = json_decode(json_encode($manifest->autoload), TRUE);
-                       if (!empty($autoloadDefinition['psr-4']) && is_array($autoloadDefinition['psr-4'])) {
+                       $autoloadPsr4 = $this->getAutoloadSectionFromManifest($manifest, 'psr-4');
+                       if (!empty($autoloadPsr4)) {
                                $classLoaderPrefixesPsr4 = $this->classLoader->getPrefixesPsr4();
-                               foreach ($autoloadDefinition['psr-4'] as $namespacePrefix => $path) {
+                               foreach ($autoloadPsr4 as $namespacePrefix => $path) {
                                        $namespacePath = $packagePath . $path;
                                        if ($useRelativePaths) {
                                                $psr4[$namespacePrefix] = $this->makePathRelative($namespacePath, realpath($namespacePath));
@@ -86,8 +93,9 @@ class ClassLoadingInformationGenerator {
                                        }
                                }
                        }
-                       if (!empty($autoloadDefinition['classmap']) && is_array($autoloadDefinition['classmap'])) {
-                               foreach ($autoloadDefinition['classmap'] as $path) {
+                       $autoloadClassmap = $this->getAutoloadSectionFromManifest($manifest, 'classmap');
+                       if (!empty($autoloadClassmap)) {
+                               foreach ($autoloadClassmap as $path) {
                                        $classMap = array_merge($classMap, $this->createClassMap($packagePath . $path, $useRelativePaths));
                                }
                        }
@@ -97,6 +105,30 @@ class ClassLoadingInformationGenerator {
        }
 
        /**
+        * Fetches class loading info from the according section from the manifest file.
+        * Development information will be extracted and merged as well.
+        *
+        * @param \stdClass $manifest
+        * @param string $section
+        * @return array
+        */
+       protected function getAutoloadSectionFromManifest($manifest, $section) {
+               $finalAutoloadSection = [];
+               $autoloadDefinition = json_decode(json_encode($manifest->autoload), TRUE);
+               if (!empty($autoloadDefinition[$section]) && is_array($autoloadDefinition[$section])) {
+                       $finalAutoloadSection = $autoloadDefinition[$section];
+               }
+               if ($this->isDevMode) {
+                       $autoloadDefinitionDev = json_decode(json_encode($manifest->{'autoload-dev'}), TRUE);
+                       if (!empty($autoloadDefinitionDev[$section]) && is_array($autoloadDefinitionDev[$section])) {
+                               $finalAutoloadSection = array_merge($finalAutoloadSection, $autoloadDefinitionDev[$section]);
+                       }
+               }
+
+               return $finalAutoloadSection;
+       }
+
+       /**
         * Creates a class map for a given (absolute) path
         *
         * @param string $classesPath
index 56f37be..8a53c50 100644 (file)
@@ -296,6 +296,108 @@ class ClassLoadingInformationGeneratorTest extends UnitTestCase {
        }
 
        /**
+        * Data provider for different autoload information
+        *
+        * @return array
+        */
+       public function autoloadDevFilesAreBuildCorrectlyDataProvider() {
+               return [
+                       'Psr-4 sections' => [
+                               [
+                                       'autoload' => [
+                                               'psr-4' => [
+                                                       'TYPO3\\CMS\\TestExtension\\' => 'Classes',
+                                               ],
+                                       ],
+                                       'autoload-dev' => [
+                                               'psr-4' => [
+                                                       'TYPO3\\CMS\\TestExtension\\Tests\\' => 'Tests',
+                                               ],
+                                       ],
+                               ],
+                               [
+                                       '\'TYPO3\\\\CMS\\\\TestExtension\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Classes\')',
+                                       '\'TYPO3\\\\CMS\\\\TestExtension\\\\Tests\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Tests\')',
+                               ],
+                               [],
+                       ],
+                       'Psr-4 sections with override' => [
+                               [
+                                       'autoload' => [
+                                               'psr-4' => [
+                                                       'TYPO3\\CMS\\TestExtension\\' => 'Classes',
+                                               ],
+                                       ],
+                                       'autoload-dev' => [
+                                               'psr-4' => [
+                                                       'TYPO3\\CMS\\TestExtension\\' => 'Tests',
+                                               ],
+                                       ],
+                               ],
+                               [
+                                       '!\'TYPO3\\\\CMS\\\\TestExtension\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Classes\')',
+                                       '\'TYPO3\\\\CMS\\\\TestExtension\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Tests\')',
+                               ],
+                               [],
+                       ],
+                       'Classmap section pointing to two files, one in dev and one not' => [
+                               [
+                                       'autoload' => [
+                                               'classmap' => [
+                                                       'Resources/PHP/Test.php',
+                                               ],
+                                       ],
+                                       'autoload-dev' => [
+                                               'classmap' => [
+                                                       'Resources/PHP/AnotherTestFile.php',
+                                               ],
+                                       ],
+                               ],
+                               [],
+                               [
+                                       '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Test.php\'',
+                                       '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/AnotherTestFile.php\'',
+                                       '!$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Subdirectory/SubdirectoryTest.php\'',
+                               ],
+                       ],
+               ];
+       }
+
+       /**
+        * @test
+        * @dataProvider autoloadDevFilesAreBuildCorrectlyDataProvider
+        *
+        * @param array $packageManifest
+        * @param array $expectedPsr4Files
+        * @param array $expectedClassMapFiles
+        */
+       public function autoloadDevFilesAreBuildCorrectly($packageManifest, $expectedPsr4Files, $expectedClassMapFiles) {
+               /** @var ClassLoader|\PHPUnit_Framework_MockObject_MockObject $classLoaderMock */
+               $classLoaderMock = $this->getMock(ClassLoader::class);
+               $generator = new ClassLoadingInformationGenerator($classLoaderMock, [$this->createPackageMock($packageManifest)], __DIR__, TRUE);
+               $files = $generator->buildAutoloadInformationFiles();
+
+               $this->assertArrayHasKey('psr-4File', $files);
+               $this->assertArrayHasKey('classMapFile', $files);
+               foreach ($expectedPsr4Files as $expectation) {
+                       if ($expectation[0] === '!') {
+                               $expectedCount = 0;
+                       } else {
+                               $expectedCount = 1;
+                       }
+                       $this->assertSame($expectedCount, substr_count($files['psr-4File'], $expectation), '' . $expectation);
+               }
+               foreach ($expectedClassMapFiles as $expectation) {
+                       if ($expectation[0] === '!') {
+                               $expectedCount = 0;
+                       } else {
+                               $expectedCount = 1;
+                       }
+                       $this->assertSame($expectedCount, substr_count($files['classMapFile'], $expectation), '' . $expectation);
+               }
+       }
+
+       /**
         * @param array Array which should be returned as composer manifest
         * @return PackageInterface
         */