From 561ba252f1bc04ec0b5e3d68722e414c10639a84 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 27 Mar 2022 23:06:36 +0200 Subject: [PATCH 1/2] LevelsTestCase - run in parallel --- Makefile | 2 +- composer.json | 2 +- composer.lock | 4 +-- src/Process/ProcessCrashedException.php | 20 ++++++++++++ src/Process/ProcessPromise.php | 4 +-- src/Testing/LevelsTestCase.php | 42 +++++++++++++++++++++++-- 6 files changed, 65 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 91f84227d1..dfb4d01d0d 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ tests: php vendor/bin/paratest --runner WrapperRunner --no-coverage tests-integration: - php vendor/bin/paratest --runner WrapperRunner --no-coverage --group exec + php vendor/bin/phpunit --no-coverage --group exec tests-static-reflection: php vendor/bin/paratest --runner WrapperRunner --no-coverage --bootstrap tests/bootstrap-static-reflection.php diff --git a/composer.json b/composer.json index 7a9d6f19fc..72174a6558 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ ], "require": { "php": "^8.0", - "clue/block-react": "^1.4", + "clue/block-react": "^1.5", "clue/ndjson-react": "^1.0", "composer/ca-bundle": "^1.2", "composer/xdebug-handler": "^3.0.3", diff --git a/composer.lock b/composer.lock index 1bc8b6d3d3..11f1df9dbf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4537cd3f3811dac40b7aa6575c60a8e1", + "content-hash": "5cb7bd74efb3a11aee3db0db1c31f56d", "packages": [ { "name": "clue/block-react", @@ -7308,5 +7308,5 @@ "platform-overrides": { "php": "8.0.99" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.2.0" } diff --git a/src/Process/ProcessCrashedException.php b/src/Process/ProcessCrashedException.php index d6278a50e7..e3e7bd9e8b 100644 --- a/src/Process/ProcessCrashedException.php +++ b/src/Process/ProcessCrashedException.php @@ -7,4 +7,24 @@ class ProcessCrashedException extends Exception { + public function __construct(private ?int $exitCode, private string $stdOut, private string $stdErr) + { + parent::__construct($this->stdOut . $this->stdErr); + } + + public function getExitCode(): ?int + { + return $this->exitCode; + } + + public function getStdOut(): string + { + return $this->stdOut; + } + + public function getStdErr(): string + { + return $this->stdErr; + } + } diff --git a/src/Process/ProcessPromise.php b/src/Process/ProcessPromise.php index 26b7fb256a..60726e7268 100644 --- a/src/Process/ProcessPromise.php +++ b/src/Process/ProcessPromise.php @@ -68,7 +68,7 @@ public function run(): CancellablePromiseInterface fclose($tmpStdErrResource); if ($exitCode === null) { - $this->deferred->reject(new ProcessCrashedException($stdOut . $stdErr)); + $this->deferred->reject(new ProcessCrashedException($exitCode, $stdOut, $stdErr)); return; } @@ -77,7 +77,7 @@ public function run(): CancellablePromiseInterface return; } - $this->deferred->reject(new ProcessCrashedException($stdOut . $stdErr)); + $this->deferred->reject(new ProcessCrashedException($exitCode, $stdOut, $stdErr)); }); /** @var ExtendedPromiseInterface&CancellablePromiseInterface */ diff --git a/src/Testing/LevelsTestCase.php b/src/Testing/LevelsTestCase.php index 0204461a34..8dae88b0a2 100644 --- a/src/Testing/LevelsTestCase.php +++ b/src/Testing/LevelsTestCase.php @@ -6,10 +6,18 @@ use Nette\Utils\JsonException; use PHPStan\File\FileHelper; use PHPStan\File\FileWriter; +use PHPStan\Process\CpuCoreCounter; +use PHPStan\Process\ProcessCrashedException; +use PHPStan\Process\ProcessPromise; +use PHPStan\Process\Runnable\RunnableQueue; +use PHPStan\Process\Runnable\RunnableQueueLogger; use PHPStan\ShouldNotHappenException; use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\TestCase; +use React\EventLoop\StreamSelectLoop; +use React\Promise\Deferred; use function array_merge; +use function Clue\React\Block\await; use function count; use function escapeshellarg; use function escapeshellcmd; @@ -17,6 +25,7 @@ use function implode; use function method_exists; use function range; +use function React\Promise\all; use function sprintf; use function unlink; use const DIRECTORY_SEPARATOR; @@ -68,12 +77,39 @@ public function testLevels( throw new ShouldNotHappenException('Could not clear result cache: ' . implode("\n", $clearResultCacheOutputLines)); } + $loop = new StreamSelectLoop(); + $cpuCoreCounter = new CpuCoreCounter(); + $queue = new RunnableQueue(new class implements RunnableQueueLogger { + + public function log(string $message): void + { + } + + }, $cpuCoreCounter->getNumberOfCpuCores()); + $promises = []; foreach (range(0, 9) as $level) { - unset($outputLines); - exec(sprintf('%s %s analyse --no-progress --error-format=prettyJson --level=%d %s %s %s', escapeshellarg(PHP_BINARY), $command, $level, $configPath !== null ? '--configuration ' . escapeshellarg($configPath) : '', $this->shouldAutoloadAnalysedFile() ? sprintf('--autoload-file %s', escapeshellarg($file)) : '', escapeshellarg($file)), $outputLines); + $process = new ProcessPromise($loop, 'level-' . $level, sprintf('%s %s analyse --no-progress --error-format=prettyJson --level=%d %s %s %s', escapeshellarg(PHP_BINARY), $command, $level, $configPath !== null ? '--configuration ' . escapeshellarg($configPath) : '', $this->shouldAutoloadAnalysedFile() ? sprintf('--autoload-file %s', escapeshellarg($file)) : '', escapeshellarg($file))); + $deferred = new Deferred(); + $queue->queue($process, 1)->then(static function (string $output) use ($deferred): void { + $deferred->resolve($output); + }, static function (ProcessCrashedException $crash) use ($deferred): void { + $exitCode = $crash->getExitCode(); + if ($exitCode !== 1) { + $deferred->reject($crash); + } + try { + Json::decode($crash->getStdOut()); + } catch (JsonException) { + $deferred->reject($crash); + return; + } - $output = implode("\n", $outputLines); + $deferred->resolve($crash->getStdOut()); + }); + $promises[$level] = $deferred->promise(); + } + foreach (await(all($promises), $loop) as $level => $output) { try { $actualJson = Json::decode($output, Json::FORCE_ARRAY); } catch (JsonException) { From 594f71b684f951129c19690bd8ca26e6f337c6c8 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 28 Mar 2022 05:54:13 +0200 Subject: [PATCH 2/2] Revert "Rework the patch" This reverts commit d99708f1971402b100ad3556a8b61a0eef326491. --- .../php72-paratest400.diff | 13 ++++++------- .github/workflows/tests.yml | 3 +++ 2 files changed, 9 insertions(+), 7 deletions(-) rename patches/paratest.patch => .github/php72-paratest400.diff (73%) diff --git a/patches/paratest.patch b/.github/php72-paratest400.diff similarity index 73% rename from patches/paratest.patch rename to .github/php72-paratest400.diff index 1c38e962db..abbb1ce78c 100644 --- a/patches/paratest.patch +++ b/.github/php72-paratest400.diff @@ -1,9 +1,8 @@ -@package brianium/paratest -@version ^4.0 - ---- src/Runners/PHPUnit/Worker/BaseWorker.php 2020-02-07 23:07:07.000000000 +0100 -+++ src/Runners/PHPUnit/Worker/BaseWorker.php 2022-03-27 17:35:45.000000000 +0200 -@@ -28,17 +28,18 @@ +diff --git a/vendor/brianium/paratest/src/Runners/PHPUnit/Worker/BaseWorker.php b/vendor/brianium/paratest/src/Runners/PHPUnit/Worker/BaseWorker.php +index f96dc0a..b038ee2 100644 +--- a/vendor/brianium/paratest/src/Runners/PHPUnit/Worker/BaseWorker.php ++++ b/vendor/brianium/paratest/src/Runners/PHPUnit/Worker/BaseWorker.php +@@ -28,17 +28,18 @@ abstract class BaseWorker array $parameters = [], ?Options $options = null ) { @@ -27,7 +26,7 @@ if ($options && $options->passthruPhp) { $bin .= $options->passthruPhp . ' '; } -@@ -50,7 +51,7 @@ +@@ -50,7 +51,7 @@ abstract class BaseWorker if ($options && $options->verbose) { echo "Starting WrapperWorker via: $bin\n"; } diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5565e8b2bd..44e137446f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -188,5 +188,8 @@ jobs: - name: "Downgrade PHPUnit" run: "composer require --dev phpunit/phpunit:^7.5.20 brianium/paratest:^4.0 --update-with-dependencies --ignore-platform-reqs" + - name: "Apply ParaTest patch to allow WrapperRunner" + run: "git apply --ignore-whitespace .github/php72-paratest400.diff" + - name: "Tests" run: "${{ matrix.script }}"