Skip to content

Allow --shard-by=file#2655

Open
sigurdm wants to merge 6 commits into
masterfrom
shard-by-file-docs-and-tests
Open

Allow --shard-by=file#2655
sigurdm wants to merge 6 commits into
masterfrom
shard-by-file-docs-and-tests

Conversation

@sigurdm
Copy link
Copy Markdown
Contributor

@sigurdm sigurdm commented May 26, 2026

Parsing all files, and running all setupAll hooks is expensive when what we are trying to do is to minimize the time spent for each shard.

This allows choosing a less fine-grained strategy of sharding by individual test files.

Fixes #2614

@sigurdm sigurdm requested a review from a team as a code owner May 26, 2026 07:57
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 26, 2026

PR Health

Changelog Entry ✔️
Package Changed Files

Changes to files need to be accounted for in their respective changelogs.

This check can be disabled by tagging the PR with skip-changelog-check.

--total-shards The total number of invocations of the test runner being run.
--shard-index The index of this test runner invocation (of --total-shards).
--shard-by How to distribute tests across shards.
[test (default), file]
Copy link
Copy Markdown
Member

@lrhn lrhn May 26, 2026

Choose a reason for hiding this comment

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

Any expectations of other granularities?
(What would that even be? "Directory", but most packages have all the tests in one directory anyway.)

Is there any reason to not always use --shard=by=file?
(If you have only one big file with many tests, I guess?)

Comment thread pkgs/test/README.md
as evenly as possible across all shards.
* When sharding by `file`, the files are partitioned *before* they are loaded
and filtered. If a filter only matches tests in a few files, some shards
might run no tests because those files were allocated to other shards.
Copy link
Copy Markdown
Member

@lrhn lrhn May 26, 2026

Choose a reason for hiding this comment

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

Why not shard after filtering?

I'm assuming there is a reason, but it would be nice to know what it is.
I guess each shard will individually do the same "find all tests" computation and sharding, but if it can do that normally, it should be able to do it sharding by file as well. As long as all the shards agree on the test-set, they should agree after filtering too.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some filters are runtime only, but we should be able to respect the top level @TestOn annotation filters without loading the test.


source = StreamGroup.merge(
shardFiles.map((path) {
final key = _config.testSelections.keys.firstWhere(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(Can use _config.testSelections.entries.firstWhere((e) => path == e.key || p.isWithin(e.key, path), ..., then you don't need to do a ...[key]! afterwards)

final Stream<LoadSuite> source;
if (_config.totalShards != null && _config.shardBy == 'file') {
final allFiles = <String>[];
for (var pathEntry in _config.testSelections.entries) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(Can use patter assignment:

for (var MapEntry(key: testPath, value: ...)) ...

but I'm not sure the value is actually used, so it could just iterate keys?)

@jakemac53
Copy link
Copy Markdown
Contributor

Meta comment, but we call test "files" test "suites". So it might be best to make this --shard-by=suite. Although it may not be as intuitive for users, but it would match our general terminology.

I am curious what @natebosch thinks though.

@@ -172,6 +172,147 @@ void main() {
await test.shouldExit(79);
Copy link
Copy Markdown
Contributor

@jakemac53 jakemac53 May 26, 2026

Choose a reason for hiding this comment

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

Should add some edge cases, 0 test suites, less test suites than shards, more test suites than shards, etc.

@@ -172,6 +172,147 @@ void main() {
await test.shouldExit(79);
Copy link
Copy Markdown
Contributor

@jakemac53 jakemac53 May 26, 2026

Choose a reason for hiding this comment

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

I am also curious how this interacts with the @TestOn annotation as well as tags.

Comment thread pkgs/test/test/utils.dart
int? concurrency,
int? shardIndex,
int? totalShards,
String? shardBy,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Lets make this an enum

Stream<LoadSuite> _loadSuites() {
return StreamGroup.merge(
_config.testSelections.entries.map((pathEntry) {
final Stream<LoadSuite> source;
Copy link
Copy Markdown
Contributor

@jakemac53 jakemac53 May 26, 2026

Choose a reason for hiding this comment

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

I would break this out into separate functions instead of inlining it all.

Or instead of making shardBy an enum make it a sealed class where the subclasses contain implementations so this is just _config.shardBy.loadSuites(...) or something.

.map((f) => f.path)
.where((path) => _config.filename.matches(p.basename(path))),
);
} else if (File(testPath).existsSync()) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This test does nothig, you add the testPath to allFiles whether it's true or false.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable dart test to shard on files, not individual tests

3 participants