Skip to content

[CI] Replace inline Go test runner with tomasvotruba/fast-unit package (parallel runner + -tia) - #8351

Merged
TomasVotruba merged 8 commits into
mainfrom
use-fast-unit-runner
Aug 15, 2026
Merged

[CI] Replace inline Go test runner with tomasvotruba/fast-unit package (parallel runner + -tia)#8351
TomasVotruba merged 8 commits into
mainfrom
use-fast-unit-runner

Conversation

@TomasVotruba

@TomasVotruba TomasVotruba commented Aug 15, 2026

Copy link
Copy Markdown
Member

Extracts the inline Go PHPUnit runner (utils-tests-runner/, added in #8349) into a standalone, reusable Composer package -- tomasvotruba/fast-unit -- and adopts it as the main test runner here.

What it does

fast-unit splits the suite's test classes into N weight-balanced chunks and runs each chunk in one warm PHP process, so the container boots N times instead of once per class. It ships prebuilt Go binaries fetched on first run (sha256-verified), so no Go toolchain is needed; the downloader shim is PHP 7.2+ compatible.

Speed -- ubuntu CI, same 4-vCPU hardware

mode wall time vs serial
serial vendor/bin/phpunit ~29-34s 1.0x
go run (previous, on main) ~22-23s ~1.5x
vendor/bin/fastunit (this PR) ~17s ~2.0x

The prebuilt binary also beats the old go run step, which recompiled the runner on every invocation. The full ~4x from the runner's README needs more cores; GitHub runners give 4 vCPU.

Changes

  • Consume the package. composer.json requires tomasvotruba/fast-unit (dev), plus test / test-tia scripts. utils-tests-runner/ is removed.
  • Main runner. tests.yaml runs vendor/bin/fastunit over tests rules-tests utils/phpstan/tests.
  • Clickable failing fixture. The fixture data provider is now keyed by path, so a failure prints the exact .php.inc file (clickable in an IDE) instead of an anonymous data set #N:
1) Rector\Tests\...\CombineIfRectorTest::test@.../Fixture/docblock.php.inc

Optional -tia (test impact analysis)

fast-unit -tia runs only the tests whose code changed, using a static tokenizer-based dependency graph (no pcov/xdebug, works on any PHP 7.2+). For each test it takes the transitive closure from its directory (so a rule wired in config/configured_rule.php counts) and re-runs only classes whose closure or fixtures changed vs a cached sha256 snapshot.

TIA: 1 changed files, 1 impacted, 10 skipped    # edited one rule
TIA: 0 changed files, 0 impacted, 688 skipped   # nothing changed -> ~1s

Safe by construction: static analysis over-approximates, so it may run a few extra tests but never silently skips a real dependency; first run or any scan error runs everything.

A PR-only tests_tia job runs it with a persisted .fastunit-cache (via actions/cache) for fast feedback. It is not the merge gate -- the full tests job stays authoritative, since -tia cannot see dynamic (reflection / class-string) dependencies.

yield [$fileInfo->getRealPath()];
// key the data set by fixture path, so a failure prints the exact
// clickable ".php.inc" file instead of an anonymous "data set #N"
yield $fileInfo->getRealPath() => [$fileInfo->getRealPath()];

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Time to give this a go 👍

- key fixture data provider by path, so a failure prints the exact
  clickable .php.inc file instead of 'data set #N'
- tests.yaml runs vendor/bin/fastunit over tests + rules-tests + utils/phpstan/tests
… composer test script

The parallel runner batches many test classes into one warm PHP process,
which exposed a latent leak: RectorConfig::configure() marks only the first
call in a process as 'root', so RectorConfigTest's root-rule assertions failed
when another class configured first. Add RectorConfig::resetRecreated() and
reset it (plus the registered-rule lists) in the test's setUp.

Also add composer 'test' script running fastunit over the suite dirs.
…itignore

- benchmark: keep fast-unit wording, ubuntu-only (Windows dropped in #8352)
- .gitignore: main already ignores .fastunit-cache; drop the duplicate
Runs only tests whose static dependency closure changed, with a persisted
.fastunit-cache via actions/cache. PR-only and non-blocking in intent: the
full 'tests' job stays the authoritative gate, since -tia over-approximates
but cannot see dynamic (reflection / class-string) dependencies.
@TomasVotruba
TomasVotruba marked this pull request as ready for review August 15, 2026 18:54
@TomasVotruba TomasVotruba changed the title [CI] Use extracted tomasvotruba/fast-unit package instead of inline Go runner [CI] Replace inline Go test runner with tomasvotruba/fast-unit package (parallel runner + -tia) Aug 15, 2026
@TomasVotruba
TomasVotruba merged commit 4574023 into main Aug 15, 2026
51 checks passed
@TomasVotruba

Copy link
Copy Markdown
Member Author

Let's give this a go :)

@TomasVotruba
TomasVotruba deleted the use-fast-unit-runner branch August 15, 2026 20:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant