From 81244e10cb0959ede7d570fc89f46a64901ba744 Mon Sep 17 00:00:00 2001
From: Christian Kuhn <lolli@schwarzbu.ch>
Date: Wed, 6 Jun 2018 01:12:07 +0200
Subject: [PATCH] [TASK] Functional tests without phpunit process isolation

We're finally able to manage our core internal framework state,
at least in the backend. This is a huge step. To proof this,
functional tests now execute without process isolation.

We need a couple of additional reset state methods. Those are
for now marked @internal to allow us changing this stuff if
needed later.

The SystemEnvironmentBuilder also needs a change to not
directly rely on PATH_thisScript anymore.

composer require --dev typo3/testing-framework ~4.1.0

Change-Id: I37fbee7e4cf6ccb2eec18d057b6b9671d6a85167
Resolves: #85649
Releases: master
Reviewed-on: https://review.typo3.org/57129
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
---
 composer.json                                 |  2 +-
 composer.lock                                 | 12 ++++----
 .../Classes/Core/SystemEnvironmentBuilder.php | 29 +++++++++++++++++--
 .../core/Classes/Database/ConnectionPool.php  | 12 ++++++++
 .../core/Classes/Utility/GeneralUtility.php   | 13 +++++++++
 .../Cache/Backend/MemcachedBackendTest.php    |  3 +-
 .../Cache/Backend/RedisBackendTest.php        |  2 ++
 typo3/sysext/core/composer.json               |  2 +-
 8 files changed, 63 insertions(+), 12 deletions(-)

diff --git a/composer.json b/composer.json
index 30e4a8987fa3..a5f5e940a8c3 100644
--- a/composer.json
+++ b/composer.json
@@ -66,7 +66,7 @@
 		"fiunchinho/phpunit-randomizer": "^4.0",
 		"friendsofphp/php-cs-fixer": "^2.12.2",
 		"typo3/cms-styleguide": "^9.1",
-		"typo3/testing-framework": "^4.0.2"
+		"typo3/testing-framework": "~4.1.0"
 	},
 	"suggest": {
 		"ext-gd": "GDlib/Freetype is required for building images with text (GIFBUILDER) and can also be used to scale images",
diff --git a/composer.lock b/composer.lock
index 47ba30809019..5e22986778ad 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "a71e25c63829d22da3013a310006d675",
+    "content-hash": "cb674f386df7a9d87641f393120ecbac",
     "packages": [
         {
             "name": "cogpowered/finediff",
@@ -4722,16 +4722,16 @@
         },
         {
             "name": "typo3/testing-framework",
-            "version": "4.0.2",
+            "version": "4.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/TYPO3/testing-framework.git",
-                "reference": "dadfd1c953856429ad2a1b9ab8b8bea779e1f960"
+                "reference": "f3371a5cedc30ada5bc38a4d3b3d92baea1d2873"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/TYPO3/testing-framework/zipball/dadfd1c953856429ad2a1b9ab8b8bea779e1f960",
-                "reference": "dadfd1c953856429ad2a1b9ab8b8bea779e1f960",
+                "url": "https://api.github.com/repos/TYPO3/testing-framework/zipball/f3371a5cedc30ada5bc38a4d3b3d92baea1d2873",
+                "reference": "f3371a5cedc30ada5bc38a4d3b3d92baea1d2873",
                 "shasum": ""
             },
             "require": {
@@ -4778,7 +4778,7 @@
                 "tests",
                 "typo3"
             ],
-            "time": "2018-07-23T11:56:51+00:00"
+            "time": "2018-07-25T20:17:43+00:00"
         },
         {
             "name": "webmozart/assert",
diff --git a/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php b/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php
index 2ed868d234b5..96e3e6e8df46 100644
--- a/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php
+++ b/typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php
@@ -267,18 +267,41 @@ class SystemEnvironmentBuilder
      */
     public static function initializeEnvironment(ApplicationContext $context)
     {
-        $sitePath = rtrim(PATH_site, '/');
+        $isCli = PHP_SAPI === 'cli';
+        // Absolute path of the entry script that was called
+        $scriptPath = PATH_thisScript;
+        $rootPath = rtrim(PATH_site, '/');
+        if (getenv('TYPO3_PATH_ROOT')) {
+            $rootPath = GeneralUtility::fixWindowsFilePath(getenv('TYPO3_PATH_ROOT'));
+            $rootPath = rtrim($rootPath, '/');
+            // Check if the root path has been set in the environment (e.g. by the composer installer)
+            if ($isCli && self::usesComposerClassLoading() && StringUtility::endsWith($scriptPath, 'typo3')) {
+                // PATH_thisScript is used for various path calculations based on the document root
+                // Therefore we assume it is always a subdirectory of the document root, which is not the case
+                // in composer mode on cli, as the binary is in the composer bin directory.
+                // Because of that, we enforce the document root path of this binary to be set
+                $scriptName = '/typo3/sysext/core/bin/typo3';
+            } else {
+                // Base the script path on the path taken from the environment
+                // to make relative path calculations work in case only one of both is symlinked
+                // or has the real path
+                $scriptName = substr($scriptPath, strlen($rootPath));
+            }
+            $scriptPath = $rootPath . $scriptName;
+        }
+
+        $sitePath = rtrim($rootPath, '/');
         $projectRootPath = GeneralUtility::fixWindowsFilePath(getenv('TYPO3_PATH_APP'));
         $isDifferentRootPath = ($projectRootPath && $projectRootPath !== $sitePath);
         Environment::initialize(
             $context,
-            PHP_SAPI === 'cli',
+            $isCli,
             self::usesComposerClassLoading(),
             $isDifferentRootPath ? $projectRootPath : $sitePath,
             $sitePath,
             $isDifferentRootPath ? $projectRootPath . '/var'    : $sitePath . '/typo3temp/var',
             $isDifferentRootPath ? $projectRootPath . '/config' : $sitePath . '/typo3conf',
-            PATH_thisScript,
+            $scriptPath,
             self::getTypo3Os() === 'WIN' ? 'WINDOWS' : 'UNIX'
         );
     }
diff --git a/typo3/sysext/core/Classes/Database/ConnectionPool.php b/typo3/sysext/core/Classes/Database/ConnectionPool.php
index 47e5ced106a2..5f7776634301 100644
--- a/typo3/sysext/core/Classes/Database/ConnectionPool.php
+++ b/typo3/sysext/core/Classes/Database/ConnectionPool.php
@@ -235,4 +235,16 @@ class ConnectionPool
     {
         return $this->customDoctrineTypes;
     }
+
+    /**
+     * Reset internal list of connections. This is an internal method (for now)
+     * currently used in functional tests only to close connections and start
+     * new ones in between single tests.
+     *
+     * @internal May be changed or removed any point in time
+     */
+    public function resetConnections(): void
+    {
+        static::$connections = [];
+    }
 }
diff --git a/typo3/sysext/core/Classes/Utility/GeneralUtility.php b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
index f2c8c7eedc54..58aae0174841 100644
--- a/typo3/sysext/core/Classes/Utility/GeneralUtility.php
+++ b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
@@ -4141,6 +4141,19 @@ class GeneralUtility
         }
     }
 
+    /**
+     * For testing purposes only!
+     * The functional test framework uses this to reset the internal $application context
+     * variable in between multiple tests before it is re-initialized using presetApplicationContext()
+     * which otherwise throws an exception if the internal variable is already set.
+     *
+     * @internal May be changed or removed any time
+     */
+    public static function resetApplicationContext(): void
+    {
+        static::$applicationContext = null;
+    }
+
     /**
      * Get the ApplicationContext
      *
diff --git a/typo3/sysext/core/Tests/Functional/Cache/Backend/MemcachedBackendTest.php b/typo3/sysext/core/Tests/Functional/Cache/Backend/MemcachedBackendTest.php
index 5ee450e3eae7..c03545b795c8 100644
--- a/typo3/sysext/core/Tests/Functional/Cache/Backend/MemcachedBackendTest.php
+++ b/typo3/sysext/core/Tests/Functional/Cache/Backend/MemcachedBackendTest.php
@@ -29,7 +29,8 @@ class MemcachedBackendTest extends FunctionalTestCase
      */
     protected function setUp()
     {
-        parent::setUp();
+        // Note this functional does NOT call parent::setUp() since it does
+        // not need a full blown instance and database
         if (!extension_loaded('memcache') && !extension_loaded('memcached')) {
             $this->markTestSkipped('Neither "memcache" nor "memcached" extension was available');
         }
diff --git a/typo3/sysext/core/Tests/Functional/Cache/Backend/RedisBackendTest.php b/typo3/sysext/core/Tests/Functional/Cache/Backend/RedisBackendTest.php
index 7e9600937acc..45f41a2e4fb9 100644
--- a/typo3/sysext/core/Tests/Functional/Cache/Backend/RedisBackendTest.php
+++ b/typo3/sysext/core/Tests/Functional/Cache/Backend/RedisBackendTest.php
@@ -32,6 +32,8 @@ class RedisBackendTest extends FunctionalTestCase
      */
     protected function setUp()
     {
+        // Note this functional does NOT call parent::setUp() since it does
+        // not need a full blown instance and database
         if (!extension_loaded('redis')) {
             $this->markTestSkipped('redis extension was not available');
         }
diff --git a/typo3/sysext/core/composer.json b/typo3/sysext/core/composer.json
index c81f31707338..0033f27c6d4a 100644
--- a/typo3/sysext/core/composer.json
+++ b/typo3/sysext/core/composer.json
@@ -47,7 +47,7 @@
 		"fiunchinho/phpunit-randomizer": "^4.0",
 		"friendsofphp/php-cs-fixer": "^2.12.2",
 		"typo3/cms-styleguide": "^9.1",
-		"typo3/testing-framework": "^4.0.2"
+		"typo3/testing-framework": "~4.1.0"
 	},
 	"suggest": {
 		"ext-fileinfo": "Used for proper file type detection in the file abstraction layer",
-- 
GitLab