Skip to content

[BUG] setUp() never restores TYPO3_PATH_ROOT, breaking every following forked test #746

Description

@dkd-kaehm

What are you trying to achieve?

Run functional test cases without processIsolation, where one of them is marked
#[RunInSeparateProcess] and loads a test extension by a relative path.

What do you get instead?

The forked test case resolves its paths against the instance directory of the previous test
case, so linking the test extension fails:

TYPO3\TestingFramework\Core\Exception: Test extension path
/var/www/html/public/typo3temp/var/tests/functional-<hashOfATest>/../vendor/acme/foo/Tests/Fixtures/Extensions/fake_extension
not found

The prefix is ATest's instance path instead of the document root.

FunctionalTestCase::setUp() points TYPO3_PATH_ROOT at the instance of the running test and
never restores it:

// Classes/Core/Functional/FunctionalTestCase.php:289
putenv('TYPO3_PATH_ROOT=' . $this->instancePath);
putenv('TYPO3_PATH_APP=' . $this->instancePath);

putenv() is process global, so the value outlives the test case. A forked test inherits it,
runs the bootstrap again and derives ORIGINAL_ROOT from the inherited value:

// Classes/Core/Testbase.php:91
define('ORIGINAL_ROOT', $this->getWebRoot()); // getWebRoot() reads TYPO3_PATH_ROOT

From there every path built on ORIGINAL_ROOT points one instance deeper, which
linkTestExtensionsToInstance() hits first:

// Classes/Core/Testbase.php:342
$absoluteExtensionPath = ORIGINAL_ROOT . $extensionPath;

Observed values:

process ORIGINAL_ROOT instancePath of BTest
parent, ATest /var/www/html/public/ …/tests/functional-<hashOfATest>
fork, BTest /var/www/html/public/typo3temp/var/tests/functional-<hashOfATest>/ …functional-<hashOfATest>/typo3temp/var/tests/functional-<hashOfBTest>

How to reproduce the issue?

  1. Remove processIsolation="true" from the phpunit configuration.
  2. Add two functional test cases, where the second one is forked and loads a test extension by
    a relative path:
final class ATest extends FunctionalTestCase
{
    #[Test]
    public function pollutesTheEnvironment(): void
    {
        self::assertTrue(true); // setUp() alone is enough
    }
}

final class BTest extends FunctionalTestCase
{
    protected array $testExtensionsToLoad = ['../vendor/acme/foo/Tests/Fixtures/Extensions/fake_extension'];

    #[Test]
    #[RunInSeparateProcess]
    public function fails(): void
    {
        self::assertTrue(true);
    }
}
  1. Run both in one process, ATest first:
phpunit -c Build/Test/FunctionalTests.xml --filter='ATest|BTest'

BTest fails with the exception above. It passes when run on its own, with
processIsolation="true", or without #[RunInSeparateProcess].

Additional information you would like to provide?

processIsolation="true" hides the problem, because then every test case starts from the
pristine environment of the parent process.

A test case can work around it by unsetting the variable in its own tearDown()
(putenv('TYPO3_PATH_ROOT')), which is what led us to the cause.

Possible fix: remember TYPO3_PATH_ROOT and TYPO3_PATH_APP before overwriting them in
setUp() and restore them in tearDown().

Specify some data of the environment

  • TYPO3 testing framework version: 9.6.1
  • TYPO3 version: 14.3.6
  • TYPO3 installation type: composer
  • PHP version: 8.2.31
  • Web server used + version: nginx-fpm (DDEV), tests run on CLI
  • Operating system: DDEV environmen and/or Github Actions

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions