From 1ad660c4cfcc06bca0c75c128215c6265a5aad2e Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Tue, 10 Dec 2024 12:42:35 +0100 Subject: [PATCH] fix: PHPunit extensions must run before data providers (#917) * test: add a failing test case * Run extension before loading the suite --------- Co-authored-by: Filippo Tessarotto --- composer.json | 12 +++---- src/WrapperRunner/SuiteLoader.php | 33 ++++++++++++++++++- src/WrapperRunner/WrapperRunner.php | 3 -- test/TestBase.php | 12 ++++++- test/Unit/WrapperRunner/WrapperRunnerTest.php | 21 ++++++++++++ ...ExtensionMustRunBeforeDataProviderTest.php | 30 +++++++++++++++++ .../PHPUnitExtension.php | 27 +++++++++++++++ .../phpunit.xml | 10 ++++++ 8 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 test/fixtures/extension_run_before_data_provider/ExtensionMustRunBeforeDataProviderTest.php create mode 100644 test/fixtures/extension_run_before_data_provider/PHPUnitExtension.php create mode 100644 test/fixtures/extension_run_before_data_provider/phpunit.xml diff --git a/composer.json b/composer.json index f5a72bab..5b7153ab 100644 --- a/composer.json +++ b/composer.json @@ -45,19 +45,19 @@ "phpunit/php-timer": "^7.0.1", "phpunit/phpunit": "^11.5.0", "sebastian/environment": "^7.2.0", - "symfony/console": "^6.4.14 || ^7.1.7", - "symfony/process": "^6.4.14 || ^7.1.7" + "symfony/console": "^6.4.14 || ^7.2.0", + "symfony/process": "^6.4.14 || ^7.2.0" }, "require-dev": { "ext-pcov": "*", "ext-posix": "*", "doctrine/coding-standard": "^12.0.0", - "phpstan/phpstan": "^2", - "phpstan/phpstan-deprecation-rules": "^2", - "phpstan/phpstan-phpunit": "^2", + "phpstan/phpstan": "^2.0.3", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpstan/phpstan-phpunit": "^2.0.1", "phpstan/phpstan-strict-rules": "^2", "squizlabs/php_codesniffer": "^3.11.1", - "symfony/filesystem": "^6.4.13 || ^7.1.6" + "symfony/filesystem": "^6.4.13 || ^7.2.0" }, "autoload": { "psr-4": { diff --git a/src/WrapperRunner/SuiteLoader.php b/src/WrapperRunner/SuiteLoader.php index 27e8b04d..03b966f9 100644 --- a/src/WrapperRunner/SuiteLoader.php +++ b/src/WrapperRunner/SuiteLoader.php @@ -6,11 +6,16 @@ use Generator; use ParaTest\Options; +use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\Extension\ExtensionBootstrapper; +use PHPUnit\Runner\Extension\Facade as ExtensionFacade; +use PHPUnit\Runner\Extension\PharLoader; use PHPUnit\Runner\PhptTestCase; use PHPUnit\Runner\ResultCache\NullResultCache; use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\TestRunner\TestResult\Facade as TestResultFacade; use PHPUnit\TextUI\Command\Result; use PHPUnit\TextUI\Command\WarmCodeCoverageCacheCommand; use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry; @@ -50,9 +55,35 @@ public function __construct( (new PhpHandler())->handle($this->options->configuration->php()); if ($this->options->configuration->hasBootstrap()) { - include_once $this->options->configuration->bootstrap(); + $bootstrapFilename = $this->options->configuration->bootstrap(); + include_once $bootstrapFilename; + EventFacade::emitter()->testRunnerBootstrapFinished($bootstrapFilename); } + if (! $this->options->configuration->noExtensions()) { + if ($this->options->configuration->hasPharExtensionDirectory()) { + (new PharLoader())->loadPharExtensionsInDirectory( + $this->options->configuration->pharExtensionDirectory(), + ); + } + + $extensionFacade = new ExtensionFacade(); + $extensionBootstrapper = new ExtensionBootstrapper( + $this->options->configuration, + $extensionFacade, + ); + + foreach ($this->options->configuration->extensionBootstrappers() as $bootstrapper) { + $extensionBootstrapper->bootstrap( + $bootstrapper['className'], + $bootstrapper['parameters'], + ); + } + } + + TestResultFacade::init(); + EventFacade::instance()->seal(); + $testSuite = (new TestSuiteBuilder())->build($this->options->configuration); if ($this->options->configuration->executionOrder() === TestSuiteSorter::ORDER_RANDOMIZED) { diff --git a/src/WrapperRunner/WrapperRunner.php b/src/WrapperRunner/WrapperRunner.php index 20640bf8..b7672eb5 100644 --- a/src/WrapperRunner/WrapperRunner.php +++ b/src/WrapperRunner/WrapperRunner.php @@ -9,7 +9,6 @@ use ParaTest\JUnit\Writer; use ParaTest\Options; use ParaTest\RunnerInterface; -use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Runner\CodeCoverage; use PHPUnit\TestRunner\TestResult\Facade as TestResultFacade; use PHPUnit\TestRunner\TestResult\TestResult; @@ -100,8 +99,6 @@ public function run(): int $directory = dirname(__DIR__); assert($directory !== ''); ExcludeList::addDirectory($directory); - TestResultFacade::init(); - EventFacade::instance()->seal(); $suiteLoader = new SuiteLoader( $this->options, $this->output, diff --git a/test/TestBase.php b/test/TestBase.php index aaec88d8..1dde65fd 100644 --- a/test/TestBase.php +++ b/test/TestBase.php @@ -8,7 +8,9 @@ use ParaTest\Options; use ParaTest\RunnerInterface; use ParaTest\WrapperRunner\WrapperRunner; +use PHPUnit\Event\Facade; use PHPUnit\Framework\TestCase; +use ReflectionProperty; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Output\BufferedOutput; @@ -87,7 +89,15 @@ final protected function runRunner(): RunnerResult unset($_SERVER[Options::ENV_KEY_UNIQUE_TOKEN]); } - $exitCode = $runner->run(); + $refProperty = new ReflectionProperty(Facade::class, 'instance'); + $keep = $refProperty->getValue(new Facade()); + $refProperty->setValue(new Facade(), null); + try { + $exitCode = $runner->run(); + } finally { + $refProperty->setValue(new Facade(), $keep); + } + if ($shouldPutEnvForParatestTestingItSelf) { putenv(Options::ENV_KEY_TOKEN . '=' . $prevToken); putenv(Options::ENV_KEY_UNIQUE_TOKEN . '=' . $prevUniqueToken); diff --git a/test/Unit/WrapperRunner/WrapperRunnerTest.php b/test/Unit/WrapperRunner/WrapperRunnerTest.php index 91f151f9..b7dca8df 100644 --- a/test/Unit/WrapperRunner/WrapperRunnerTest.php +++ b/test/Unit/WrapperRunner/WrapperRunnerTest.php @@ -756,6 +756,27 @@ public function testBaselineIsRespected(): void Time: %s, Memory: %s MB +OK%a +EOF; + self::assertStringMatchesFormat($expectedOutput, $runnerResult->output); + self::assertEquals(RunnerInterface::SUCCESS_EXIT, $runnerResult->exitCode); + } + + public function testExtensionMustRunBeforeDataProvider(): void + { + $this->bareOptions['--configuration'] = $this->fixture('extension_run_before_data_provider' . DIRECTORY_SEPARATOR . 'phpunit.xml'); + + $runnerResult = $this->runRunner(); + + $expectedOutput = <<bareOptions['--configuration']} + +. 1 / 1 (100%) + +Time: %s, Memory: %s MB + OK%a EOF; self::assertStringMatchesFormat($expectedOutput, $runnerResult->output); diff --git a/test/fixtures/extension_run_before_data_provider/ExtensionMustRunBeforeDataProviderTest.php b/test/fixtures/extension_run_before_data_provider/ExtensionMustRunBeforeDataProviderTest.php new file mode 100644 index 00000000..09d1f7b7 --- /dev/null +++ b/test/fixtures/extension_run_before_data_provider/ExtensionMustRunBeforeDataProviderTest.php @@ -0,0 +1,30 @@ + */ + public static function provide(): iterable + { + if (self::$var !== 'bar') { + throw new RuntimeException('Extension did not run before data provider.'); + } + + yield [self::$var]; + } +} diff --git a/test/fixtures/extension_run_before_data_provider/PHPUnitExtension.php b/test/fixtures/extension_run_before_data_provider/PHPUnitExtension.php new file mode 100644 index 00000000..4bfacc88 --- /dev/null +++ b/test/fixtures/extension_run_before_data_provider/PHPUnitExtension.php @@ -0,0 +1,27 @@ +registerSubscribers( + new class implements Event\Test\DataProviderMethodCalledSubscriber { + public function notify(Event\Test\DataProviderMethodCalled $event): void + { + ExtensionMustRunBeforeDataProviderTest::$var = 'bar'; + } + }, + ); + } +} diff --git a/test/fixtures/extension_run_before_data_provider/phpunit.xml b/test/fixtures/extension_run_before_data_provider/phpunit.xml new file mode 100644 index 00000000..ae566689 --- /dev/null +++ b/test/fixtures/extension_run_before_data_provider/phpunit.xml @@ -0,0 +1,10 @@ + + + + ExtensionMustRunBeforeDataProviderTest.php + + + + + +