diff --git a/composer.json b/composer.json index 9478c5d0..079fddcf 100644 --- a/composer.json +++ b/composer.json @@ -40,10 +40,10 @@ "ext-simplexml": "*", "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1", "jean85/pretty-package-versions": "^2.0.5", - "phpunit/php-code-coverage": "^10.1.1", + "phpunit/php-code-coverage": "^10.1.2", "phpunit/php-file-iterator": "^4.0.2", "phpunit/php-timer": "^6.0", - "phpunit/phpunit": "^10.2.2", + "phpunit/phpunit": "^10.2.6", "sebastian/environment": "^6.0.1", "symfony/console": "^6.3.0", "symfony/process": "^6.3.0" @@ -53,12 +53,12 @@ "ext-posix": "*", "doctrine/coding-standard": "^12.0.0", "infection/infection": "^0.27.0", - "phpstan/phpstan": "^1.10.18", + "phpstan/phpstan": "^1.10.26", "phpstan/phpstan-deprecation-rules": "^1.1.3", "phpstan/phpstan-phpunit": "^1.3.13", "phpstan/phpstan-strict-rules": "^1.5.1", "squizlabs/php_codesniffer": "^3.7.2", - "symfony/filesystem": "^6.3.0" + "symfony/filesystem": "^6.3.1" }, "autoload": { "psr-4": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 17e6d6a5..1e4d90a3 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -20,6 +20,46 @@ parameters: count: 1 path: src/WrapperRunner/ResultPrinter.php + - + message: "#^Access to an uninitialized readonly property ParaTest\\\\WrapperRunner\\\\WrapperWorker\\:\\:\\$coverageFile\\.$#" + count: 2 + path: src/WrapperRunner/WrapperWorker.php + + - + message: "#^Access to an uninitialized readonly property ParaTest\\\\WrapperRunner\\\\WrapperWorker\\:\\:\\$junitFile\\.$#" + count: 2 + path: src/WrapperRunner/WrapperWorker.php + + - + message: "#^Access to an uninitialized readonly property ParaTest\\\\WrapperRunner\\\\WrapperWorker\\:\\:\\$teamcityFile\\.$#" + count: 2 + path: src/WrapperRunner/WrapperWorker.php + + - + message: "#^Access to an uninitialized readonly property ParaTest\\\\WrapperRunner\\\\WrapperWorker\\:\\:\\$testdoxFile\\.$#" + count: 2 + path: src/WrapperRunner/WrapperWorker.php + + - + message: "#^Class ParaTest\\\\WrapperRunner\\\\WrapperWorker has an uninitialized readonly property \\$coverageFile\\. Assign it in the constructor\\.$#" + count: 1 + path: src/WrapperRunner/WrapperWorker.php + + - + message: "#^Class ParaTest\\\\WrapperRunner\\\\WrapperWorker has an uninitialized readonly property \\$junitFile\\. Assign it in the constructor\\.$#" + count: 1 + path: src/WrapperRunner/WrapperWorker.php + + - + message: "#^Class ParaTest\\\\WrapperRunner\\\\WrapperWorker has an uninitialized readonly property \\$teamcityFile\\. Assign it in the constructor\\.$#" + count: 1 + path: src/WrapperRunner/WrapperWorker.php + + - + message: "#^Class ParaTest\\\\WrapperRunner\\\\WrapperWorker has an uninitialized readonly property \\$testdoxFile\\. Assign it in the constructor\\.$#" + count: 1 + path: src/WrapperRunner/WrapperWorker.php + - message: "#^Unreachable statement \\- code above always terminates\\.$#" count: 1 diff --git a/src/WrapperRunner/ApplicationForWrapperWorker.php b/src/WrapperRunner/ApplicationForWrapperWorker.php index 665df8d7..6647b28f 100644 --- a/src/WrapperRunner/ApplicationForWrapperWorker.php +++ b/src/WrapperRunner/ApplicationForWrapperWorker.php @@ -25,6 +25,7 @@ use PHPUnit\TextUI\Configuration\Configuration; use PHPUnit\TextUI\Configuration\PhpHandler; use PHPUnit\TextUI\Output\Default\ProgressPrinter\ProgressPrinter; +use PHPUnit\TextUI\Output\Default\UnexpectedOutputPrinter; use PHPUnit\TextUI\Output\DefaultPrinter; use PHPUnit\TextUI\Output\NullPrinter; use PHPUnit\TextUI\Output\TestDox\ResultPrinter as TestDoxResultPrinter; @@ -166,11 +167,14 @@ private function bootstrap(): void ); } + $printer = new ProgressPrinterOutput( + DefaultPrinter::from($this->progressFile), + DefaultPrinter::from($this->unexpectedOutputFile), + ); + + new UnexpectedOutputPrinter($printer, EventFacade::instance()); new ProgressPrinter( - new ProgressPrinterOutput( - DefaultPrinter::from($this->progressFile), - DefaultPrinter::from($this->unexpectedOutputFile), - ), + $printer, EventFacade::instance(), false, 120, diff --git a/test/Unit/WrapperRunner/WrapperRunnerTest.php b/test/Unit/WrapperRunner/WrapperRunnerTest.php index 174473fd..d8df7583 100644 --- a/test/Unit/WrapperRunner/WrapperRunnerTest.php +++ b/test/Unit/WrapperRunner/WrapperRunnerTest.php @@ -22,15 +22,18 @@ use function array_reverse; use function array_unique; use function defined; +use function explode; use function file_get_contents; use function file_put_contents; use function glob; +use function implode; use function min; use function posix_mkfifo; use function preg_match; use function preg_match_all; use function preg_replace; use function scandir; +use function sort; use function sprintf; use function str_replace; use function uniqid; @@ -38,6 +41,7 @@ use const DIRECTORY_SEPARATOR; use const FIXTURES; +use const PHP_EOL; /** @internal */ #[CoversClass(WrapperRunner::class)] @@ -241,7 +245,18 @@ public function testTeamcityOutput(): void $result = $this->runRunner(); - self::assertSame(36, preg_match_all('/^##teamcity/m', $result->output)); + $format = file_get_contents(__DIR__ . '/fixtures/common_results_teamcity_output'); + self::assertNotFalse($format); + + $output = $result->output; + $output = preg_replace("/^Processes: \\d+\nRuntime: PHP \\d+.\\d+.\\d+(-dev)?\n\n/", '', $output, 1, $count); + self::assertSame(1, $count); + self::assertNotNull($output); + + self::assertStringMatchesFormat( + self::sorted($format), + self::sorted($output), + ); } public function testExitCodesPathWithoutTests(): void @@ -456,11 +471,17 @@ public function testTeamcityLog(): void $this->runRunner(); + $format = file_get_contents(__DIR__ . '/fixtures/common_results_teamcity_output'); + self::assertNotFalse($format); + self::assertFileExists($outputPath); $content = file_get_contents($outputPath); self::assertNotFalse($content); - self::assertSame(36, preg_match_all('/^##teamcity/m', $content)); + self::assertStringMatchesFormat( + self::sorted($format), + self::sorted($content), + ); } public function testRunningFewerTestsThanTheWorkersIsPossible(): void @@ -587,4 +608,12 @@ public function testProcessIsolation(): void $runnerResult = $this->runRunner(); self::assertSame(RunnerInterface::SUCCESS_EXIT, $runnerResult->exitCode); } + + private static function sorted(string $from): string + { + $from = explode(PHP_EOL, $from); + sort($from); + + return implode(PHP_EOL, $from); + } } diff --git a/test/Unit/WrapperRunner/fixtures/common_results_teamcity_output b/test/Unit/WrapperRunner/fixtures/common_results_teamcity_output new file mode 100644 index 00000000..f50ba10a --- /dev/null +++ b/test/Unit/WrapperRunner/fixtures/common_results_teamcity_output @@ -0,0 +1,70 @@ + +##teamcity[testCount count='1' flowId='%d'] + +##teamcity[testSuiteStarted name='ParaTest\Tests\fixtures\common_results\ErrorTest' locationHint='php_qn://%s/test/fixtures/common_results/ErrorTest.php::\ParaTest\Tests\fixtures\common_results\ErrorTest' flowId='%d'] + +##teamcity[testStarted name='testError' locationHint='php_qn://%s/test/fixtures/common_results/ErrorTest.php::\ParaTest\Tests\fixtures\common_results\ErrorTest::testError' flowId='%d'] + +##teamcity[testFailed name='testError' message='RuntimeException: Error here!' details='%s/test/fixtures/common_results/ErrorTest.php:15|n' duration='%d' flowId='%d'] + +##teamcity[testFinished name='testError' duration='%d' flowId='%d'] + +##teamcity[testSuiteFinished name='ParaTest\Tests\fixtures\common_results\ErrorTest' flowId='%d'] + +##teamcity[testCount count='1' flowId='%d'] + +##teamcity[testSuiteStarted name='ParaTest\Tests\fixtures\common_results\FailureTest' locationHint='php_qn://%s/test/fixtures/common_results/FailureTest.php::\ParaTest\Tests\fixtures\common_results\FailureTest' flowId='%d'] + +##teamcity[testStarted name='testFailure' locationHint='php_qn://%s/test/fixtures/common_results/FailureTest.php::\ParaTest\Tests\fixtures\common_results\FailureTest::testFailure' flowId='%d'] + +##teamcity[testFailed name='testFailure' message='Failed asserting that false is true.' details='%s/test/fixtures/common_results/FailureTest.php:14|n' duration='%d' flowId='%d'] + +##teamcity[testFinished name='testFailure' duration='%d' flowId='%d'] + +##teamcity[testSuiteFinished name='ParaTest\Tests\fixtures\common_results\FailureTest' flowId='%d'] + +##teamcity[testSuiteStarted name='ParaTest\Tests\fixtures\common_results\IncompleteTest' locationHint='php_qn://%s/test/fixtures/common_results/IncompleteTest.php::\ParaTest\Tests\fixtures\common_results\IncompleteTest' flowId='%d'] + +##teamcity[testStarted name='testIncomplete' locationHint='php_qn://%s/test/fixtures/common_results/IncompleteTest.php::\ParaTest\Tests\fixtures\common_results\IncompleteTest::testIncomplete' flowId='%d'] + +##teamcity[testIgnored name='testIncomplete' message='' details='%s/test/fixtures/common_results/IncompleteTest.php:14|n' duration='%d' flowId='%d'] + +##teamcity[testFinished name='testIncomplete' duration='%d' flowId='%d'] + +##teamcity[testSuiteFinished name='ParaTest\Tests\fixtures\common_results\IncompleteTest' flowId='%d'] + +##teamcity[testSuiteStarted name='ParaTest\Tests\fixtures\common_results\RiskyTest' locationHint='php_qn://%s/test/fixtures/common_results/RiskyTest.php::\ParaTest\Tests\fixtures\common_results\RiskyTest' flowId='%d'] + +##teamcity[testStarted name='testRisky' locationHint='php_qn://%s/test/fixtures/common_results/RiskyTest.php::\ParaTest\Tests\fixtures\common_results\RiskyTest::testRisky' flowId='%d'] + +##teamcity[testFailed name='testRisky' message='This test did not perform any assertions' details='' duration='%d' flowId='%d'] + +##teamcity[testFinished name='testRisky' duration='%d' flowId='%d'] + +##teamcity[testSuiteFinished name='ParaTest\Tests\fixtures\common_results\RiskyTest' flowId='%d'] + +##teamcity[testSuiteStarted name='ParaTest\Tests\fixtures\common_results\SkippedTest' locationHint='php_qn://%s/test/fixtures/common_results/SkippedTest.php::\ParaTest\Tests\fixtures\common_results\SkippedTest' flowId='%d'] + +##teamcity[testStarted name='testSkipped' locationHint='php_qn://%s/test/fixtures/common_results/SkippedTest.php::\ParaTest\Tests\fixtures\common_results\SkippedTest::testSkipped' flowId='%d'] + +##teamcity[testIgnored name='testSkipped' message='' duration='%d' flowId='%d'] + +##teamcity[testFinished name='testSkipped' duration='%d' flowId='%d'] + +##teamcity[testSuiteFinished name='ParaTest\Tests\fixtures\common_results\SkippedTest' flowId='%d'] + +##teamcity[testSuiteStarted name='ParaTest\Tests\fixtures\common_results\SuccessTest' locationHint='php_qn://%s/test/fixtures/common_results/SuccessTest.php::\ParaTest\Tests\fixtures\common_results\SuccessTest' flowId='%d'] + +##teamcity[testStarted name='testSuccess' locationHint='php_qn://%s/test/fixtures/common_results/SuccessTest.php::\ParaTest\Tests\fixtures\common_results\SuccessTest::testSuccess' flowId='%d'] + +##teamcity[testFinished name='testSuccess' duration='%d' flowId='%d'] + +##teamcity[testSuiteFinished name='ParaTest\Tests\fixtures\common_results\SuccessTest' flowId='%d'] + +##teamcity[testSuiteStarted name='ParaTest\Tests\fixtures\common_results\WarningTest' locationHint='php_qn://%s/test/fixtures/common_results/WarningTest.php::\ParaTest\Tests\fixtures\common_results\WarningTest' flowId='%d'] + +##teamcity[testStarted name='testWarning' locationHint='php_qn://%s/test/fixtures/common_results/WarningTest.php::\ParaTest\Tests\fixtures\common_results\WarningTest::testWarning' flowId='%d'] + +##teamcity[testFinished name='testWarning' duration='%d' flowId='%d'] + +##teamcity[testSuiteFinished name='ParaTest\Tests\fixtures\common_results\WarningTest' flowId='%d']