Skip to content

Commit 75e829d

Browse files
matanlureypull[bot]
authored andcommitted
Add --dry-run to dev/bots/test.dart. (flutter#158956)
Closes flutter#158884. /cc @reidbaker. Example output: ```sh % SHARD=tool_integration_tests SUBSHARD=6_6 dart dev/bots/test.dart --dry-run ▌13:59:18▐ STARTING ANALYSIS ▌13:59:18▐ --dry-run enabled. Tests will not actually be executed. ▌13:59:18▐ SHARD=tool_integration_tests ▌13:59:18▐ SUBSHARD=6_6 ▌13:59:18▐ |> bin/flutter: --version ▌13:59:18▐ |> bin/cache/dart-sdk/bin/dart (packages/flutter_tools): run test --reporter=expanded --file-reporter=json:/var/folders/qw/qw_3qd1x4kz5w975jhdq4k58007b7h/T/metrics_1731621558619861.json --test-randomize-ordering-seed=20241114 -j1 test/integration.shard/shader_compiler_test.dart test/integration.shard/overall_experience_test.dart test/integration.shard/expression_evaluation_test.dart test/integration.shard/isolated/native_assets_without_cbuild_assemble_test.dart test/integration.shard/isolated/native_assets_test.dart test/integration.shard/isolated/native_assets_agp_version_test.dart test/integration.shard/coverage_collection_test.dart test/integration.shard/devtools_uri_test.dart test/integration.shard/deprecated_gradle_settings_test.dart test/integration.shard/batch_entrypoint_test.dart test/integration.shard/bash_entrypoint_test.dart test/integration.shard/background_isolate_test.dart ```
1 parent 69ad705 commit 75e829d

File tree

5 files changed

+106
-28
lines changed

5 files changed

+106
-28
lines changed

dev/bots/run_command.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ Future<CommandResult> runCommand(String executable, List<String> arguments, {
162162
}) async {
163163
final String commandDescription = '${path.relative(executable, from: workingDirectory)} ${arguments.join(' ')}';
164164
final String relativeWorkingDir = workingDirectory ?? path.relative(io.Directory.current.path);
165+
if (dryRun) {
166+
printProgress(_prettyPrintRunCommand(executable, arguments, workingDirectory));
167+
return CommandResult._(
168+
0,
169+
Duration.zero,
170+
'$executable ${arguments.join(' ')}',
171+
'Simulated execution due to --dry-run',
172+
);
173+
}
165174

166175
final Command command = await startCommand(executable, arguments,
167176
workingDirectory: workingDirectory,
@@ -205,6 +214,23 @@ Future<CommandResult> runCommand(String executable, List<String> arguments, {
205214
return result;
206215
}
207216

217+
final String _flutterRoot = path.dirname(path.dirname(path.dirname(path.fromUri(io.Platform.script))));
218+
219+
String _prettyPrintRunCommand(String executable, List<String> arguments, String? workingDirectory) {
220+
final StringBuffer output = StringBuffer();
221+
222+
// Print CWD relative to the root.
223+
output.write('|> ');
224+
output.write(path.relative(executable, from: _flutterRoot));
225+
if (workingDirectory != null) {
226+
output.write(' (${path.relative(workingDirectory, from: _flutterRoot)})');
227+
}
228+
output.writeln(': ');
229+
output.writeAll(arguments.map((String a) => ' $a'), '\n');
230+
231+
return output.toString();
232+
}
233+
208234
/// Specifies what to do with the command output from [runCommand] and [startCommand].
209235
enum OutputMode {
210236
/// Forwards standard output and standard error streams to the test process'

dev/bots/suite_runners/run_web_tests.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,9 @@ class WebTestsSuite {
708708
// metriciFile is a transitional file that needs to be deleted once it is parsed.
709709
// TODO(godofredoc): Ensure metricFile is parsed and aggregated before deleting.
710710
// https://github.com/flutter/flutter/issues/146003
711-
metricFile.deleteSync();
711+
if (!dryRun) {
712+
metricFile.deleteSync();
713+
}
712714
}
713715

714716
// The `chromedriver` process created by this test.

dev/bots/test.dart

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,23 @@
44

55
// Runs the tests for the flutter/flutter repository.
66
//
7-
//
87
// By default, test output is filtered and only errors are shown. (If a
98
// particular test takes longer than _quietTimeout in utils.dart, the output is
109
// shown then also, in case something has hung.)
1110
//
1211
// --verbose stops the output cleanup and just outputs everything verbatim.
1312
//
14-
//
1513
// By default, errors are non-fatal; all tests are executed and the output
1614
// ends with a summary of the errors that were detected.
1715
//
1816
// Exit code is 1 if there was an error.
1917
//
2018
// --abort-on-error causes the script to exit immediately when hitting an error.
2119
//
22-
//
2320
// By default, all tests are run. However, the tests support being split by
24-
// shard and subshard. (Inspect the code to see what shards and subshards are
25-
// supported.)
21+
// shard and subshard. Inspect the code to see what shards and subshards are
22+
// supported, or run with `--dry-run` to get a list of tests that _would_ have
23+
// been executed.
2624
//
2725
// If the CIRRUS_TASK_NAME environment variable exists, it is used to determine
2826
// the shard and sub-shard, by parsing it in the form shard-subshard-platform,
@@ -43,7 +41,6 @@
4341
//
4442
// --test-randomize-ordering-seed=<n> sets the shuffle seed for reproducing runs.
4543
//
46-
//
4744
// All other arguments are treated as arguments to pass to the flutter tool when
4845
// running tests.
4946

@@ -96,6 +93,7 @@ const String CIRRUS_TASK_NAME = 'CIRRUS_TASK_NAME';
9693
Future<void> main(List<String> args) async {
9794
try {
9895
printProgress('STARTING ANALYSIS');
96+
bool dryRunArgSet = false;
9997
for (final String arg in args) {
10098
if (arg.startsWith('--local-engine=')) {
10199
localEngineEnv['FLUTTER_LOCAL_ENGINE'] = arg.substring('--local-engine='.length);
@@ -116,10 +114,16 @@ Future<void> main(List<String> args) async {
116114
onError = () {
117115
system.exit(1);
118116
};
117+
} else if (arg == '--dry-run') {
118+
dryRunArgSet = true;
119+
printProgress('--dry-run enabled. Tests will not actually be executed.');
119120
} else {
120121
flutterTestArgs.add(arg);
121122
}
122123
}
124+
if (dryRunArgSet) {
125+
enableDryRun();
126+
}
123127
if (Platform.environment.containsKey(CIRRUS_TASK_NAME)) {
124128
printProgress('Running task: ${Platform.environment[CIRRUS_TASK_NAME]}');
125129
}
@@ -541,6 +545,9 @@ Future<void> _flutterBuild(
541545
}
542546

543547
bool _allTargetsCached(File performanceFile) {
548+
if (dryRun) {
549+
return true;
550+
}
544551
final Map<String, Object?> data = json.decode(performanceFile.readAsStringSync())
545552
as Map<String, Object?>;
546553
final List<Map<String, Object?>> targets = (data['targets']! as List<Object?>)

dev/bots/test/test_test.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,15 @@ void main() {
154154
expectExitCode(result, 255);
155155
expect(result.stdout, contains('Invalid subshard name'));
156156
});
157+
158+
test('--dry-run prints every test that would run', () async {
159+
final ProcessResult result = await runScript(
160+
<String, String> {},
161+
<String>['--dry-run'],
162+
);
163+
expectExitCode(result, 0);
164+
expect(result.stdout, contains('|> bin/flutter'));
165+
}, testOn: 'posix');
157166
});
158167

159168
test('selectTestsForSubShard distributes tests amongst subshards correctly', () async {

dev/bots/utils.dart

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,27 @@ const String CIRRUS_TASK_NAME = 'CIRRUS_TASK_NAME';
6868
final Map<String,String> localEngineEnv = <String, String>{};
6969

7070
/// The arguments to pass to `flutter test` (typically the local engine
71-
/// configuration) -- prefilled with the arguments passed to test.dart.
71+
/// configuration) -- prefilled with the arguments passed to test.dart.
7272
final List<String> flutterTestArgs = <String>[];
7373

74+
/// Whether execution should be simulated for debugging purposes.
75+
///
76+
/// When `true`, calls to [runCommand] print to [io.stdout] instead of running
77+
/// the process. This is useful for determing what an invocation of `test.dart`
78+
/// _might_ due if not invoked with `--dry-run`, or otherwise determine what the
79+
/// different test shards and sub-shards are configured as.
80+
bool get dryRun => _dryRun ?? false;
81+
82+
/// Switches [dryRun] to `true`.
83+
///
84+
/// Expected to be called at most once during execution of a process.
85+
void enableDryRun() {
86+
if (_dryRun != null) {
87+
throw StateError('Should only be called at most once');
88+
}
89+
_dryRun = true;
90+
}
91+
bool? _dryRun;
7492

7593
const int kESC = 0x1B;
7694
const int kOpenSquareBracket = 0x5B;
@@ -135,6 +153,10 @@ final List<String> _pendingLogs = <String>[];
135153
Timer? _hideTimer; // When this is null, the output is verbose.
136154

137155
void foundError(List<String> messages) {
156+
if (dryRun) {
157+
printProgress(messages.join('\n'));
158+
return;
159+
}
138160
assert(messages.isNotEmpty);
139161
// Make the error message easy to notice in the logs by
140162
// wrapping it in a red box.
@@ -413,6 +435,10 @@ Future<void> runDartTest(String workingDirectory, {
413435
removeLine: useBuildRunner ? (String line) => line.startsWith('[INFO]') : null,
414436
);
415437

438+
if (dryRun) {
439+
return;
440+
}
441+
416442
final TestFileReporterResults test = TestFileReporterResults.fromFile(metricFile); // --file-reporter name
417443
final File info = fileSystem.file(path.join(flutterRoot, 'error.log'));
418444
info.writeAsStringSync(json.encode(test.errors));
@@ -508,7 +534,9 @@ Future<void> runFlutterTest(String workingDirectory, {
508534
// metriciFile is a transitional file that needs to be deleted once it is parsed.
509535
// TODO(godofredoc): Ensure metricFile is parsed and aggregated before deleting.
510536
// https://github.com/flutter/flutter/issues/146003
511-
metricFile.deleteSync();
537+
if (!dryRun) {
538+
metricFile.deleteSync();
539+
}
512540

513541
if (outputChecker != null) {
514542
final String? message = outputChecker(result);
@@ -619,27 +647,33 @@ List<T> selectIndexOfTotalSubshard<T>(List<T> tests, {String subshardKey = kSubs
619647
}
620648

621649
Future<void> _runFromList(Map<String, ShardRunner> items, String key, String name, int positionInTaskName) async {
622-
String? item = Platform.environment[key];
623-
if (item == null && Platform.environment.containsKey(CIRRUS_TASK_NAME)) {
624-
final List<String> parts = Platform.environment[CIRRUS_TASK_NAME]!.split('-');
625-
assert(positionInTaskName < parts.length);
626-
item = parts[positionInTaskName];
627-
}
628-
if (item == null) {
629-
for (final String currentItem in items.keys) {
630-
printProgress('$bold$key=$currentItem$reset');
631-
await items[currentItem]!();
650+
try {
651+
String? item = Platform.environment[key];
652+
if (item == null && Platform.environment.containsKey(CIRRUS_TASK_NAME)) {
653+
final List<String> parts = Platform.environment[CIRRUS_TASK_NAME]!.split('-');
654+
assert(positionInTaskName < parts.length);
655+
item = parts[positionInTaskName];
632656
}
633-
} else {
634-
printProgress('$bold$key=$item$reset');
635-
if (!items.containsKey(item)) {
636-
foundError(<String>[
637-
'${red}Invalid $name: $item$reset',
638-
'The available ${name}s are: ${items.keys.join(", ")}',
639-
]);
640-
return;
657+
if (item == null) {
658+
for (final String currentItem in items.keys) {
659+
printProgress('$bold$key=$currentItem$reset');
660+
await items[currentItem]!();
661+
}
662+
} else {
663+
printProgress('$bold$key=$item$reset');
664+
if (!items.containsKey(item)) {
665+
foundError(<String>[
666+
'${red}Invalid $name: $item$reset',
667+
'The available ${name}s are: ${items.keys.join(", ")}',
668+
]);
669+
return;
670+
}
671+
await items[item]!();
672+
}
673+
} catch (_) {
674+
if (!dryRun) {
675+
rethrow;
641676
}
642-
await items[item]!();
643677
}
644678
}
645679

0 commit comments

Comments
 (0)