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?
- Remove
processIsolation="true" from the phpunit configuration.
- 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);
}
}
- 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
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:
The prefix is
ATest's instance path instead of the document root.FunctionalTestCase::setUp()pointsTYPO3_PATH_ROOTat the instance of the running test andnever restores it:
putenv()is process global, so the value outlives the test case. A forked test inherits it,runs the bootstrap again and derives
ORIGINAL_ROOTfrom the inherited value:From there every path built on
ORIGINAL_ROOTpoints one instance deeper, whichlinkTestExtensionsToInstance()hits first:Observed values:
ATest/var/www/html/public/…/tests/functional-<hashOfATest>BTest/var/www/html/public/typo3temp/var/tests/functional-<hashOfATest>/…functional-<hashOfATest>/typo3temp/var/tests/functional-<hashOfBTest>How to reproduce the issue?
processIsolation="true"from the phpunit configuration.a relative path:
ATestfirst:phpunit -c Build/Test/FunctionalTests.xml --filter='ATest|BTest'BTestfails with the exception above. It passes when run on its own, withprocessIsolation="true", or without#[RunInSeparateProcess].Additional information you would like to provide?
processIsolation="true"hides the problem, because then every test case starts from thepristine 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_ROOTandTYPO3_PATH_APPbefore overwriting them insetUp()and restore them intearDown().Specify some data of the environment