From e24882c48644fbafb6c0de6402ab5fd2336a98ea Mon Sep 17 00:00:00 2001 From: nuno maduro Date: Tue, 21 Apr 2026 09:41:19 -0700 Subject: [PATCH] wip --- src/Concerns/Testable.php | 2 +- src/Plugins/Tia.php | 56 +++++++++---------- src/Plugins/Tia/BaselineSync.php | 6 +- src/Plugins/Tia/ChangedFiles.php | 4 +- src/Plugins/Tia/CoverageMerger.php | 15 ++--- src/Plugins/Tia/FileState.php | 4 +- ...sureTiaAssertionsAreRecordedOnFinished.php | 4 +- .../EnsureTiaResultIsRecordedOnErrored.php | 4 +- .../EnsureTiaResultIsRecordedOnFailed.php | 4 +- .../EnsureTiaResultIsRecordedOnIncomplete.php | 4 +- .../EnsureTiaResultIsRecordedOnPassed.php | 4 +- .../EnsureTiaResultIsRecordedOnRisky.php | 4 +- .../EnsureTiaResultIsRecordedOnSkipped.php | 4 +- .../EnsureTiaResultsAreCollected.php | 4 +- src/Support/Coverage.php | 3 +- 15 files changed, 61 insertions(+), 61 deletions(-) diff --git a/src/Concerns/Testable.php b/src/Concerns/Testable.php index 8ffbeefc..6d304765 100644 --- a/src/Concerns/Testable.php +++ b/src/Concerns/Testable.php @@ -9,8 +9,8 @@ use Pest\Exceptions\DatasetArgumentsMismatch; use Pest\Panic; use Pest\Plugins\Tia; use Pest\Preset; -use Pest\Support\Container; use Pest\Support\ChainableClosure; +use Pest\Support\Container; use Pest\Support\ExceptionTrace; use Pest\Support\Reflection; use Pest\Support\Shell; diff --git a/src/Plugins/Tia.php b/src/Plugins/Tia.php index 87eb8691..e66ee239 100644 --- a/src/Plugins/Tia.php +++ b/src/Plugins/Tia.php @@ -8,7 +8,6 @@ use NunoMaduro\Collision\Adapters\Phpunit\Printers\DefaultPrinter; use Pest\Contracts\Plugins\AddsOutput; use Pest\Contracts\Plugins\HandlesArguments; use Pest\Contracts\Plugins\Terminable; -use PHPUnit\Framework\TestStatus\TestStatus; use Pest\Plugins\Tia\BaselineSync; use Pest\Plugins\Tia\ChangedFiles; use Pest\Plugins\Tia\Contracts\State; @@ -20,6 +19,7 @@ use Pest\Plugins\Tia\ResultCollector; use Pest\Plugins\Tia\WatchPatterns; use Pest\Support\Container; use Pest\TestSuite; +use PHPUnit\Framework\TestStatus\TestStatus; use Symfony\Component\Console\Output\OutputInterface; use Throwable; @@ -173,12 +173,12 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable */ private array $affectedFiles = []; - private static function workerEdgesKey(string $token): string + private function workerEdgesKey(string $token): string { return self::KEY_WORKER_EDGES_PREFIX.$token.'.json'; } - private static function workerResultsKey(string $token): string + private function workerResultsKey(string $token): string { return self::KEY_WORKER_RESULTS_PREFIX.$token.'.json'; } @@ -242,7 +242,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable */ public function getCachedResult(string $filename, string $testId): ?TestStatus { - if ($this->replayGraph === null) { + if (! $this->replayGraph instanceof Graph) { return null; } @@ -271,7 +271,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable // branch (falls back to main if branch is fresh). $result = $this->replayGraph->getResult($this->branch, $testId); - if ($result !== null) { + if ($result instanceof TestStatus) { $this->replayedCount++; // Cache the assertion count alongside the status so `Testable` // can emit the exact `addToAssertionCount()` at replay time @@ -351,7 +351,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable // the graph lands with results on first write — otherwise the next // run would load a graph with edges but empty results, miss the // cache for every test, and look pointlessly slow. - if (Parallel::isWorker() && ($this->replayGraph !== null || $this->recordingActive)) { + if (Parallel::isWorker() && ($this->replayGraph instanceof Graph || $this->recordingActive)) { $this->flushWorkerReplay(); } @@ -376,7 +376,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable } if (Parallel::isWorker()) { - $this->flushWorkerPartial($projectRoot, $perTest); + $this->flushWorkerPartial($perTest); $recorder->reset(); $this->coverageCollector->reset(); @@ -635,13 +635,10 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable // to pull a team-shared baseline so fresh checkouts (new devs, CI // containers) don't pay the full record cost. If the pull succeeds // the graph is re-read and reconciled against the local env. - if ($graph === null && ! $forceRebuild) { - if ($this->baselineSync->fetchIfAvailable($projectRoot)) { - $graph = $this->loadGraph($projectRoot); - - if ($graph instanceof Graph) { - $graph = $this->reconcileFingerprint($graph, $fingerprint); - } + if (! $graph instanceof Graph && ! $forceRebuild && $this->baselineSync->fetchIfAvailable($projectRoot)) { + $graph = $this->loadGraph($projectRoot); + if ($graph instanceof Graph) { + $graph = $this->reconcileFingerprint($graph, $fingerprint); } } @@ -657,14 +654,14 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable // report collapses to near-zero coverage. Fall back to recording // (full suite) to seed the cache for next time. if ($this->piggybackCoverage && ! $this->state->exists(self::KEY_COVERAGE_CACHE)) { - return $this->enterRecordMode($projectRoot, $arguments); + return $this->enterRecordMode($arguments); } if ($graph instanceof Graph) { return $this->enterReplayMode($graph, $projectRoot, $arguments); } - return $this->enterRecordMode($projectRoot, $arguments); + return $this->enterRecordMode($arguments); } /** @@ -792,7 +789,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable } // Parallel: persist affected set so workers can install the filter. - if (! $this->persistAffectedSet($projectRoot, $affected)) { + if (! $this->persistAffectedSet($affected)) { $this->output->writeln( ' TIA failed to persist affected set — running full suite.', ); @@ -802,7 +799,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable // Clear stale partials from a previous interrupted run so the merge // pass doesn't pick up results from an unrelated invocation. - $this->purgeWorkerPartials($projectRoot); + $this->purgeWorkerPartials(); Parallel::setGlobal(self::REPLAYING_GLOBAL, '1'); @@ -812,7 +809,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable /** * @param array $affected Project-relative paths. */ - private function persistAffectedSet(string $projectRoot, array $affected): bool + private function persistAffectedSet(array $affected): bool { $json = json_encode(array_values($affected), JSON_UNESCAPED_SLASHES); @@ -827,7 +824,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable * @param array $arguments * @return array */ - private function enterRecordMode(string $projectRoot, array $arguments): array + private function enterRecordMode(array $arguments): array { $recorder = $this->recorder; @@ -852,7 +849,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable // recording. We only advertise the intent through a global. // Clean up any stale partial files from a previous interrupted // run so the merge step doesn't confuse itself. - $this->purgeWorkerPartials($projectRoot); + $this->purgeWorkerPartials(); Parallel::setGlobal(self::RECORDING_GLOBAL, '1'); @@ -907,7 +904,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable /** * @param array> $perTest */ - private function flushWorkerPartial(string $projectRoot, array $perTest): void + private function flushWorkerPartial(array $perTest): void { $json = json_encode($perTest, JSON_UNESCAPED_SLASHES); @@ -915,7 +912,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable return; } - $this->state->write(self::workerEdgesKey($this->workerToken()), $json); + $this->state->write($this->workerEdgesKey($this->workerToken()), $json); } /** @@ -926,12 +923,11 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable return $this->state->keysWithPrefix(self::KEY_WORKER_EDGES_PREFIX); } - private function purgeWorkerPartials(string $projectRoot): void + private function purgeWorkerPartials(): void { foreach ($this->collectWorkerEdgesPartials() as $key) { $this->state->delete($key); } - foreach ($this->collectWorkerReplayPartials() as $key) { $this->state->delete($key); } @@ -963,7 +959,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable return; } - $this->state->write(self::workerResultsKey($this->workerToken()), $json); + $this->state->write($this->workerResultsKey($this->workerToken()), $json); } /** @@ -1011,10 +1007,12 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable /** @var mixed $result */ foreach ($decoded['results'] as $testId => $result) { - if (! is_string($testId) || ! is_array($result)) { + if (! is_string($testId)) { + continue; + } + if (! is_array($result)) { continue; } - $normalised[$testId] = [ 'status' => is_int($result['status'] ?? null) ? $result['status'] : 0, 'message' => is_string($result['message'] ?? null) ? $result['message'] : '', @@ -1119,7 +1117,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable $fragments[] = $this->replayedCount.' replayed'; } - return $fragments === [] ? '' : implode(', ', $fragments); + return implode(', ', $fragments); }); } diff --git a/src/Plugins/Tia/BaselineSync.php b/src/Plugins/Tia/BaselineSync.php index 9fe5b97e..97466f85 100644 --- a/src/Plugins/Tia/BaselineSync.php +++ b/src/Plugins/Tia/BaselineSync.php @@ -39,7 +39,7 @@ use Symfony\Component\Process\Process; * * @internal */ -final class BaselineSync +final readonly class BaselineSync { /** * Conventional workflow filename teams publish from. Not configurable @@ -63,8 +63,8 @@ final class BaselineSync private const string COVERAGE_ASSET = Tia::KEY_COVERAGE_CACHE; public function __construct( - private readonly State $state, - private readonly OutputInterface $output, + private State $state, + private OutputInterface $output, ) {} /** diff --git a/src/Plugins/Tia/ChangedFiles.php b/src/Plugins/Tia/ChangedFiles.php index e24fda26..171b007a 100644 --- a/src/Plugins/Tia/ChangedFiles.php +++ b/src/Plugins/Tia/ChangedFiles.php @@ -38,7 +38,7 @@ final readonly class ChangedFiles * that git still reports as modified but whose content is bit-identical * to the previous TIA invocation. * - * @param array $files project-relative paths. + * @param array $files project-relative paths. * @param array $lastRunTree path → content hash from last run. * @return array */ @@ -101,7 +101,7 @@ final readonly class ChangedFiles * detect which files are actually different. * * @param array $files - * @return array path → xxh128 content hash + * @return array path → xxh128 content hash */ public function snapshotTree(array $files): array { diff --git a/src/Plugins/Tia/CoverageMerger.php b/src/Plugins/Tia/CoverageMerger.php index fb482023..0f984571 100644 --- a/src/Plugins/Tia/CoverageMerger.php +++ b/src/Plugins/Tia/CoverageMerger.php @@ -46,7 +46,7 @@ final class CoverageMerger { $state = self::state(); - if ($state === null || ! $state->exists(Tia::KEY_COVERAGE_MARKER)) { + if (! $state instanceof State || ! $state->exists(Tia::KEY_COVERAGE_MARKER)) { return; } @@ -60,7 +60,7 @@ final class CoverageMerger // verbatim (as serialised bytes) for next time. $current = self::requireCoverage($reportPath); - if ($current !== null) { + if ($current instanceof CodeCoverage) { $state->write(Tia::KEY_COVERAGE_CACHE, serialize($current)); } @@ -70,7 +70,7 @@ final class CoverageMerger $cached = self::unserializeCoverage($cachedBytes); $current = self::requireCoverage($reportPath); - if ($cached === null || $current === null) { + if (! $cached instanceof CodeCoverage || ! $current instanceof CodeCoverage) { return; } @@ -84,7 +84,7 @@ final class CoverageMerger // can `require` it, and to the state cache for the next run. @file_put_contents( $reportPath, - "write(Tia::KEY_COVERAGE_CACHE, $serialised); } @@ -108,10 +108,12 @@ final class CoverageMerger foreach ($lineCoverage as $file => $lines) { foreach ($lines as $line => $ids) { - if ($ids === null || $ids === []) { + if ($ids === null) { + continue; + } + if ($ids === []) { continue; } - $filtered = array_values(array_diff($ids, $currentIds)); if ($filtered !== $ids) { @@ -175,7 +177,6 @@ final class CoverageMerger private static function unserializeCoverage(string $bytes): ?CodeCoverage { try { - /** @var mixed $value */ $value = @unserialize($bytes); } catch (Throwable) { return null; diff --git a/src/Plugins/Tia/FileState.php b/src/Plugins/Tia/FileState.php index 33060d8e..d1938407 100644 --- a/src/Plugins/Tia/FileState.php +++ b/src/Plugins/Tia/FileState.php @@ -17,14 +17,14 @@ use Pest\Plugins\Tia\Contracts\State; * * @internal */ -final class FileState implements State +final readonly class FileState implements State { /** * Configured root. May not exist on disk yet; resolved + created on * the first write. Keeping the raw string lets the instance be built * before Pest's temp dir has been materialised. */ - private readonly string $rootDir; + private string $rootDir; public function __construct(string $rootDir) { diff --git a/src/Subscribers/EnsureTiaAssertionsAreRecordedOnFinished.php b/src/Subscribers/EnsureTiaAssertionsAreRecordedOnFinished.php index a6f1e2a9..9764919b 100644 --- a/src/Subscribers/EnsureTiaAssertionsAreRecordedOnFinished.php +++ b/src/Subscribers/EnsureTiaAssertionsAreRecordedOnFinished.php @@ -16,9 +16,9 @@ use PHPUnit\Event\Test\FinishedSubscriber; * * @internal */ -final class EnsureTiaAssertionsAreRecordedOnFinished implements FinishedSubscriber +final readonly class EnsureTiaAssertionsAreRecordedOnFinished implements FinishedSubscriber { - public function __construct(private readonly ResultCollector $collector) {} + public function __construct(private ResultCollector $collector) {} public function notify(Finished $event): void { diff --git a/src/Subscribers/EnsureTiaResultIsRecordedOnErrored.php b/src/Subscribers/EnsureTiaResultIsRecordedOnErrored.php index c6cd4a27..ecdf3833 100644 --- a/src/Subscribers/EnsureTiaResultIsRecordedOnErrored.php +++ b/src/Subscribers/EnsureTiaResultIsRecordedOnErrored.php @@ -11,9 +11,9 @@ use PHPUnit\Event\Test\ErroredSubscriber; /** * @internal */ -final class EnsureTiaResultIsRecordedOnErrored implements ErroredSubscriber +final readonly class EnsureTiaResultIsRecordedOnErrored implements ErroredSubscriber { - public function __construct(private readonly ResultCollector $collector) {} + public function __construct(private ResultCollector $collector) {} public function notify(Errored $event): void { diff --git a/src/Subscribers/EnsureTiaResultIsRecordedOnFailed.php b/src/Subscribers/EnsureTiaResultIsRecordedOnFailed.php index c46cf8a0..29940e0c 100644 --- a/src/Subscribers/EnsureTiaResultIsRecordedOnFailed.php +++ b/src/Subscribers/EnsureTiaResultIsRecordedOnFailed.php @@ -11,9 +11,9 @@ use PHPUnit\Event\Test\FailedSubscriber; /** * @internal */ -final class EnsureTiaResultIsRecordedOnFailed implements FailedSubscriber +final readonly class EnsureTiaResultIsRecordedOnFailed implements FailedSubscriber { - public function __construct(private readonly ResultCollector $collector) {} + public function __construct(private ResultCollector $collector) {} public function notify(Failed $event): void { diff --git a/src/Subscribers/EnsureTiaResultIsRecordedOnIncomplete.php b/src/Subscribers/EnsureTiaResultIsRecordedOnIncomplete.php index fe91ecbb..330525e8 100644 --- a/src/Subscribers/EnsureTiaResultIsRecordedOnIncomplete.php +++ b/src/Subscribers/EnsureTiaResultIsRecordedOnIncomplete.php @@ -11,9 +11,9 @@ use PHPUnit\Event\Test\MarkedIncompleteSubscriber; /** * @internal */ -final class EnsureTiaResultIsRecordedOnIncomplete implements MarkedIncompleteSubscriber +final readonly class EnsureTiaResultIsRecordedOnIncomplete implements MarkedIncompleteSubscriber { - public function __construct(private readonly ResultCollector $collector) {} + public function __construct(private ResultCollector $collector) {} public function notify(MarkedIncomplete $event): void { diff --git a/src/Subscribers/EnsureTiaResultIsRecordedOnPassed.php b/src/Subscribers/EnsureTiaResultIsRecordedOnPassed.php index 739b213a..09ebcc21 100644 --- a/src/Subscribers/EnsureTiaResultIsRecordedOnPassed.php +++ b/src/Subscribers/EnsureTiaResultIsRecordedOnPassed.php @@ -11,9 +11,9 @@ use PHPUnit\Event\Test\PassedSubscriber; /** * @internal */ -final class EnsureTiaResultIsRecordedOnPassed implements PassedSubscriber +final readonly class EnsureTiaResultIsRecordedOnPassed implements PassedSubscriber { - public function __construct(private readonly ResultCollector $collector) {} + public function __construct(private ResultCollector $collector) {} public function notify(Passed $event): void { diff --git a/src/Subscribers/EnsureTiaResultIsRecordedOnRisky.php b/src/Subscribers/EnsureTiaResultIsRecordedOnRisky.php index 8554816e..fe65f6eb 100644 --- a/src/Subscribers/EnsureTiaResultIsRecordedOnRisky.php +++ b/src/Subscribers/EnsureTiaResultIsRecordedOnRisky.php @@ -11,9 +11,9 @@ use PHPUnit\Event\Test\ConsideredRiskySubscriber; /** * @internal */ -final class EnsureTiaResultIsRecordedOnRisky implements ConsideredRiskySubscriber +final readonly class EnsureTiaResultIsRecordedOnRisky implements ConsideredRiskySubscriber { - public function __construct(private readonly ResultCollector $collector) {} + public function __construct(private ResultCollector $collector) {} public function notify(ConsideredRisky $event): void { diff --git a/src/Subscribers/EnsureTiaResultIsRecordedOnSkipped.php b/src/Subscribers/EnsureTiaResultIsRecordedOnSkipped.php index 1b94cf66..58de98ad 100644 --- a/src/Subscribers/EnsureTiaResultIsRecordedOnSkipped.php +++ b/src/Subscribers/EnsureTiaResultIsRecordedOnSkipped.php @@ -11,9 +11,9 @@ use PHPUnit\Event\Test\SkippedSubscriber; /** * @internal */ -final class EnsureTiaResultIsRecordedOnSkipped implements SkippedSubscriber +final readonly class EnsureTiaResultIsRecordedOnSkipped implements SkippedSubscriber { - public function __construct(private readonly ResultCollector $collector) {} + public function __construct(private ResultCollector $collector) {} public function notify(Skipped $event): void { diff --git a/src/Subscribers/EnsureTiaResultsAreCollected.php b/src/Subscribers/EnsureTiaResultsAreCollected.php index aec17fea..7d432865 100644 --- a/src/Subscribers/EnsureTiaResultsAreCollected.php +++ b/src/Subscribers/EnsureTiaResultsAreCollected.php @@ -20,9 +20,9 @@ use PHPUnit\Event\Test\PreparedSubscriber; * * @internal */ -final class EnsureTiaResultsAreCollected implements PreparedSubscriber +final readonly class EnsureTiaResultsAreCollected implements PreparedSubscriber { - public function __construct(private readonly ResultCollector $collector) {} + public function __construct(private ResultCollector $collector) {} public function notify(Prepared $event): void { diff --git a/src/Support/Coverage.php b/src/Support/Coverage.php index 6845ad4e..62d24212 100644 --- a/src/Support/Coverage.php +++ b/src/Support/Coverage.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Pest\Support; use Pest\Exceptions\ShouldNotHappen; +use Pest\Plugins\Tia\CoverageMerger; use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeCoverage\Node\Directory; use SebastianBergmann\CodeCoverage\Node\File; @@ -92,7 +93,7 @@ final class Coverage // tests. Merge their fresh coverage slice into the cached full-run // snapshot (stored by the previous `--tia --coverage` pass) so the // report reflects the entire suite, not just what re-ran. - \Pest\Plugins\Tia\CoverageMerger::applyIfMarked($reportPath); + CoverageMerger::applyIfMarked($reportPath); /** @var CodeCoverage $codeCoverage */ $codeCoverage = require $reportPath;