feat(test): support sharding tests across CI runners - #1707
Draft
peter-trost wants to merge 2 commits into
Draft
Conversation
Add `--shard-index` and `--total-shards` to `very_good test` and `very_good dart test`, so a test suite can be split across multiple CI runners with a `strategy.matrix`. The test optimizer already discovers every test file, so sharding is a partition of that list rather than new machinery. Files are sorted and dealt out round-robin, which keeps shards balanced by file count and makes the partition deterministic across machines — `Directory.listSync` order is filesystem dependent, so without sorting two runners could disagree and either skip or duplicate tests. Tests tagged `skip_very_good_optimization` are sharded as well. They run as standalone files alongside the optimizer entrypoint, so leaving them unsharded would re-run all of them on every runner. Each runner generates its own `.test_optimizer.dart` containing only its slice, so no shard-specific filenames are needed. A shard with no test files succeeds instead of failing with "No tests were found", so an oversized matrix does not break the build. Sharding is rejected with a usage error when combined with `--min-coverage`, since each shard only exercises a subset of the codebase and its coverage is not representative of the whole suite, and when the optimizer is disabled, which sharding depends on. Closes VeryGoodOpenSource#1538 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Normalize path separators in getNotOptimizedTests so tagged tests in subdirectories are excluded from the optimized set on Windows. Without this the relative paths compare unequal to the forward-slash normalized paths built in run(), causing those tests to be both inlined into the optimizer entrypoint and run standalone. Parse the shard values inside validateSharding instead of passing both the raw and parsed forms, and shorten the --min-coverage error. Also cover the shard that only contains non optimized tests, and document how sharding interacts with --recursive. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds
--shard-indexand--total-shardstovery_good testandvery_good dart test, so a test suite can be split across multiple CI runners with astrategy.matrix.Opening as a draft because @ryzizub is assigned to #1538 — happy to hand this over, close it, or adapt to a different design if work is already underway or a different approach is preferred.
Why this approach
The test optimizer already discovers every test file, so sharding is a partition of that list rather than new machinery.
Directory.listSyncorder is filesystem dependent, so without sorting two runners could disagree on the partition and either skip or duplicate tests. Sorting first makes the partition deterministic across machines; dealing out round-robin keeps shards balanced by file count.skip_very_good_optimizationrun as standalone files alongside the optimizer entrypoint. Leaving them unsharded would re-run all of them on every runner, partly defeating the purpose..test_optimizer_1_of_3.dart. That turned out to be unnecessary — each runner generates its own.test_optimizer.dartcontaining only its slice, and the file is cleaned up afterwards anyway. Same result, no filename plumbing.No tests were found, breaking builds on oversized matrices. It now reports success, matching the existing "no test folder" behaviour.Interaction with coverage
--min-coverageis rejected with a usage error when sharding. Each shard only exercises a subset of the codebase, so its coverage is not representative of the whole suite and would fail the build spuriously. The error explains the alternative: collect per-shard coverage with--coverage, merge the lcov reports, and enforce the threshold once in a separate job.Sharding also requires the optimizer, so it is rejected with
--no-optimizationand with--platform(which disables the optimizer). Both cases exit withExitCode.usageand an actionable message.This is the part I'd most like a maintainer opinion on — a documented merge-then-check workflow is the other reasonable option, and #804 may change what's possible here.
Testing
testanddart test.dart analyze --fatal-infos --fatal-warnings .is clean, and the touchedlib/files are at 100% line coverage.Note
The full suite passes with
-j 1(529 tests). At-j 8a handful of unrelated tests fail due to a pre-existing working-directory race between test files — I confirmed the same failures on a cleanmaincheckout, so it is not introduced here.Closes #1538
🤖 Generated with Claude Code