[TASK] Make base test classes always available 59/49159/3
authorHelmut Hummel <info@helhum.io>
Wed, 1 Jun 2016 17:03:13 +0000 (19:03 +0200)
committerHelmut Hummel <typo3@helhum.io>
Wed, 27 Jul 2016 09:02:01 +0000 (11:02 +0200)
To ease the usage of the core with extensions,
we move the base test classes into a different place
to be able to make them available when typo3/cms is pulled in
as dependency, e.g. for testing extensions or in projects.

This is a non breaking change, as the classes will be available
in any case after that.

By doing so, we can nicely cleanup the .gitattributes file

Resolves: #76425
Releases: master, 7.6
Change-Id: I639f08a4e3e5dd73aa47570d326b72c64452acaf
Reviewed-on: https://review.typo3.org/49159
Reviewed-by: Peter Foerger <pfoerger@mac.com>
Tested-by: Peter Foerger <pfoerger@mac.com>
Reviewed-by: Mathias Brodala <mbrodala@pagemachine.de>
Reviewed-by: Timo Hund <timo.hund@yahoo.com>
Tested-by: Timo Hund <timo.hund@yahoo.com>
Reviewed-by: Helmut Hummel <typo3@helhum.io>
Tested-by: Helmut Hummel <typo3@helhum.io>
13 files changed:
.gitattributes
typo3/sysext/core/Classes/Tests/AccessibleObjectInterface.php [new file with mode: 0644]
typo3/sysext/core/Classes/Tests/BaseTestCase.php [new file with mode: 0644]
typo3/sysext/core/Classes/Tests/Exception.php [new file with mode: 0644]
typo3/sysext/core/Classes/Tests/FileStreamWrapper.php [new file with mode: 0644]
typo3/sysext/core/Classes/Tests/FunctionalTestCase.php [new file with mode: 0644]
typo3/sysext/core/Classes/Tests/UnitTestCase.php [new file with mode: 0644]
typo3/sysext/core/Tests/AccessibleObjectInterface.php [deleted file]
typo3/sysext/core/Tests/BaseTestCase.php [deleted file]
typo3/sysext/core/Tests/Exception.php [deleted file]
typo3/sysext/core/Tests/FileStreamWrapper.php [deleted file]
typo3/sysext/core/Tests/FunctionalTestCase.php [deleted file]
typo3/sysext/core/Tests/UnitTestCase.php [deleted file]

index 863f7aa..298a76f 100644 (file)
@@ -4,33 +4,4 @@
 /.travis.yml export-ignore
 /Build/ export-ignore
 /dynamicReturnTypeMeta.json export-ignore
-/typo3/sysext/backend/Tests/ export-ignore
-/typo3/sysext/belog/Tests/ export-ignore
-/typo3/sysext/beuser/Tests/ export-ignore
-/typo3/sysext/core/Tests/Acceptance/ export-ignore
-/typo3/sysext/core/Tests/Functional/ export-ignore
-/typo3/sysext/core/Tests/Integrity/ export-ignore
-/typo3/sysext/core/Tests/Legacy/ export-ignore
-/typo3/sysext/core/Tests/Unit/ export-ignore
-/typo3/sysext/dbal/Tests/ export-ignore
-/typo3/sysext/documentation/Tests/ export-ignore
-/typo3/sysext/extbase/Tests/ export-ignore
-/typo3/sysext/extensionmanager/Tests/ export-ignore
-/typo3/sysext/felogin/Tests/ export-ignore
-/typo3/sysext/fluid/Tests/ export-ignore
-/typo3/sysext/form/Tests/ export-ignore
-/typo3/sysext/frontend/Tests/ export-ignore
-/typo3/sysext/impexp/Tests/ export-ignore
-/typo3/sysext/indexed_search/Tests/ export-ignore
-/typo3/sysext/install/Tests/ export-ignore
-/typo3/sysext/lang/Tests/ export-ignore
-/typo3/sysext/lowlevel/Tests/ export-ignore
-/typo3/sysext/recordlist/Tests/ export-ignore
-/typo3/sysext/recycler/Tests/ export-ignore
-/typo3/sysext/reports/Tests/ export-ignore
-/typo3/sysext/rsaauth/Tests/ export-ignore
-/typo3/sysext/saltedpasswords/Tests/ export-ignore
-/typo3/sysext/scheduler/Tests/ export-ignore
-/typo3/sysext/setup/Tests/ export-ignore
-/typo3/sysext/sv/Tests/ export-ignore
-/typo3/sysext/workspaces/Tests/ export-ignore
+/typo3/sysext/*/Tests/ export-ignore
diff --git a/typo3/sysext/core/Classes/Tests/AccessibleObjectInterface.php b/typo3/sysext/core/Classes/Tests/AccessibleObjectInterface.php
new file mode 100644 (file)
index 0000000..b655c72
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+namespace TYPO3\CMS\Core\Tests;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * This interface defines the methods provided by TYPO3\CMS\Core\Tests\TestCase::getAccessibleMock.::
+ */
+interface AccessibleObjectInterface
+{
+    /**
+     * Calls the method $method using call_user_func* and returns its return value.
+     *
+     * @param string $methodName name of method to call, must not be empty
+     *
+     * @return mixed the return value from the method $methodName
+     */
+    public function _call($methodName);
+
+    /**
+     * Calls the method $method without using call_user_func* and returns its return value.
+     *
+     * @param string $methodName name of method to call, must not be empty
+     * @param mixed &$arg1 first argument given to method $methodName
+     * @param mixed &$arg2 second argument given to method $methodName
+     * @param mixed &$arg3 third argument given to method $methodName
+     * @param mixed &$arg4 fourth argument given to method $methodName
+     * @param mixed &$arg5 fifth argument given to method $methodName
+     * @param mixed &$arg6 sixth argument given to method $methodName
+     * @param mixed &$arg7 seventh argument given to method $methodName
+     * @param mixed &$arg8 eighth argument given to method $methodName
+     * @param mixed &$arg9 ninth argument given to method $methodName
+     *
+     * @return mixed the return value from the method $methodName
+     */
+    public function _callRef(
+        $methodName, &$arg1 = null, &$arg2 = null, &$arg3 = null, &$arg4 = null, &$arg5= null, &$arg6 = null, &$arg7 = null,
+        &$arg8 = null, &$arg9 = null
+    );
+
+    /**
+     * Sets the value of a property.
+     *
+     * @param string $propertyName name of property to set value for, must not be empty
+     * @param mixed $value the new value for the property defined in $propertyName
+     *
+     * @return void
+     */
+    public function _set($propertyName, $value);
+
+    /**
+     * Sets the value of a property by reference.
+     *
+     * @param string $propertyName name of property to set value for, must not be empty
+     * @param mixed &$value the new value for the property defined in $propertyName
+     *
+     * @return void
+     */
+    public function _setRef($propertyName, &$value);
+
+    /**
+     * Sets the value of a static property.
+     *
+     * @param string $propertyName name of property to set value for, must not be empty
+     * @param mixed $value the new value for the property defined in $propertyName
+     *
+     * @return void
+     */
+    public function _setStatic($propertyName, $value);
+
+    /**
+     * Gets the value of the given property.
+     *
+     * @param string $propertyName name of property to return value of, must not be empty
+     *
+     * @return mixed the value of the property $propertyName
+     */
+    public function _get($propertyName);
+
+    /**
+     * Gets the value of the given static property.
+     *
+     * @param string $propertyName name of property to return value of, must not be empty
+     *
+     * @return mixed the value of the static property $propertyName
+     */
+    public function _getStatic($propertyName);
+}
diff --git a/typo3/sysext/core/Classes/Tests/BaseTestCase.php b/typo3/sysext/core/Classes/Tests/BaseTestCase.php
new file mode 100644 (file)
index 0000000..e7975ba
--- /dev/null
@@ -0,0 +1,291 @@
+<?php
+namespace TYPO3\CMS\Core\Tests;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\StringUtility;
+
+/**
+ * The mother of all test cases.
+ *
+ * Don't sub class this test case but rather choose a more specialized base test case,
+ * such as UnitTestCase or FunctionalTestCase
+ *
+ */
+abstract class BaseTestCase extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Whether global variables should be backed up
+     *
+     * @var bool
+     */
+    protected $backupGlobals = true;
+
+    /**
+     * Whether static attributes should be backed up
+     *
+     * @var bool
+     */
+    protected $backupStaticAttributes = false;
+
+    /**
+     * Creates a mock object which allows for calling protected methods and access of protected properties.
+     *
+     * @param string $originalClassName name of class to create the mock object of, must not be empty
+     * @param string[]|null $methods name of the methods to mock, null for "mock no methods"
+     * @param array $arguments arguments to pass to constructor
+     * @param string $mockClassName the class name to use for the mock class
+     * @param bool $callOriginalConstructor whether to call the constructor
+     * @param bool $callOriginalClone whether to call the __clone method
+     * @param bool $callAutoload whether to call any autoload function
+     *
+     * @return \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
+     *         a mock of $originalClassName with access methods added
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function getAccessibleMock(
+        $originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '',
+        $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true
+    ) {
+        if ($originalClassName === '') {
+            throw new \InvalidArgumentException('$originalClassName must not be empty.', 1334701880);
+        }
+
+        return $this->getMock(
+            $this->buildAccessibleProxy($originalClassName),
+            $methods,
+            $arguments,
+            $mockClassName,
+            $callOriginalConstructor,
+            $callOriginalClone,
+            $callAutoload
+        );
+    }
+
+    /**
+     * Returns a mock object which allows for calling protected methods and access
+     * of protected properties. Concrete methods to mock can be specified with
+     * the last parameter
+     *
+     * @param string $originalClassName Full qualified name of the original class
+     * @param array $arguments
+     * @param string $mockClassName
+     * @param bool $callOriginalConstructor
+     * @param bool $callOriginalClone
+     * @param bool $callAutoload
+     * @param array $mockedMethods
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @return \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
+     *
+     */
+    protected function getAccessibleMockForAbstractClass(
+        $originalClassName, array $arguments = array(), $mockClassName = '',
+        $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = array()
+    ) {
+        if ($originalClassName === '') {
+            throw new \InvalidArgumentException('$originalClassName must not be empty.', 1384268260);
+        }
+
+        return $this->getMockForAbstractClass(
+            $this->buildAccessibleProxy($originalClassName),
+            $arguments,
+            $mockClassName,
+            $callOriginalConstructor,
+            $callOriginalClone,
+            $callAutoload,
+            $mockedMethods
+        );
+    }
+
+    /**
+     * Creates a proxy class of the specified class which allows
+     * for calling even protected methods and access of protected properties.
+     *
+     * @param string $className Name of class to make available, must not be empty
+     * @return string Fully qualified name of the built class, will not be empty
+     */
+    protected function buildAccessibleProxy($className)
+    {
+        $accessibleClassName = $this->getUniqueId('Tx_Phpunit_AccessibleProxy');
+        $class = new \ReflectionClass($className);
+        $abstractModifier = $class->isAbstract() ? 'abstract ' : '';
+
+        eval(
+            $abstractModifier . 'class ' . $accessibleClassName .
+                ' extends ' . $className . ' implements ' . \TYPO3\CMS\Core\Tests\AccessibleObjectInterface::class . ' {' .
+                    'public function _call($methodName) {' .
+                        'if ($methodName === \'\') {' .
+                            'throw new \InvalidArgumentException(\'$methodName must not be empty.\', 1334663993);' .
+                        '}' .
+                        '$args = func_get_args();' .
+                        'return call_user_func_array(array($this, $methodName), array_slice($args, 1));' .
+                    '}' .
+                    'public function _callRef(' .
+                        '$methodName, &$arg1 = NULL, &$arg2 = NULL, &$arg3 = NULL, &$arg4 = NULL, &$arg5= NULL, &$arg6 = NULL, ' .
+                        '&$arg7 = NULL, &$arg8 = NULL, &$arg9 = NULL' .
+                    ') {' .
+                        'if ($methodName === \'\') {' .
+                            'throw new \InvalidArgumentException(\'$methodName must not be empty.\', 1334664210);' .
+                        '}' .
+                        'switch (func_num_args()) {' .
+                            'case 0:' .
+                                'throw new RuntimeException(\'The case of 0 arguments is not supposed to happen.\', 1334703124);' .
+                                'break;' .
+                            'case 1:' .
+                                '$returnValue = $this->$methodName();' .
+                                'break;' .
+                            'case 2:' .
+                                '$returnValue = $this->$methodName($arg1);' .
+                                'break;' .
+                            'case 3:' .
+                                '$returnValue = $this->$methodName($arg1, $arg2);' .
+                                'break;' .
+                            'case 4:' .
+                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3);' .
+                                'break;' .
+                            'case 5:' .
+                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4);' .
+                                'break;' .
+                            'case 6:' .
+                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5);' .
+                                'break;' .
+                            'case 7:' .
+                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6);' .
+                                'break;' .
+                            'case 8:' .
+                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7);' .
+                                'break;' .
+                            'case 9:' .
+                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8);' .
+                                'break;' .
+                            'case 10:' .
+                                '$returnValue = $this->$methodName(' .
+                                    '$arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8, $arg9' .
+                                ');' .
+                                'break;' .
+                            'default:' .
+                                'throw new \InvalidArgumentException(' .
+                                    '\'_callRef currently only allows calls to methods with no more than 9 parameters.\'' .
+                                ');' .
+                        '}' .
+                        'return $returnValue;' .
+                    '}' .
+                    'public function _set($propertyName, $value) {' .
+                        'if ($propertyName === \'\') {' .
+                            'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664355);' .
+                        '}' .
+                        '$this->$propertyName = $value;' .
+                    '}' .
+                    'public function _setRef($propertyName, &$value) {' .
+                        'if ($propertyName === \'\') {' .
+                            'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664545);' .
+                        '}' .
+                        '$this->$propertyName = $value;' .
+                    '}' .
+                    'public function _setStatic($propertyName, $value) {' .
+                        'if ($propertyName === \'\') {' .
+                            'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1344242602);' .
+                        '}' .
+                        'self::$$propertyName = $value;' .
+                    '}' .
+                    'public function _get($propertyName) {' .
+                        'if ($propertyName === \'\') {' .
+                            'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664967);' .
+                        '}' .
+                        'return $this->$propertyName;' .
+                    '}' .
+                    'public function _getStatic($propertyName) {' .
+                        'if ($propertyName === \'\') {' .
+                            'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1344242603);' .
+                        '}' .
+                        'return self::$$propertyName;' .
+                    '}' .
+            '}'
+        );
+
+        return $accessibleClassName;
+    }
+
+    /**
+     * Helper function to call protected or private methods
+     *
+     * @param object $object The object to be invoked
+     * @param string $name the name of the method to call
+     * @return mixed
+     */
+    protected function callInaccessibleMethod($object, $name)
+    {
+        // Remove first two arguments ($object and $name)
+        $arguments = func_get_args();
+        array_splice($arguments, 0, 2);
+
+        $reflectionObject = new \ReflectionObject($object);
+        $reflectionMethod = $reflectionObject->getMethod($name);
+        $reflectionMethod->setAccessible(true);
+        return $reflectionMethod->invokeArgs($object, $arguments);
+    }
+
+    /**
+     * Injects $dependency into property $name of $target
+     *
+     * This is a convenience method for setting a protected or private property in
+     * a test subject for the purpose of injecting a dependency.
+     *
+     * @param object $target The instance which needs the dependency
+     * @param string $name Name of the property to be injected
+     * @param mixed $dependency The dependency to inject – usually an object but can also be any other type
+     * @return void
+     * @throws \RuntimeException
+     * @throws \InvalidArgumentException
+     */
+    protected function inject($target, $name, $dependency)
+    {
+        if (!is_object($target)) {
+            throw new \InvalidArgumentException('Wrong type for argument $target, must be object.');
+        }
+
+        $objectReflection = new \ReflectionObject($target);
+        $methodNamePart = strtoupper($name[0]) . substr($name, 1);
+        if ($objectReflection->hasMethod('set' . $methodNamePart)) {
+            $methodName = 'set' . $methodNamePart;
+            $target->$methodName($dependency);
+        } elseif ($objectReflection->hasMethod('inject' . $methodNamePart)) {
+            $methodName = 'inject' . $methodNamePart;
+            $target->$methodName($dependency);
+        } elseif ($objectReflection->hasProperty($name)) {
+            $property = $objectReflection->getProperty($name);
+            $property->setAccessible(true);
+            $property->setValue($target, $dependency);
+        } else {
+            throw new \RuntimeException('Could not inject ' . $name . ' into object of type ' . get_class($target));
+        }
+    }
+
+    /**
+     * Create and return a unique id optionally prepended by a given string
+     *
+     * This function is used because on windows and in cygwin environments uniqid() has a resolution of one second which
+     * results in identical ids if simply uniqid('Foo'); is called.
+     *
+     * @param string $prefix
+     * @return string
+     */
+    protected function getUniqueId($prefix = '')
+    {
+        return $prefix . StringUtility::getUniqueId(mt_rand());
+    }
+}
diff --git a/typo3/sysext/core/Classes/Tests/Exception.php b/typo3/sysext/core/Classes/Tests/Exception.php
new file mode 100644 (file)
index 0000000..dd97aba
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace TYPO3\CMS\Core\Tests;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * An exception - Thrown in abstract test cases to mark
+ * a test configuration or setup error.
+ */
+class Exception extends \Exception
+{
+}
diff --git a/typo3/sysext/core/Classes/Tests/FileStreamWrapper.php b/typo3/sysext/core/Classes/Tests/FileStreamWrapper.php
new file mode 100644 (file)
index 0000000..01d96a8
--- /dev/null
@@ -0,0 +1,610 @@
+<?php
+namespace TYPO3\CMS\Core\Tests;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Stream wrapper for the file:// protocol
+ *
+ * Implementation details:
+ * Due to the nature of PHP, it is not possible to switch to the default handler
+ * other then restoring the default handler for file:// and registering it again
+ * around each call.
+ * It is important that the default handler is restored to allow autoloading (including)
+ * of files during the test run.
+ * For each method allowed to pass paths, the passed path is checked against the
+ * the list of paths to overlay and rewritten if needed.
+ *
+ * = Usage =
+ * <code title="Add use statements">
+ * use org\bovigo\vfs\vfsStream;
+ * use org\bovigo\vfs\visitor\vfsStreamStructureVisitor;
+ * </code>
+ *
+ * <code title="Usage in test">
+ * $root = \org\bovigo\vfs\vfsStream::setup('root');
+ * $subfolder = \org\bovigo\vfs\vfsStream::newDirectory('fileadmin');
+ * $root->addChild($subfolder);
+ * // Load fixture files and folders from disk
+ * \org\bovigo\vfs\vfsStream::copyFromFileSystem(__DIR__ . '/Fixture/Files', $subfolder, 1024*1024);
+ * FileStreamWrapper::init(PATH_site);
+ * FileStreamWrapper::registerOverlayPath('fileadmin', 'vfs://root/fileadmin');
+ *
+ * // Use file functions as usual
+ * mkdir(PATH_site . 'fileadmin/test/');
+ * $file = PATH_site . 'fileadmin/test/Foo.bar';
+ * file_put_contents($file, 'Baz');
+ * $content = file_get_contents($file);
+ * $this->assertSame('Baz', $content);
+ *
+ * $this->assertEqual(**array(file system structure as array**), vfsStream::inspect(new vfsStreamStructureVisitor())->getStructure());
+ *
+ * FileStreamWrapper::destroy();
+ * </code>
+ *
+ * @see http://www.php.net/manual/en/class.streamwrapper.php
+ */
+class FileStreamWrapper
+{
+    /**
+     * @var resource
+     */
+    protected $dirHandle = null;
+
+    /**
+     * @var resource
+     */
+    protected $fileHandle = null;
+
+    /**
+     * Switch whether class has already been registered as stream wrapper or not
+     *
+     * @type bool
+     */
+    protected static $registered = false;
+
+    /**
+     * Array of paths to overlay
+     *
+     * @var array
+     */
+    protected static $overlayPaths = array();
+
+    /**
+     * The first part of each (absolute) path that shall be ignored
+     *
+     * @var string
+     */
+    protected static $rootPath = '';
+
+    /**
+     * Initialize the stream wrapper with a root path and register itself
+     *
+     * @param $rootPath
+     * @return void
+     */
+    public static function init($rootPath)
+    {
+        self::$rootPath = rtrim(str_replace('\\', '/', $rootPath), '/') . '/';
+        self::register();
+    }
+
+    /**
+     * Unregister the stream wrapper and reset all static members to their default values
+     * @return void
+     */
+    public static function destroy()
+    {
+        self::$overlayPaths = array();
+        self::$rootPath = '';
+        if (self::$registered) {
+            self::restore();
+        }
+    }
+
+    /**
+     * Register a path relative to the root path (set in init) to be overlaid
+     *
+     * @param string $overlay Relative path to the root folder
+     * @param string $replace The path that should replace the overlay path
+     * @param bool $createFolder TRUE of the folder should be created (mkdir)
+     * @return void
+     */
+    public static function registerOverlayPath($overlay, $replace, $createFolder = true)
+    {
+        $overlay = trim(str_replace('\\', '/', $overlay), '/') . '/';
+        $replace = rtrim(str_replace('\\', '/', $replace), '/') . '/';
+        self::$overlayPaths[$overlay] = $replace;
+        if ($createFolder) {
+            mkdir($replace);
+        }
+    }
+
+    /**
+     * Checks and overlays a path
+     *
+     * @param string $path The path to check
+     * @return string The potentially overlaid path
+     */
+    protected static function overlayPath($path)
+    {
+        $path = str_replace('\\', '/', $path);
+        $hasOverlay = false;
+        if (strpos($path, self::$rootPath) !== 0) {
+            // Path is not below root path, ignore it
+            return $path;
+        }
+
+        $newPath = ltrim(substr($path, strlen(self::$rootPath)), '/');
+        foreach (self::$overlayPaths as $overlay => $replace) {
+            if (strpos($newPath, $overlay) === 0) {
+                $newPath = $replace . substr($newPath, strlen($overlay));
+                $hasOverlay = true;
+                break;
+            }
+        }
+        return $hasOverlay ? $newPath : $path;
+    }
+
+    /**
+     * Method to register the stream wrapper
+     *
+     * If the stream is already registered the method returns silently. If there
+     * is already another stream wrapper registered for the scheme used by
+     * file:// scheme a \BadFunctionCallException will be thrown.
+     *
+     * @throws \BadFunctionCallException
+     * @return void
+     */
+    protected static function register()
+    {
+        if (self::$registered) {
+            return;
+        }
+
+        if (@stream_wrapper_unregister('file') === false) {
+            throw new \BadFunctionCallException('Cannot unregister file:// stream wrapper.', 1396340331);
+        }
+        if (@stream_wrapper_register('file', __CLASS__) === false) {
+            throw new \BadFunctionCallException('A handler has already been registered for the file:// scheme.', 1396340332);
+        }
+
+        self::$registered = true;
+    }
+
+    /**
+     * Restore the file handler
+     *
+     * @return void
+     */
+    protected static function restore()
+    {
+        if (!self::$registered) {
+            return;
+        }
+        if (@stream_wrapper_restore('file') === false) {
+            throw new \BadFunctionCallException('Cannot restore the default file:// stream handler.', 1396340333);
+        }
+        self::$registered = false;
+    }
+
+    /*
+     * The following list of functions is implemented as of
+     * @see http://www.php.net/manual/en/streamwrapper.dir-closedir.php
+     */
+
+    /**
+     * Close the directory
+     *
+     * @return bool
+     */
+    public function dir_closedir()
+    {
+        if ($this->dirHandle === null) {
+            return false;
+        } else {
+            self::restore();
+            closedir($this->dirHandle);
+            self::register();
+            $this->dirHandle = null;
+            return true;
+        }
+    }
+
+    /**
+     * Opens a directory for reading
+     *
+     * @param string $path
+     * @param int $options
+     * @return bool
+     */
+    public function dir_opendir($path, $options = 0)
+    {
+        if ($this->dirHandle !== null) {
+            return false;
+        }
+        self::restore();
+        $path = self::overlayPath($path);
+        $this->dirHandle = opendir($path);
+        self::register();
+        return $this->dirHandle !== false;
+    }
+
+    /**
+     * Read a single filename of a directory
+     *
+     * @return string|bool
+     */
+    public function dir_readdir()
+    {
+        if ($this->dirHandle === null) {
+            return false;
+        }
+        self::restore();
+        $success = readdir($this->dirHandle);
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Reset directory name pointer
+     *
+     * @return bool
+     */
+    public function dir_rewinddir()
+    {
+        if ($this->dirHandle === null) {
+            return false;
+        }
+        self::restore();
+        rewinddir($this->dirHandle);
+        self::register();
+        return true;
+    }
+
+    /**
+     * Create a directory
+     *
+     * @param string $path
+     * @param int $mode
+     * @param int $options
+     * @return bool
+     */
+    public function mkdir($path, $mode, $options = 0)
+    {
+        self::restore();
+        $path = self::overlayPath($path);
+        $success = mkdir($path, $mode, (bool)($options & STREAM_MKDIR_RECURSIVE));
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Rename a file
+     *
+     * @param string $pathFrom
+     * @param string $pathTo
+     * @return bool
+     */
+    public function rename($pathFrom, $pathTo)
+    {
+        self::restore();
+        $pathFrom = self::overlayPath($pathFrom);
+        $pathTo = self::overlayPath($pathTo);
+        $success = rename($pathFrom, $pathTo);
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Remove a directory
+     *
+     * @param string $path
+     * @return bool
+     */
+    public function rmdir($path)
+    {
+        self::restore();
+        $path = self::overlayPath($path);
+        $success = rmdir($path);
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Retrieve the underlying resource
+     *
+     * @param int $castAs Can be STREAM_CAST_FOR_SELECT when stream_select()
+     * is calling stream_cast() or STREAM_CAST_AS_STREAM when stream_cast()
+     * is called for other uses.
+     * @return resource|bool
+     */
+    public function stream_cast($castAs)
+    {
+        if ($this->fileHandle !== null && $castAs & STREAM_CAST_AS_STREAM) {
+            return $this->fileHandle;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Close a file
+     *
+     */
+    public function stream_close()
+    {
+        self::restore();
+        if ($this->fileHandle !== null) {
+            fclose($this->fileHandle);
+            $this->fileHandle = null;
+        }
+        self::register();
+    }
+
+    /**
+     * Test for end-of-file on a file pointer
+     *
+     * @return bool
+     */
+    public function stream_eof()
+    {
+        if ($this->fileHandle === null) {
+            return false;
+        }
+        self::restore();
+        $success = feof($this->fileHandle);
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Flush the output
+     *
+     * @return bool
+     */
+    public function stream_flush()
+    {
+        if ($this->fileHandle === null) {
+            return false;
+        }
+        self::restore();
+        $success = fflush($this->fileHandle);
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Advisory file locking
+     *
+     * @param int $operation
+     * @return bool
+     */
+    public function stream_lock($operation)
+    {
+        if ($this->fileHandle === null) {
+            return false;
+        }
+        self::restore();
+        $success = flock($this->fileHandle, $operation);
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Change file options
+     *
+     * @param string $path
+     * @param int $options
+     * @param mixed $value
+     * @return bool
+     */
+    public function stream_metadata($path, $options, $value)
+    {
+        self::restore();
+        $path = self::overlayPath($path);
+        switch ($options) {
+            case STREAM_META_TOUCH:
+                $success = touch($path, $value[0], $value[1]);
+                break;
+            case STREAM_META_OWNER_NAME:
+                // Fall through
+            case STREAM_META_OWNER:
+                $success = chown($path, $value);
+                break;
+            case STREAM_META_GROUP_NAME:
+                // Fall through
+            case STREAM_META_GROUP:
+                $success = chgrp($path, $value);
+                break;
+            case STREAM_META_ACCESS:
+                $success = chmod($path, $value);
+                break;
+            default:
+                $success = false;
+        }
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Open a file
+     *
+     * @param string $path
+     * @param string $mode
+     * @param int $options
+     * @param string &$opened_path
+     * @return bool
+     */
+    public function stream_open($path, $mode, $options, &$opened_path)
+    {
+        if ($this->fileHandle !== null) {
+            return false;
+        }
+        self::restore();
+        $path = self::overlayPath($path);
+        $this->fileHandle = fopen($path, $mode, (bool)($options & STREAM_USE_PATH));
+        self::register();
+        return $this->fileHandle !== false;
+    }
+
+    /**
+     * Read from a file
+     *
+     * @param int $length
+     * @return string
+     */
+    public function stream_read($length)
+    {
+        if ($this->fileHandle === null) {
+            return false;
+        }
+        self::restore();
+        $content = fread($this->fileHandle, $length);
+        self::register();
+        return $content;
+    }
+
+    /**
+     * Seek to specific location in a stream
+     *
+     * @param int $offset
+     * @param int $whence = SEEK_SET
+     * @return bool
+     */
+    public function stream_seek($offset, $whence = SEEK_SET)
+    {
+        if ($this->fileHandle === null) {
+            return false;
+        }
+        self::restore();
+        $success = fseek($this->fileHandle, $offset, $whence);
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Change stream options (not implemented)
+     *
+     * @param int $option
+     * @param int $arg1
+     * @param int $arg2
+     * @return bool
+     */
+    public function stream_set_option($option, $arg1, $arg2)
+    {
+        return false;
+    }
+
+    /**
+     * Retrieve information about a file resource
+     *
+     * @return array
+     */
+    public function stream_stat()
+    {
+        if ($this->fileHandle === null) {
+            return false;
+        }
+        self::restore();
+        $stats = fstat($this->fileHandle);
+        self::register();
+        return $stats;
+    }
+
+    /**
+     * Retrieve the current position of a stream
+     *
+     * @return int
+     */
+    public function stream_tell()
+    {
+        if ($this->fileHandle === null) {
+            return false;
+        }
+        self::restore();
+        $position = ftell($this->fileHandle);
+        self::register();
+        return $position;
+    }
+
+    /**
+     * Truncates a file to the given size
+     *
+     * @param int $size Truncate to this size
+     * @return bool
+     */
+    public function stream_truncate($size)
+    {
+        if ($this->fileHandle === null) {
+            return false;
+        }
+        self::restore();
+        $success = ftruncate($this->fileHandle, $size);
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Write to stream
+     *
+     * @param string $data
+     * @return int
+     */
+    public function stream_write($data)
+    {
+        if ($this->fileHandle === null) {
+            return false;
+        }
+        self::restore();
+        $length = fwrite($this->fileHandle, $data);
+        self::register();
+        return $length;
+    }
+
+    /**
+     * Unlink a file
+     *
+     * @param string $path
+     * @return bool
+     */
+    public function unlink($path)
+    {
+        self::restore();
+        $path = self::overlayPath($path);
+        $success = unlink($path);
+        self::register();
+        return $success;
+    }
+
+    /**
+     * Retrieve information about a file
+     *
+     * @param string $path
+     * @param int $flags
+     * @return array
+     */
+    public function url_stat($path, $flags)
+    {
+        self::restore();
+        $path = self::overlayPath($path);
+        if ($flags & STREAM_URL_STAT_LINK) {
+            $information = @lstat($path);
+        } else {
+            $information = @stat($path);
+        }
+        self::register();
+        return $information;
+    }
+}
diff --git a/typo3/sysext/core/Classes/Tests/FunctionalTestCase.php b/typo3/sysext/core/Classes/Tests/FunctionalTestCase.php
new file mode 100644 (file)
index 0000000..b5a26ec
--- /dev/null
@@ -0,0 +1,411 @@
+<?php
+namespace TYPO3\CMS\Core\Tests;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Tests\Functional\Framework\Frontend\Response;
+
+/**
+ * Base test case class for functional tests, all TYPO3 CMS
+ * functional tests should extend from this class!
+ *
+ * If functional tests need additional setUp() and tearDown() code,
+ * they *must* call parent::setUp() and parent::tearDown() to properly
+ * set up and destroy the test system.
+ *
+ * The functional test system creates a full new TYPO3 CMS instance
+ * within typo3temp/ of the base system and the bootstraps this TYPO3 instance.
+ * This abstract class takes care of creating this instance with its
+ * folder structure and a LocalConfiguration, creates an own database
+ * for each test run and imports tables of loaded extensions.
+ *
+ * Functional tests must be run standalone (calling native phpunit
+ * directly) and can not be executed by eg. the ext:phpunit backend module.
+ * Additionally, the script must be called from the document root
+ * of the instance, otherwise path calculation is not successfully.
+ *
+ * Call whole functional test suite, example:
+ * - cd /var/www/t3master/foo  # Document root of CMS instance, here is index.php of frontend
+ * - typo3/../bin/phpunit -c typo3/sysext/core/Build/FunctionalTests.xml
+ *
+ * Call single test case, example:
+ * - cd /var/www/t3master/foo  # Document root of CMS instance, here is index.php of frontend
+ * - typo3/../bin/phpunit \
+ *     --process-isolation \
+ *     --bootstrap typo3/sysext/core/Build/FunctionalTestsBootstrap.php \
+ *     typo3/sysext/core/Tests/Functional/DataHandling/DataHandlerTest.php
+ */
+abstract class FunctionalTestCase extends BaseTestCase
+{
+    /**
+     * Core extensions to load.
+     *
+     * If the test case needs additional core extensions as requirement,
+     * they can be noted here and will be added to LocalConfiguration
+     * extension list and ext_tables.sql of those extensions will be applied.
+     *
+     * This property will stay empty in this abstract, so it is possible
+     * to just overwrite it in extending classes. Extensions noted here will
+     * be loaded for every test of a test case and it is not possible to change
+     * the list of loaded extensions between single tests of a test case.
+     *
+     * A default list of core extensions is always loaded.
+     *
+     * @see FunctionalTestCaseUtility $defaultActivatedCoreExtensions
+     * @var array
+     */
+    protected $coreExtensionsToLoad = array();
+
+    /**
+     * Array of test/fixture extensions paths that should be loaded for a test.
+     *
+     * This property will stay empty in this abstract, so it is possible
+     * to just overwrite it in extending classes. Extensions noted here will
+     * be loaded for every test of a test case and it is not possible to change
+     * the list of loaded extensions between single tests of a test case.
+     *
+     * Given path is expected to be relative to your document root, example:
+     *
+     * array(
+     *   'typo3conf/ext/some_extension/Tests/Functional/Fixtures/Extensions/test_extension',
+     *   'typo3conf/ext/base_extension',
+     * );
+     *
+     * Extensions in this array are linked to the test instance, loaded
+     * and their ext_tables.sql will be applied.
+     *
+     * @var array
+     */
+    protected $testExtensionsToLoad = array();
+
+    /**
+     * Array of test/fixture folder or file paths that should be linked for a test.
+     *
+     * This property will stay empty in this abstract, so it is possible
+     * to just overwrite it in extending classes. Path noted here will
+     * be linked for every test of a test case and it is not possible to change
+     * the list of folders between single tests of a test case.
+     *
+     * array(
+     *   'link-source' => 'link-destination'
+     * );
+     *
+     * Given paths are expected to be relative to the test instance root.
+     * The array keys are the source paths and the array values are the destination
+     * paths, example:
+     *
+     * array(
+     *   'typo3/sysext/impext/Tests/Functional/Fixtures/Folders/fileadmin/user_upload' =>
+     *   'fileadmin/user_upload',
+     *   'typo3conf/ext/my_own_ext/Tests/Functional/Fixtures/Folders/uploads/tx_myownext' =>
+     *   'uploads/tx_myownext'
+     * );
+     *
+     * To be able to link from my_own_ext the extension path needs also to be registered in
+     * property $testExtensionsToLoad
+     *
+     * @var array
+     */
+    protected $pathsToLinkInTestInstance = array();
+
+    /**
+     * This configuration array is merged with TYPO3_CONF_VARS
+     * that are set in default configuration and factory configuration
+     *
+     * @var array
+     */
+    protected $configurationToUseInTestInstance = array();
+
+    /**
+     * Array of folders that should be created inside the test instance document root.
+     *
+     * This property will stay empty in this abstract, so it is possible
+     * to just overwrite it in extending classes. Path noted here will
+     * be linked for every test of a test case and it is not possible to change
+     * the list of folders between single tests of a test case.
+     *
+     * Per default the following folder are created
+     * /fileadmin
+     * /typo3temp
+     * /typo3conf
+     * /typo3conf/ext
+     * /uploads
+     *
+     * To create additional folders add the paths to this array. Given paths are expected to be
+     * relative to the test instance root and have to begin with a slash. Example:
+     *
+     * array(
+     *   'fileadmin/user_upload'
+     * );
+     *
+     * @var array
+     */
+    protected $additionalFoldersToCreate = array();
+
+    /**
+     * The fixture which is used when initializing a backend user
+     *
+     * @var string
+     */
+    protected $backendUserFixture = 'typo3/sysext/core/Tests/Functional/Fixtures/be_users.xml';
+
+    /**
+     * Private utility class used in setUp() and tearDown(). Do NOT use in test cases!
+     *
+     * @var \TYPO3\CMS\Core\Tests\FunctionalTestCaseBootstrapUtility
+     */
+    private $bootstrapUtility = null;
+
+    /**
+     * Calculate a "unique" identifier for the test database and the
+     * instance patch based on the given test case class name.
+     *
+     * @return string
+     */
+    protected function getInstanceIdentifier()
+    {
+        return FunctionalTestCaseBootstrapUtility::getInstanceIdentifier(get_class($this));
+    }
+
+    /**
+     * Calculates path to TYPO3 CMS test installation for this test case.
+     *
+     * @return string
+     */
+    protected function getInstancePath()
+    {
+        return FunctionalTestCaseBootstrapUtility::getInstancePath(get_class($this));
+    }
+
+    /**
+     * Set up creates a test instance and database.
+     *
+     * This method should be called with parent::setUp() in your test cases!
+     *
+     * @return void
+     */
+    protected function setUp()
+    {
+        if (!defined('ORIGINAL_ROOT')) {
+            $this->markTestSkipped('Functional tests must be called through phpunit on CLI');
+        }
+        $this->bootstrapUtility = new FunctionalTestCaseBootstrapUtility();
+        $this->bootstrapUtility->setUp(
+            get_class($this),
+            $this->coreExtensionsToLoad,
+            $this->testExtensionsToLoad,
+            $this->pathsToLinkInTestInstance,
+            $this->configurationToUseInTestInstance,
+            $this->additionalFoldersToCreate
+        );
+    }
+
+    /**
+     * Get DatabaseConnection instance - $GLOBALS['TYPO3_DB']
+     *
+     * This method should be used instead of direct access to
+     * $GLOBALS['TYPO3_DB'] for easy IDE auto completion.
+     *
+     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
+     */
+    protected function getDatabaseConnection()
+    {
+        return $GLOBALS['TYPO3_DB'];
+    }
+
+    /**
+     * Initialize backend user
+     *
+     * @param int $userUid uid of the user we want to initialize. This user must exist in the fixture file
+     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
+     * @throws Exception
+     */
+    protected function setUpBackendUserFromFixture($userUid)
+    {
+        $this->importDataSet(ORIGINAL_ROOT . $this->backendUserFixture);
+        $database = $this->getDatabaseConnection();
+        $userRow = $database->exec_SELECTgetSingleRow('*', 'be_users', 'uid = ' . (int)$userUid);
+
+        /** @var $backendUser \TYPO3\CMS\Core\Authentication\BackendUserAuthentication */
+        $backendUser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Authentication\BackendUserAuthentication::class);
+        $sessionId = $backendUser->createSessionId();
+        $_COOKIE['be_typo_user'] = $sessionId;
+        $backendUser->id = $sessionId;
+        $backendUser->sendNoCacheHeaders = false;
+        $backendUser->dontSetCookie = true;
+        $backendUser->createUserSession($userRow);
+
+        $GLOBALS['BE_USER'] = $backendUser;
+        $GLOBALS['BE_USER']->start();
+        if (!is_array($GLOBALS['BE_USER']->user) || !$GLOBALS['BE_USER']->user['uid']) {
+            throw new Exception(
+                'Can not initialize backend user',
+                1377095807
+            );
+        }
+        $GLOBALS['BE_USER']->backendCheckLogin();
+
+        return $backendUser;
+    }
+
+    /**
+     * Imports a data set represented as XML into the test database,
+     *
+     * @param string $path Absolute path to the XML file containing the data set to load
+     * @return void
+     * @throws Exception
+     */
+    protected function importDataSet($path)
+    {
+        if (!is_file($path)) {
+            throw new Exception(
+                'Fixture file ' . $path . ' not found',
+                1376746261
+            );
+        }
+
+        $database = $this->getDatabaseConnection();
+
+        $fileContent = file_get_contents($path);
+        // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
+        $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
+        $xml = simplexml_load_string($fileContent);
+        libxml_disable_entity_loader($previousValueOfEntityLoader);
+        $foreignKeys = array();
+
+        /** @var $table \SimpleXMLElement */
+        foreach ($xml->children() as $table) {
+            $insertArray = array();
+
+            /** @var $column \SimpleXMLElement */
+            foreach ($table->children() as $column) {
+                $columnName = $column->getName();
+                $columnValue = null;
+
+                if (isset($column['ref'])) {
+                    list($tableName, $elementId) = explode('#', $column['ref']);
+                    $columnValue = $foreignKeys[$tableName][$elementId];
+                } elseif (isset($column['is-NULL']) && ($column['is-NULL'] === 'yes')) {
+                    $columnValue = null;
+                } else {
+                    $columnValue = (string)$table->$columnName;
+                }
+
+                $insertArray[$columnName] = $columnValue;
+            }
+
+            $tableName = $table->getName();
+            $result = $database->exec_INSERTquery($tableName, $insertArray);
+            if ($result === false) {
+                throw new Exception(
+                    'Error when processing fixture file: ' . $path . ' Can not insert data to table ' . $tableName . ': ' . $database->sql_error(),
+                    1376746262
+                );
+            }
+            if (isset($table['id'])) {
+                $elementId = (string)$table['id'];
+                $foreignKeys[$tableName][$elementId] = $database->sql_insert_id();
+            }
+        }
+    }
+
+    /**
+     * @param int $pageId
+     * @param array $typoScriptFiles
+     */
+    protected function setUpFrontendRootPage($pageId, array $typoScriptFiles = array())
+    {
+        $pageId = (int)$pageId;
+        $page = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'pages', 'uid=' . $pageId);
+
+        if (empty($page)) {
+            $this->fail('Cannot set up frontend root page "' . $pageId . '"');
+        }
+
+        $pagesFields = array(
+            'is_siteroot' => 1
+        );
+
+        $this->getDatabaseConnection()->exec_UPDATEquery('pages', 'uid=' . $pageId, $pagesFields);
+
+        $templateFields = array(
+            'pid' => $pageId,
+            'title' => '',
+            'config' => '',
+            'clear' => 3,
+            'root' => 1,
+        );
+
+        foreach ($typoScriptFiles as $typoScriptFile) {
+            $templateFields['config'] .= '<INCLUDE_TYPOSCRIPT: source="FILE:' . $typoScriptFile . '">' . LF;
+        }
+
+        $this->getDatabaseConnection()->exec_INSERTquery('sys_template', $templateFields);
+    }
+
+    /**
+     * @param int $pageId
+     * @param int $languageId
+     * @param int $backendUserId
+     * @param int $workspaceId
+     * @param bool $failOnFailure
+     * @param int $frontendUserId
+     * @return Response
+     */
+    protected function getFrontendResponse($pageId, $languageId = 0, $backendUserId = 0, $workspaceId = 0, $failOnFailure = true, $frontendUserId = 0)
+    {
+        $pageId = (int)$pageId;
+        $languageId = (int)$languageId;
+
+        $additionalParameter = '';
+
+        if (!empty($frontendUserId)) {
+            $additionalParameter .= '&frontendUserId=' . (int)$frontendUserId;
+        }
+        if (!empty($backendUserId)) {
+            $additionalParameter .= '&backendUserId=' . (int)$backendUserId;
+        }
+        if (!empty($workspaceId)) {
+            $additionalParameter .= '&workspaceId=' . (int)$workspaceId;
+        }
+
+        $arguments = array(
+            'documentRoot' => $this->getInstancePath(),
+            'requestUrl' => 'http://localhost/?id=' . $pageId . '&L=' . $languageId . $additionalParameter,
+        );
+
+        $template = new \Text_Template(ORIGINAL_ROOT . 'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/request.tpl');
+        $template->setVar(
+            array(
+                'arguments' => var_export($arguments, true),
+                'originalRoot' => ORIGINAL_ROOT,
+            )
+        );
+
+        $php = \PHPUnit_Util_PHP::factory();
+        $response = $php->runJob($template->render());
+        $result = json_decode($response['stdout'], true);
+
+        if ($result === null) {
+            $this->fail('Frontend Response is empty');
+        }
+
+        if ($failOnFailure && $result['status'] === Response::STATUS_Failure) {
+            $this->fail('Frontend Response has failure:' . LF . $result['error']);
+        }
+
+        $response = new Response($result['status'], $result['content'], $result['error']);
+        return $response;
+    }
+}
diff --git a/typo3/sysext/core/Classes/Tests/UnitTestCase.php b/typo3/sysext/core/Classes/Tests/UnitTestCase.php
new file mode 100644 (file)
index 0000000..35b9bdc
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+namespace TYPO3\CMS\Core\Tests;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\PathUtility;
+use TYPO3\CMS\Core\Utility\StringUtility;
+
+/**
+ * Base test case for unit tests.
+ *
+ * This class currently only inherits the base test case. However, it is recommended
+ * to extend this class for unit test cases instead of the base test case because if,
+ * at some point, specific behavior needs to be implemented for unit tests, your test cases
+ * will profit from it automatically.
+ *
+ */
+abstract class UnitTestCase extends BaseTestCase
+{
+    /**
+     * @todo make LoadedExtensionsArray serializable instead
+     *
+     * @var array
+     */
+    protected $backupGlobalsBlacklist = array('TYPO3_LOADED_EXT');
+
+    /**
+     * Absolute path to files that should be removed after a test.
+     * Handled in tearDown. Tests can register here to get any files
+     * within typo3temp/ or typo3conf/ext cleaned up again.
+     *
+     * @var array
+     */
+    protected $testFilesToDelete = array();
+
+    /**
+     * Unset all additional properties of test classes to help PHP
+     * garbage collection. This reduces memory footprint with lots
+     * of tests.
+     *
+     * If owerwriting tearDown() in test classes, please call
+     * parent::tearDown() at the end. Unsetting of own properties
+     * is not needed this way.
+     *
+     * @throws \RuntimeException
+     * @return void
+     */
+    protected function tearDown()
+    {
+        // Unset properties of test classes to safe memory
+        $reflection = new \ReflectionObject($this);
+        foreach ($reflection->getProperties() as $property) {
+            $declaringClass = $property->getDeclaringClass()->getName();
+            if (
+                !$property->isStatic()
+                && $declaringClass !== \TYPO3\CMS\Core\Tests\UnitTestCase::class
+                && $declaringClass !== \TYPO3\CMS\Core\Tests\BaseTestCase::class
+                && strpos($property->getDeclaringClass()->getName(), 'PHPUnit_') !== 0
+            ) {
+                $propertyName = $property->getName();
+                unset($this->$propertyName);
+            }
+        }
+        unset($reflection);
+
+        // Delete registered test files and directories
+        foreach ($this->testFilesToDelete as $absoluteFileName) {
+            $absoluteFileName = GeneralUtility::fixWindowsFilePath(PathUtility::getCanonicalPath($absoluteFileName));
+            if (!GeneralUtility::validPathStr($absoluteFileName)) {
+                throw new \RuntimeException('tearDown() cleanup: Filename contains illegal characters', 1410633087);
+            }
+            if (!StringUtility::beginsWith($absoluteFileName, PATH_site . 'typo3temp/')) {
+                throw new \RuntimeException(
+                    'tearDown() cleanup:  Files to delete must be within typo3temp/',
+                    1410633412
+                );
+            }
+            // file_exists returns false for links pointing to not existing targets, so handle links before next check.
+            if (@is_link($absoluteFileName) || @is_file($absoluteFileName)) {
+                unlink($absoluteFileName);
+            } elseif (@is_dir($absoluteFileName)) {
+                GeneralUtility::rmdir($absoluteFileName, true);
+            } else {
+                throw new \RuntimeException('tearDown() cleanup: File, link or directory does not exist', 1410633510);
+            }
+        }
+        $this->testFilesToDelete = array();
+    }
+}
diff --git a/typo3/sysext/core/Tests/AccessibleObjectInterface.php b/typo3/sysext/core/Tests/AccessibleObjectInterface.php
deleted file mode 100644 (file)
index b655c72..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Tests;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * This interface defines the methods provided by TYPO3\CMS\Core\Tests\TestCase::getAccessibleMock.::
- */
-interface AccessibleObjectInterface
-{
-    /**
-     * Calls the method $method using call_user_func* and returns its return value.
-     *
-     * @param string $methodName name of method to call, must not be empty
-     *
-     * @return mixed the return value from the method $methodName
-     */
-    public function _call($methodName);
-
-    /**
-     * Calls the method $method without using call_user_func* and returns its return value.
-     *
-     * @param string $methodName name of method to call, must not be empty
-     * @param mixed &$arg1 first argument given to method $methodName
-     * @param mixed &$arg2 second argument given to method $methodName
-     * @param mixed &$arg3 third argument given to method $methodName
-     * @param mixed &$arg4 fourth argument given to method $methodName
-     * @param mixed &$arg5 fifth argument given to method $methodName
-     * @param mixed &$arg6 sixth argument given to method $methodName
-     * @param mixed &$arg7 seventh argument given to method $methodName
-     * @param mixed &$arg8 eighth argument given to method $methodName
-     * @param mixed &$arg9 ninth argument given to method $methodName
-     *
-     * @return mixed the return value from the method $methodName
-     */
-    public function _callRef(
-        $methodName, &$arg1 = null, &$arg2 = null, &$arg3 = null, &$arg4 = null, &$arg5= null, &$arg6 = null, &$arg7 = null,
-        &$arg8 = null, &$arg9 = null
-    );
-
-    /**
-     * Sets the value of a property.
-     *
-     * @param string $propertyName name of property to set value for, must not be empty
-     * @param mixed $value the new value for the property defined in $propertyName
-     *
-     * @return void
-     */
-    public function _set($propertyName, $value);
-
-    /**
-     * Sets the value of a property by reference.
-     *
-     * @param string $propertyName name of property to set value for, must not be empty
-     * @param mixed &$value the new value for the property defined in $propertyName
-     *
-     * @return void
-     */
-    public function _setRef($propertyName, &$value);
-
-    /**
-     * Sets the value of a static property.
-     *
-     * @param string $propertyName name of property to set value for, must not be empty
-     * @param mixed $value the new value for the property defined in $propertyName
-     *
-     * @return void
-     */
-    public function _setStatic($propertyName, $value);
-
-    /**
-     * Gets the value of the given property.
-     *
-     * @param string $propertyName name of property to return value of, must not be empty
-     *
-     * @return mixed the value of the property $propertyName
-     */
-    public function _get($propertyName);
-
-    /**
-     * Gets the value of the given static property.
-     *
-     * @param string $propertyName name of property to return value of, must not be empty
-     *
-     * @return mixed the value of the static property $propertyName
-     */
-    public function _getStatic($propertyName);
-}
diff --git a/typo3/sysext/core/Tests/BaseTestCase.php b/typo3/sysext/core/Tests/BaseTestCase.php
deleted file mode 100644 (file)
index e7975ba..0000000
+++ /dev/null
@@ -1,291 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Tests;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use TYPO3\CMS\Core\Utility\StringUtility;
-
-/**
- * The mother of all test cases.
- *
- * Don't sub class this test case but rather choose a more specialized base test case,
- * such as UnitTestCase or FunctionalTestCase
- *
- */
-abstract class BaseTestCase extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * Whether global variables should be backed up
-     *
-     * @var bool
-     */
-    protected $backupGlobals = true;
-
-    /**
-     * Whether static attributes should be backed up
-     *
-     * @var bool
-     */
-    protected $backupStaticAttributes = false;
-
-    /**
-     * Creates a mock object which allows for calling protected methods and access of protected properties.
-     *
-     * @param string $originalClassName name of class to create the mock object of, must not be empty
-     * @param string[]|null $methods name of the methods to mock, null for "mock no methods"
-     * @param array $arguments arguments to pass to constructor
-     * @param string $mockClassName the class name to use for the mock class
-     * @param bool $callOriginalConstructor whether to call the constructor
-     * @param bool $callOriginalClone whether to call the __clone method
-     * @param bool $callAutoload whether to call any autoload function
-     *
-     * @return \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
-     *         a mock of $originalClassName with access methods added
-     *
-     * @throws \InvalidArgumentException
-     */
-    protected function getAccessibleMock(
-        $originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '',
-        $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true
-    ) {
-        if ($originalClassName === '') {
-            throw new \InvalidArgumentException('$originalClassName must not be empty.', 1334701880);
-        }
-
-        return $this->getMock(
-            $this->buildAccessibleProxy($originalClassName),
-            $methods,
-            $arguments,
-            $mockClassName,
-            $callOriginalConstructor,
-            $callOriginalClone,
-            $callAutoload
-        );
-    }
-
-    /**
-     * Returns a mock object which allows for calling protected methods and access
-     * of protected properties. Concrete methods to mock can be specified with
-     * the last parameter
-     *
-     * @param string $originalClassName Full qualified name of the original class
-     * @param array $arguments
-     * @param string $mockClassName
-     * @param bool $callOriginalConstructor
-     * @param bool $callOriginalClone
-     * @param bool $callAutoload
-     * @param array $mockedMethods
-     *
-     * @throws \InvalidArgumentException
-     *
-     * @return \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
-     *
-     */
-    protected function getAccessibleMockForAbstractClass(
-        $originalClassName, array $arguments = array(), $mockClassName = '',
-        $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = array()
-    ) {
-        if ($originalClassName === '') {
-            throw new \InvalidArgumentException('$originalClassName must not be empty.', 1384268260);
-        }
-
-        return $this->getMockForAbstractClass(
-            $this->buildAccessibleProxy($originalClassName),
-            $arguments,
-            $mockClassName,
-            $callOriginalConstructor,
-            $callOriginalClone,
-            $callAutoload,
-            $mockedMethods
-        );
-    }
-
-    /**
-     * Creates a proxy class of the specified class which allows
-     * for calling even protected methods and access of protected properties.
-     *
-     * @param string $className Name of class to make available, must not be empty
-     * @return string Fully qualified name of the built class, will not be empty
-     */
-    protected function buildAccessibleProxy($className)
-    {
-        $accessibleClassName = $this->getUniqueId('Tx_Phpunit_AccessibleProxy');
-        $class = new \ReflectionClass($className);
-        $abstractModifier = $class->isAbstract() ? 'abstract ' : '';
-
-        eval(
-            $abstractModifier . 'class ' . $accessibleClassName .
-                ' extends ' . $className . ' implements ' . \TYPO3\CMS\Core\Tests\AccessibleObjectInterface::class . ' {' .
-                    'public function _call($methodName) {' .
-                        'if ($methodName === \'\') {' .
-                            'throw new \InvalidArgumentException(\'$methodName must not be empty.\', 1334663993);' .
-                        '}' .
-                        '$args = func_get_args();' .
-                        'return call_user_func_array(array($this, $methodName), array_slice($args, 1));' .
-                    '}' .
-                    'public function _callRef(' .
-                        '$methodName, &$arg1 = NULL, &$arg2 = NULL, &$arg3 = NULL, &$arg4 = NULL, &$arg5= NULL, &$arg6 = NULL, ' .
-                        '&$arg7 = NULL, &$arg8 = NULL, &$arg9 = NULL' .
-                    ') {' .
-                        'if ($methodName === \'\') {' .
-                            'throw new \InvalidArgumentException(\'$methodName must not be empty.\', 1334664210);' .
-                        '}' .
-                        'switch (func_num_args()) {' .
-                            'case 0:' .
-                                'throw new RuntimeException(\'The case of 0 arguments is not supposed to happen.\', 1334703124);' .
-                                'break;' .
-                            'case 1:' .
-                                '$returnValue = $this->$methodName();' .
-                                'break;' .
-                            'case 2:' .
-                                '$returnValue = $this->$methodName($arg1);' .
-                                'break;' .
-                            'case 3:' .
-                                '$returnValue = $this->$methodName($arg1, $arg2);' .
-                                'break;' .
-                            'case 4:' .
-                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3);' .
-                                'break;' .
-                            'case 5:' .
-                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4);' .
-                                'break;' .
-                            'case 6:' .
-                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5);' .
-                                'break;' .
-                            'case 7:' .
-                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6);' .
-                                'break;' .
-                            'case 8:' .
-                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7);' .
-                                'break;' .
-                            'case 9:' .
-                                '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8);' .
-                                'break;' .
-                            'case 10:' .
-                                '$returnValue = $this->$methodName(' .
-                                    '$arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8, $arg9' .
-                                ');' .
-                                'break;' .
-                            'default:' .
-                                'throw new \InvalidArgumentException(' .
-                                    '\'_callRef currently only allows calls to methods with no more than 9 parameters.\'' .
-                                ');' .
-                        '}' .
-                        'return $returnValue;' .
-                    '}' .
-                    'public function _set($propertyName, $value) {' .
-                        'if ($propertyName === \'\') {' .
-                            'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664355);' .
-                        '}' .
-                        '$this->$propertyName = $value;' .
-                    '}' .
-                    'public function _setRef($propertyName, &$value) {' .
-                        'if ($propertyName === \'\') {' .
-                            'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664545);' .
-                        '}' .
-                        '$this->$propertyName = $value;' .
-                    '}' .
-                    'public function _setStatic($propertyName, $value) {' .
-                        'if ($propertyName === \'\') {' .
-                            'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1344242602);' .
-                        '}' .
-                        'self::$$propertyName = $value;' .
-                    '}' .
-                    'public function _get($propertyName) {' .
-                        'if ($propertyName === \'\') {' .
-                            'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664967);' .
-                        '}' .
-                        'return $this->$propertyName;' .
-                    '}' .
-                    'public function _getStatic($propertyName) {' .
-                        'if ($propertyName === \'\') {' .
-                            'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1344242603);' .
-                        '}' .
-                        'return self::$$propertyName;' .
-                    '}' .
-            '}'
-        );
-
-        return $accessibleClassName;
-    }
-
-    /**
-     * Helper function to call protected or private methods
-     *
-     * @param object $object The object to be invoked
-     * @param string $name the name of the method to call
-     * @return mixed
-     */
-    protected function callInaccessibleMethod($object, $name)
-    {
-        // Remove first two arguments ($object and $name)
-        $arguments = func_get_args();
-        array_splice($arguments, 0, 2);
-
-        $reflectionObject = new \ReflectionObject($object);
-        $reflectionMethod = $reflectionObject->getMethod($name);
-        $reflectionMethod->setAccessible(true);
-        return $reflectionMethod->invokeArgs($object, $arguments);
-    }
-
-    /**
-     * Injects $dependency into property $name of $target
-     *
-     * This is a convenience method for setting a protected or private property in
-     * a test subject for the purpose of injecting a dependency.
-     *
-     * @param object $target The instance which needs the dependency
-     * @param string $name Name of the property to be injected
-     * @param mixed $dependency The dependency to inject – usually an object but can also be any other type
-     * @return void
-     * @throws \RuntimeException
-     * @throws \InvalidArgumentException
-     */
-    protected function inject($target, $name, $dependency)
-    {
-        if (!is_object($target)) {
-            throw new \InvalidArgumentException('Wrong type for argument $target, must be object.');
-        }
-
-        $objectReflection = new \ReflectionObject($target);
-        $methodNamePart = strtoupper($name[0]) . substr($name, 1);
-        if ($objectReflection->hasMethod('set' . $methodNamePart)) {
-            $methodName = 'set' . $methodNamePart;
-            $target->$methodName($dependency);
-        } elseif ($objectReflection->hasMethod('inject' . $methodNamePart)) {
-            $methodName = 'inject' . $methodNamePart;
-            $target->$methodName($dependency);
-        } elseif ($objectReflection->hasProperty($name)) {
-            $property = $objectReflection->getProperty($name);
-            $property->setAccessible(true);
-            $property->setValue($target, $dependency);
-        } else {
-            throw new \RuntimeException('Could not inject ' . $name . ' into object of type ' . get_class($target));
-        }
-    }
-
-    /**
-     * Create and return a unique id optionally prepended by a given string
-     *
-     * This function is used because on windows and in cygwin environments uniqid() has a resolution of one second which
-     * results in identical ids if simply uniqid('Foo'); is called.
-     *
-     * @param string $prefix
-     * @return string
-     */
-    protected function getUniqueId($prefix = '')
-    {
-        return $prefix . StringUtility::getUniqueId(mt_rand());
-    }
-}
diff --git a/typo3/sysext/core/Tests/Exception.php b/typo3/sysext/core/Tests/Exception.php
deleted file mode 100644 (file)
index dd97aba..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Tests;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * An exception - Thrown in abstract test cases to mark
- * a test configuration or setup error.
- */
-class Exception extends \Exception
-{
-}
diff --git a/typo3/sysext/core/Tests/FileStreamWrapper.php b/typo3/sysext/core/Tests/FileStreamWrapper.php
deleted file mode 100644 (file)
index 01d96a8..0000000
+++ /dev/null
@@ -1,610 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Tests;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Stream wrapper for the file:// protocol
- *
- * Implementation details:
- * Due to the nature of PHP, it is not possible to switch to the default handler
- * other then restoring the default handler for file:// and registering it again
- * around each call.
- * It is important that the default handler is restored to allow autoloading (including)
- * of files during the test run.
- * For each method allowed to pass paths, the passed path is checked against the
- * the list of paths to overlay and rewritten if needed.
- *
- * = Usage =
- * <code title="Add use statements">
- * use org\bovigo\vfs\vfsStream;
- * use org\bovigo\vfs\visitor\vfsStreamStructureVisitor;
- * </code>
- *
- * <code title="Usage in test">
- * $root = \org\bovigo\vfs\vfsStream::setup('root');
- * $subfolder = \org\bovigo\vfs\vfsStream::newDirectory('fileadmin');
- * $root->addChild($subfolder);
- * // Load fixture files and folders from disk
- * \org\bovigo\vfs\vfsStream::copyFromFileSystem(__DIR__ . '/Fixture/Files', $subfolder, 1024*1024);
- * FileStreamWrapper::init(PATH_site);
- * FileStreamWrapper::registerOverlayPath('fileadmin', 'vfs://root/fileadmin');
- *
- * // Use file functions as usual
- * mkdir(PATH_site . 'fileadmin/test/');
- * $file = PATH_site . 'fileadmin/test/Foo.bar';
- * file_put_contents($file, 'Baz');
- * $content = file_get_contents($file);
- * $this->assertSame('Baz', $content);
- *
- * $this->assertEqual(**array(file system structure as array**), vfsStream::inspect(new vfsStreamStructureVisitor())->getStructure());
- *
- * FileStreamWrapper::destroy();
- * </code>
- *
- * @see http://www.php.net/manual/en/class.streamwrapper.php
- */
-class FileStreamWrapper
-{
-    /**
-     * @var resource
-     */
-    protected $dirHandle = null;
-
-    /**
-     * @var resource
-     */
-    protected $fileHandle = null;
-
-    /**
-     * Switch whether class has already been registered as stream wrapper or not
-     *
-     * @type bool
-     */
-    protected static $registered = false;
-
-    /**
-     * Array of paths to overlay
-     *
-     * @var array
-     */
-    protected static $overlayPaths = array();
-
-    /**
-     * The first part of each (absolute) path that shall be ignored
-     *
-     * @var string
-     */
-    protected static $rootPath = '';
-
-    /**
-     * Initialize the stream wrapper with a root path and register itself
-     *
-     * @param $rootPath
-     * @return void
-     */
-    public static function init($rootPath)
-    {
-        self::$rootPath = rtrim(str_replace('\\', '/', $rootPath), '/') . '/';
-        self::register();
-    }
-
-    /**
-     * Unregister the stream wrapper and reset all static members to their default values
-     * @return void
-     */
-    public static function destroy()
-    {
-        self::$overlayPaths = array();
-        self::$rootPath = '';
-        if (self::$registered) {
-            self::restore();
-        }
-    }
-
-    /**
-     * Register a path relative to the root path (set in init) to be overlaid
-     *
-     * @param string $overlay Relative path to the root folder
-     * @param string $replace The path that should replace the overlay path
-     * @param bool $createFolder TRUE of the folder should be created (mkdir)
-     * @return void
-     */
-    public static function registerOverlayPath($overlay, $replace, $createFolder = true)
-    {
-        $overlay = trim(str_replace('\\', '/', $overlay), '/') . '/';
-        $replace = rtrim(str_replace('\\', '/', $replace), '/') . '/';
-        self::$overlayPaths[$overlay] = $replace;
-        if ($createFolder) {
-            mkdir($replace);
-        }
-    }
-
-    /**
-     * Checks and overlays a path
-     *
-     * @param string $path The path to check
-     * @return string The potentially overlaid path
-     */
-    protected static function overlayPath($path)
-    {
-        $path = str_replace('\\', '/', $path);
-        $hasOverlay = false;
-        if (strpos($path, self::$rootPath) !== 0) {
-            // Path is not below root path, ignore it
-            return $path;
-        }
-
-        $newPath = ltrim(substr($path, strlen(self::$rootPath)), '/');
-        foreach (self::$overlayPaths as $overlay => $replace) {
-            if (strpos($newPath, $overlay) === 0) {
-                $newPath = $replace . substr($newPath, strlen($overlay));
-                $hasOverlay = true;
-                break;
-            }
-        }
-        return $hasOverlay ? $newPath : $path;
-    }
-
-    /**
-     * Method to register the stream wrapper
-     *
-     * If the stream is already registered the method returns silently. If there
-     * is already another stream wrapper registered for the scheme used by
-     * file:// scheme a \BadFunctionCallException will be thrown.
-     *
-     * @throws \BadFunctionCallException
-     * @return void
-     */
-    protected static function register()
-    {
-        if (self::$registered) {
-            return;
-        }
-
-        if (@stream_wrapper_unregister('file') === false) {
-            throw new \BadFunctionCallException('Cannot unregister file:// stream wrapper.', 1396340331);
-        }
-        if (@stream_wrapper_register('file', __CLASS__) === false) {
-            throw new \BadFunctionCallException('A handler has already been registered for the file:// scheme.', 1396340332);
-        }
-
-        self::$registered = true;
-    }
-
-    /**
-     * Restore the file handler
-     *
-     * @return void
-     */
-    protected static function restore()
-    {
-        if (!self::$registered) {
-            return;
-        }
-        if (@stream_wrapper_restore('file') === false) {
-            throw new \BadFunctionCallException('Cannot restore the default file:// stream handler.', 1396340333);
-        }
-        self::$registered = false;
-    }
-
-    /*
-     * The following list of functions is implemented as of
-     * @see http://www.php.net/manual/en/streamwrapper.dir-closedir.php
-     */
-
-    /**
-     * Close the directory
-     *
-     * @return bool
-     */
-    public function dir_closedir()
-    {
-        if ($this->dirHandle === null) {
-            return false;
-        } else {
-            self::restore();
-            closedir($this->dirHandle);
-            self::register();
-            $this->dirHandle = null;
-            return true;
-        }
-    }
-
-    /**
-     * Opens a directory for reading
-     *
-     * @param string $path
-     * @param int $options
-     * @return bool
-     */
-    public function dir_opendir($path, $options = 0)
-    {
-        if ($this->dirHandle !== null) {
-            return false;
-        }
-        self::restore();
-        $path = self::overlayPath($path);
-        $this->dirHandle = opendir($path);
-        self::register();
-        return $this->dirHandle !== false;
-    }
-
-    /**
-     * Read a single filename of a directory
-     *
-     * @return string|bool
-     */
-    public function dir_readdir()
-    {
-        if ($this->dirHandle === null) {
-            return false;
-        }
-        self::restore();
-        $success = readdir($this->dirHandle);
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Reset directory name pointer
-     *
-     * @return bool
-     */
-    public function dir_rewinddir()
-    {
-        if ($this->dirHandle === null) {
-            return false;
-        }
-        self::restore();
-        rewinddir($this->dirHandle);
-        self::register();
-        return true;
-    }
-
-    /**
-     * Create a directory
-     *
-     * @param string $path
-     * @param int $mode
-     * @param int $options
-     * @return bool
-     */
-    public function mkdir($path, $mode, $options = 0)
-    {
-        self::restore();
-        $path = self::overlayPath($path);
-        $success = mkdir($path, $mode, (bool)($options & STREAM_MKDIR_RECURSIVE));
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Rename a file
-     *
-     * @param string $pathFrom
-     * @param string $pathTo
-     * @return bool
-     */
-    public function rename($pathFrom, $pathTo)
-    {
-        self::restore();
-        $pathFrom = self::overlayPath($pathFrom);
-        $pathTo = self::overlayPath($pathTo);
-        $success = rename($pathFrom, $pathTo);
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Remove a directory
-     *
-     * @param string $path
-     * @return bool
-     */
-    public function rmdir($path)
-    {
-        self::restore();
-        $path = self::overlayPath($path);
-        $success = rmdir($path);
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Retrieve the underlying resource
-     *
-     * @param int $castAs Can be STREAM_CAST_FOR_SELECT when stream_select()
-     * is calling stream_cast() or STREAM_CAST_AS_STREAM when stream_cast()
-     * is called for other uses.
-     * @return resource|bool
-     */
-    public function stream_cast($castAs)
-    {
-        if ($this->fileHandle !== null && $castAs & STREAM_CAST_AS_STREAM) {
-            return $this->fileHandle;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Close a file
-     *
-     */
-    public function stream_close()
-    {
-        self::restore();
-        if ($this->fileHandle !== null) {
-            fclose($this->fileHandle);
-            $this->fileHandle = null;
-        }
-        self::register();
-    }
-
-    /**
-     * Test for end-of-file on a file pointer
-     *
-     * @return bool
-     */
-    public function stream_eof()
-    {
-        if ($this->fileHandle === null) {
-            return false;
-        }
-        self::restore();
-        $success = feof($this->fileHandle);
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Flush the output
-     *
-     * @return bool
-     */
-    public function stream_flush()
-    {
-        if ($this->fileHandle === null) {
-            return false;
-        }
-        self::restore();
-        $success = fflush($this->fileHandle);
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Advisory file locking
-     *
-     * @param int $operation
-     * @return bool
-     */
-    public function stream_lock($operation)
-    {
-        if ($this->fileHandle === null) {
-            return false;
-        }
-        self::restore();
-        $success = flock($this->fileHandle, $operation);
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Change file options
-     *
-     * @param string $path
-     * @param int $options
-     * @param mixed $value
-     * @return bool
-     */
-    public function stream_metadata($path, $options, $value)
-    {
-        self::restore();
-        $path = self::overlayPath($path);
-        switch ($options) {
-            case STREAM_META_TOUCH:
-                $success = touch($path, $value[0], $value[1]);
-                break;
-            case STREAM_META_OWNER_NAME:
-                // Fall through
-            case STREAM_META_OWNER:
-                $success = chown($path, $value);
-                break;
-            case STREAM_META_GROUP_NAME:
-                // Fall through
-            case STREAM_META_GROUP:
-                $success = chgrp($path, $value);
-                break;
-            case STREAM_META_ACCESS:
-                $success = chmod($path, $value);
-                break;
-            default:
-                $success = false;
-        }
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Open a file
-     *
-     * @param string $path
-     * @param string $mode
-     * @param int $options
-     * @param string &$opened_path
-     * @return bool
-     */
-    public function stream_open($path, $mode, $options, &$opened_path)
-    {
-        if ($this->fileHandle !== null) {
-            return false;
-        }
-        self::restore();
-        $path = self::overlayPath($path);
-        $this->fileHandle = fopen($path, $mode, (bool)($options & STREAM_USE_PATH));
-        self::register();
-        return $this->fileHandle !== false;
-    }
-
-    /**
-     * Read from a file
-     *
-     * @param int $length
-     * @return string
-     */
-    public function stream_read($length)
-    {
-        if ($this->fileHandle === null) {
-            return false;
-        }
-        self::restore();
-        $content = fread($this->fileHandle, $length);
-        self::register();
-        return $content;
-    }
-
-    /**
-     * Seek to specific location in a stream
-     *
-     * @param int $offset
-     * @param int $whence = SEEK_SET
-     * @return bool
-     */
-    public function stream_seek($offset, $whence = SEEK_SET)
-    {
-        if ($this->fileHandle === null) {
-            return false;
-        }
-        self::restore();
-        $success = fseek($this->fileHandle, $offset, $whence);
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Change stream options (not implemented)
-     *
-     * @param int $option
-     * @param int $arg1
-     * @param int $arg2
-     * @return bool
-     */
-    public function stream_set_option($option, $arg1, $arg2)
-    {
-        return false;
-    }
-
-    /**
-     * Retrieve information about a file resource
-     *
-     * @return array
-     */
-    public function stream_stat()
-    {
-        if ($this->fileHandle === null) {
-            return false;
-        }
-        self::restore();
-        $stats = fstat($this->fileHandle);
-        self::register();
-        return $stats;
-    }
-
-    /**
-     * Retrieve the current position of a stream
-     *
-     * @return int
-     */
-    public function stream_tell()
-    {
-        if ($this->fileHandle === null) {
-            return false;
-        }
-        self::restore();
-        $position = ftell($this->fileHandle);
-        self::register();
-        return $position;
-    }
-
-    /**
-     * Truncates a file to the given size
-     *
-     * @param int $size Truncate to this size
-     * @return bool
-     */
-    public function stream_truncate($size)
-    {
-        if ($this->fileHandle === null) {
-            return false;
-        }
-        self::restore();
-        $success = ftruncate($this->fileHandle, $size);
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Write to stream
-     *
-     * @param string $data
-     * @return int
-     */
-    public function stream_write($data)
-    {
-        if ($this->fileHandle === null) {
-            return false;
-        }
-        self::restore();
-        $length = fwrite($this->fileHandle, $data);
-        self::register();
-        return $length;
-    }
-
-    /**
-     * Unlink a file
-     *
-     * @param string $path
-     * @return bool
-     */
-    public function unlink($path)
-    {
-        self::restore();
-        $path = self::overlayPath($path);
-        $success = unlink($path);
-        self::register();
-        return $success;
-    }
-
-    /**
-     * Retrieve information about a file
-     *
-     * @param string $path
-     * @param int $flags
-     * @return array
-     */
-    public function url_stat($path, $flags)
-    {
-        self::restore();
-        $path = self::overlayPath($path);
-        if ($flags & STREAM_URL_STAT_LINK) {
-            $information = @lstat($path);
-        } else {
-            $information = @stat($path);
-        }
-        self::register();
-        return $information;
-    }
-}
diff --git a/typo3/sysext/core/Tests/FunctionalTestCase.php b/typo3/sysext/core/Tests/FunctionalTestCase.php
deleted file mode 100644 (file)
index b5a26ec..0000000
+++ /dev/null
@@ -1,411 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Tests;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use TYPO3\CMS\Core\Tests\Functional\Framework\Frontend\Response;
-
-/**
- * Base test case class for functional tests, all TYPO3 CMS
- * functional tests should extend from this class!
- *
- * If functional tests need additional setUp() and tearDown() code,
- * they *must* call parent::setUp() and parent::tearDown() to properly
- * set up and destroy the test system.
- *
- * The functional test system creates a full new TYPO3 CMS instance
- * within typo3temp/ of the base system and the bootstraps this TYPO3 instance.
- * This abstract class takes care of creating this instance with its
- * folder structure and a LocalConfiguration, creates an own database
- * for each test run and imports tables of loaded extensions.
- *
- * Functional tests must be run standalone (calling native phpunit
- * directly) and can not be executed by eg. the ext:phpunit backend module.
- * Additionally, the script must be called from the document root
- * of the instance, otherwise path calculation is not successfully.
- *
- * Call whole functional test suite, example:
- * - cd /var/www/t3master/foo  # Document root of CMS instance, here is index.php of frontend
- * - typo3/../bin/phpunit -c typo3/sysext/core/Build/FunctionalTests.xml
- *
- * Call single test case, example:
- * - cd /var/www/t3master/foo  # Document root of CMS instance, here is index.php of frontend
- * - typo3/../bin/phpunit \
- *     --process-isolation \
- *     --bootstrap typo3/sysext/core/Build/FunctionalTestsBootstrap.php \
- *     typo3/sysext/core/Tests/Functional/DataHandling/DataHandlerTest.php
- */
-abstract class FunctionalTestCase extends BaseTestCase
-{
-    /**
-     * Core extensions to load.
-     *
-     * If the test case needs additional core extensions as requirement,
-     * they can be noted here and will be added to LocalConfiguration
-     * extension list and ext_tables.sql of those extensions will be applied.
-     *
-     * This property will stay empty in this abstract, so it is possible
-     * to just overwrite it in extending classes. Extensions noted here will
-     * be loaded for every test of a test case and it is not possible to change
-     * the list of loaded extensions between single tests of a test case.
-     *
-     * A default list of core extensions is always loaded.
-     *
-     * @see FunctionalTestCaseUtility $defaultActivatedCoreExtensions
-     * @var array
-     */
-    protected $coreExtensionsToLoad = array();
-
-    /**
-     * Array of test/fixture extensions paths that should be loaded for a test.
-     *
-     * This property will stay empty in this abstract, so it is possible
-     * to just overwrite it in extending classes. Extensions noted here will
-     * be loaded for every test of a test case and it is not possible to change
-     * the list of loaded extensions between single tests of a test case.
-     *
-     * Given path is expected to be relative to your document root, example:
-     *
-     * array(
-     *   'typo3conf/ext/some_extension/Tests/Functional/Fixtures/Extensions/test_extension',
-     *   'typo3conf/ext/base_extension',
-     * );
-     *
-     * Extensions in this array are linked to the test instance, loaded
-     * and their ext_tables.sql will be applied.
-     *
-     * @var array
-     */
-    protected $testExtensionsToLoad = array();
-
-    /**
-     * Array of test/fixture folder or file paths that should be linked for a test.
-     *
-     * This property will stay empty in this abstract, so it is possible
-     * to just overwrite it in extending classes. Path noted here will
-     * be linked for every test of a test case and it is not possible to change
-     * the list of folders between single tests of a test case.
-     *
-     * array(
-     *   'link-source' => 'link-destination'
-     * );
-     *
-     * Given paths are expected to be relative to the test instance root.
-     * The array keys are the source paths and the array values are the destination
-     * paths, example:
-     *
-     * array(
-     *   'typo3/sysext/impext/Tests/Functional/Fixtures/Folders/fileadmin/user_upload' =>
-     *   'fileadmin/user_upload',
-     *   'typo3conf/ext/my_own_ext/Tests/Functional/Fixtures/Folders/uploads/tx_myownext' =>
-     *   'uploads/tx_myownext'
-     * );
-     *
-     * To be able to link from my_own_ext the extension path needs also to be registered in
-     * property $testExtensionsToLoad
-     *
-     * @var array
-     */
-    protected $pathsToLinkInTestInstance = array();
-
-    /**
-     * This configuration array is merged with TYPO3_CONF_VARS
-     * that are set in default configuration and factory configuration
-     *
-     * @var array
-     */
-    protected $configurationToUseInTestInstance = array();
-
-    /**
-     * Array of folders that should be created inside the test instance document root.
-     *
-     * This property will stay empty in this abstract, so it is possible
-     * to just overwrite it in extending classes. Path noted here will
-     * be linked for every test of a test case and it is not possible to change
-     * the list of folders between single tests of a test case.
-     *
-     * Per default the following folder are created
-     * /fileadmin
-     * /typo3temp
-     * /typo3conf
-     * /typo3conf/ext
-     * /uploads
-     *
-     * To create additional folders add the paths to this array. Given paths are expected to be
-     * relative to the test instance root and have to begin with a slash. Example:
-     *
-     * array(
-     *   'fileadmin/user_upload'
-     * );
-     *
-     * @var array
-     */
-    protected $additionalFoldersToCreate = array();
-
-    /**
-     * The fixture which is used when initializing a backend user
-     *
-     * @var string
-     */
-    protected $backendUserFixture = 'typo3/sysext/core/Tests/Functional/Fixtures/be_users.xml';
-
-    /**
-     * Private utility class used in setUp() and tearDown(). Do NOT use in test cases!
-     *
-     * @var \TYPO3\CMS\Core\Tests\FunctionalTestCaseBootstrapUtility
-     */
-    private $bootstrapUtility = null;
-
-    /**
-     * Calculate a "unique" identifier for the test database and the
-     * instance patch based on the given test case class name.
-     *
-     * @return string
-     */
-    protected function getInstanceIdentifier()
-    {
-        return FunctionalTestCaseBootstrapUtility::getInstanceIdentifier(get_class($this));
-    }
-
-    /**
-     * Calculates path to TYPO3 CMS test installation for this test case.
-     *
-     * @return string
-     */
-    protected function getInstancePath()
-    {
-        return FunctionalTestCaseBootstrapUtility::getInstancePath(get_class($this));
-    }
-
-    /**
-     * Set up creates a test instance and database.
-     *
-     * This method should be called with parent::setUp() in your test cases!
-     *
-     * @return void
-     */
-    protected function setUp()
-    {
-        if (!defined('ORIGINAL_ROOT')) {
-            $this->markTestSkipped('Functional tests must be called through phpunit on CLI');
-        }
-        $this->bootstrapUtility = new FunctionalTestCaseBootstrapUtility();
-        $this->bootstrapUtility->setUp(
-            get_class($this),
-            $this->coreExtensionsToLoad,
-            $this->testExtensionsToLoad,
-            $this->pathsToLinkInTestInstance,
-            $this->configurationToUseInTestInstance,
-            $this->additionalFoldersToCreate
-        );
-    }
-
-    /**
-     * Get DatabaseConnection instance - $GLOBALS['TYPO3_DB']
-     *
-     * This method should be used instead of direct access to
-     * $GLOBALS['TYPO3_DB'] for easy IDE auto completion.
-     *
-     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
-     */
-    protected function getDatabaseConnection()
-    {
-        return $GLOBALS['TYPO3_DB'];
-    }
-
-    /**
-     * Initialize backend user
-     *
-     * @param int $userUid uid of the user we want to initialize. This user must exist in the fixture file
-     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
-     * @throws Exception
-     */
-    protected function setUpBackendUserFromFixture($userUid)
-    {
-        $this->importDataSet(ORIGINAL_ROOT . $this->backendUserFixture);
-        $database = $this->getDatabaseConnection();
-        $userRow = $database->exec_SELECTgetSingleRow('*', 'be_users', 'uid = ' . (int)$userUid);
-
-        /** @var $backendUser \TYPO3\CMS\Core\Authentication\BackendUserAuthentication */
-        $backendUser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Authentication\BackendUserAuthentication::class);
-        $sessionId = $backendUser->createSessionId();
-        $_COOKIE['be_typo_user'] = $sessionId;
-        $backendUser->id = $sessionId;
-        $backendUser->sendNoCacheHeaders = false;
-        $backendUser->dontSetCookie = true;
-        $backendUser->createUserSession($userRow);
-
-        $GLOBALS['BE_USER'] = $backendUser;
-        $GLOBALS['BE_USER']->start();
-        if (!is_array($GLOBALS['BE_USER']->user) || !$GLOBALS['BE_USER']->user['uid']) {
-            throw new Exception(
-                'Can not initialize backend user',
-                1377095807
-            );
-        }
-        $GLOBALS['BE_USER']->backendCheckLogin();
-
-        return $backendUser;
-    }
-
-    /**
-     * Imports a data set represented as XML into the test database,
-     *
-     * @param string $path Absolute path to the XML file containing the data set to load
-     * @return void
-     * @throws Exception
-     */
-    protected function importDataSet($path)
-    {
-        if (!is_file($path)) {
-            throw new Exception(
-                'Fixture file ' . $path . ' not found',
-                1376746261
-            );
-        }
-
-        $database = $this->getDatabaseConnection();
-
-        $fileContent = file_get_contents($path);
-        // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
-        $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
-        $xml = simplexml_load_string($fileContent);
-        libxml_disable_entity_loader($previousValueOfEntityLoader);
-        $foreignKeys = array();
-
-        /** @var $table \SimpleXMLElement */
-        foreach ($xml->children() as $table) {
-            $insertArray = array();
-
-            /** @var $column \SimpleXMLElement */
-            foreach ($table->children() as $column) {
-                $columnName = $column->getName();
-                $columnValue = null;
-
-                if (isset($column['ref'])) {
-                    list($tableName, $elementId) = explode('#', $column['ref']);
-                    $columnValue = $foreignKeys[$tableName][$elementId];
-                } elseif (isset($column['is-NULL']) && ($column['is-NULL'] === 'yes')) {
-                    $columnValue = null;
-                } else {
-                    $columnValue = (string)$table->$columnName;
-                }
-
-                $insertArray[$columnName] = $columnValue;
-            }
-
-            $tableName = $table->getName();
-            $result = $database->exec_INSERTquery($tableName, $insertArray);
-            if ($result === false) {
-                throw new Exception(
-                    'Error when processing fixture file: ' . $path . ' Can not insert data to table ' . $tableName . ': ' . $database->sql_error(),
-                    1376746262
-                );
-            }
-            if (isset($table['id'])) {
-                $elementId = (string)$table['id'];
-                $foreignKeys[$tableName][$elementId] = $database->sql_insert_id();
-            }
-        }
-    }
-
-    /**
-     * @param int $pageId
-     * @param array $typoScriptFiles
-     */
-    protected function setUpFrontendRootPage($pageId, array $typoScriptFiles = array())
-    {
-        $pageId = (int)$pageId;
-        $page = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'pages', 'uid=' . $pageId);
-
-        if (empty($page)) {
-            $this->fail('Cannot set up frontend root page "' . $pageId . '"');
-        }
-
-        $pagesFields = array(
-            'is_siteroot' => 1
-        );
-
-        $this->getDatabaseConnection()->exec_UPDATEquery('pages', 'uid=' . $pageId, $pagesFields);
-
-        $templateFields = array(
-            'pid' => $pageId,
-            'title' => '',
-            'config' => '',
-            'clear' => 3,
-            'root' => 1,
-        );
-
-        foreach ($typoScriptFiles as $typoScriptFile) {
-            $templateFields['config'] .= '<INCLUDE_TYPOSCRIPT: source="FILE:' . $typoScriptFile . '">' . LF;
-        }
-
-        $this->getDatabaseConnection()->exec_INSERTquery('sys_template', $templateFields);
-    }
-
-    /**
-     * @param int $pageId
-     * @param int $languageId
-     * @param int $backendUserId
-     * @param int $workspaceId
-     * @param bool $failOnFailure
-     * @param int $frontendUserId
-     * @return Response
-     */
-    protected function getFrontendResponse($pageId, $languageId = 0, $backendUserId = 0, $workspaceId = 0, $failOnFailure = true, $frontendUserId = 0)
-    {
-        $pageId = (int)$pageId;
-        $languageId = (int)$languageId;
-
-        $additionalParameter = '';
-
-        if (!empty($frontendUserId)) {
-            $additionalParameter .= '&frontendUserId=' . (int)$frontendUserId;
-        }
-        if (!empty($backendUserId)) {
-            $additionalParameter .= '&backendUserId=' . (int)$backendUserId;
-        }
-        if (!empty($workspaceId)) {
-            $additionalParameter .= '&workspaceId=' . (int)$workspaceId;
-        }
-
-        $arguments = array(
-            'documentRoot' => $this->getInstancePath(),
-            'requestUrl' => 'http://localhost/?id=' . $pageId . '&L=' . $languageId . $additionalParameter,
-        );
-
-        $template = new \Text_Template(ORIGINAL_ROOT . 'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/request.tpl');
-        $template->setVar(
-            array(
-                'arguments' => var_export($arguments, true),
-                'originalRoot' => ORIGINAL_ROOT,
-            )
-        );
-
-        $php = \PHPUnit_Util_PHP::factory();
-        $response = $php->runJob($template->render());
-        $result = json_decode($response['stdout'], true);
-
-        if ($result === null) {
-            $this->fail('Frontend Response is empty');
-        }
-
-        if ($failOnFailure && $result['status'] === Response::STATUS_Failure) {
-            $this->fail('Frontend Response has failure:' . LF . $result['error']);
-        }
-
-        $response = new Response($result['status'], $result['content'], $result['error']);
-        return $response;
-    }
-}
diff --git a/typo3/sysext/core/Tests/UnitTestCase.php b/typo3/sysext/core/Tests/UnitTestCase.php
deleted file mode 100644 (file)
index 35b9bdc..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Tests;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\PathUtility;
-use TYPO3\CMS\Core\Utility\StringUtility;
-
-/**
- * Base test case for unit tests.
- *
- * This class currently only inherits the base test case. However, it is recommended
- * to extend this class for unit test cases instead of the base test case because if,
- * at some point, specific behavior needs to be implemented for unit tests, your test cases
- * will profit from it automatically.
- *
- */
-abstract class UnitTestCase extends BaseTestCase
-{
-    /**
-     * @todo make LoadedExtensionsArray serializable instead
-     *
-     * @var array
-     */
-    protected $backupGlobalsBlacklist = array('TYPO3_LOADED_EXT');
-
-    /**
-     * Absolute path to files that should be removed after a test.
-     * Handled in tearDown. Tests can register here to get any files
-     * within typo3temp/ or typo3conf/ext cleaned up again.
-     *
-     * @var array
-     */
-    protected $testFilesToDelete = array();
-
-    /**
-     * Unset all additional properties of test classes to help PHP
-     * garbage collection. This reduces memory footprint with lots
-     * of tests.
-     *
-     * If owerwriting tearDown() in test classes, please call
-     * parent::tearDown() at the end. Unsetting of own properties
-     * is not needed this way.
-     *
-     * @throws \RuntimeException
-     * @return void
-     */
-    protected function tearDown()
-    {
-        // Unset properties of test classes to safe memory
-        $reflection = new \ReflectionObject($this);
-        foreach ($reflection->getProperties() as $property) {
-            $declaringClass = $property->getDeclaringClass()->getName();
-            if (
-                !$property->isStatic()
-                && $declaringClass !== \TYPO3\CMS\Core\Tests\UnitTestCase::class
-                && $declaringClass !== \TYPO3\CMS\Core\Tests\BaseTestCase::class
-                && strpos($property->getDeclaringClass()->getName(), 'PHPUnit_') !== 0
-            ) {
-                $propertyName = $property->getName();
-                unset($this->$propertyName);
-            }
-        }
-        unset($reflection);
-
-        // Delete registered test files and directories
-        foreach ($this->testFilesToDelete as $absoluteFileName) {
-            $absoluteFileName = GeneralUtility::fixWindowsFilePath(PathUtility::getCanonicalPath($absoluteFileName));
-            if (!GeneralUtility::validPathStr($absoluteFileName)) {
-                throw new \RuntimeException('tearDown() cleanup: Filename contains illegal characters', 1410633087);
-            }
-            if (!StringUtility::beginsWith($absoluteFileName, PATH_site . 'typo3temp/')) {
-                throw new \RuntimeException(
-                    'tearDown() cleanup:  Files to delete must be within typo3temp/',
-                    1410633412
-                );
-            }
-            // file_exists returns false for links pointing to not existing targets, so handle links before next check.
-            if (@is_link($absoluteFileName) || @is_file($absoluteFileName)) {
-                unlink($absoluteFileName);
-            } elseif (@is_dir($absoluteFileName)) {
-                GeneralUtility::rmdir($absoluteFileName, true);
-            } else {
-                throw new \RuntimeException('tearDown() cleanup: File, link or directory does not exist', 1410633510);
-            }
-        }
-        $this->testFilesToDelete = array();
-    }
-}