mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add --dry-run
to dev/bots/test.dart
. (#158956)
Closes https://github.com/flutter/flutter/issues/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 ```
This commit is contained in:
parent
f2446f2dfb
commit
bf36437cae
@ -162,6 +162,15 @@ Future<CommandResult> runCommand(String executable, List<String> arguments, {
|
|||||||
}) async {
|
}) async {
|
||||||
final String commandDescription = '${path.relative(executable, from: workingDirectory)} ${arguments.join(' ')}';
|
final String commandDescription = '${path.relative(executable, from: workingDirectory)} ${arguments.join(' ')}';
|
||||||
final String relativeWorkingDir = workingDirectory ?? path.relative(io.Directory.current.path);
|
final String relativeWorkingDir = workingDirectory ?? path.relative(io.Directory.current.path);
|
||||||
|
if (dryRun) {
|
||||||
|
printProgress(_prettyPrintRunCommand(executable, arguments, workingDirectory));
|
||||||
|
return CommandResult._(
|
||||||
|
0,
|
||||||
|
Duration.zero,
|
||||||
|
'$executable ${arguments.join(' ')}',
|
||||||
|
'Simulated execution due to --dry-run',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final Command command = await startCommand(executable, arguments,
|
final Command command = await startCommand(executable, arguments,
|
||||||
workingDirectory: workingDirectory,
|
workingDirectory: workingDirectory,
|
||||||
@ -205,6 +214,23 @@ Future<CommandResult> runCommand(String executable, List<String> arguments, {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String _flutterRoot = path.dirname(path.dirname(path.dirname(path.fromUri(io.Platform.script))));
|
||||||
|
|
||||||
|
String _prettyPrintRunCommand(String executable, List<String> arguments, String? workingDirectory) {
|
||||||
|
final StringBuffer output = StringBuffer();
|
||||||
|
|
||||||
|
// Print CWD relative to the root.
|
||||||
|
output.write('|> ');
|
||||||
|
output.write(path.relative(executable, from: _flutterRoot));
|
||||||
|
if (workingDirectory != null) {
|
||||||
|
output.write(' (${path.relative(workingDirectory, from: _flutterRoot)})');
|
||||||
|
}
|
||||||
|
output.writeln(': ');
|
||||||
|
output.writeAll(arguments.map((String a) => ' $a'), '\n');
|
||||||
|
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/// Specifies what to do with the command output from [runCommand] and [startCommand].
|
/// Specifies what to do with the command output from [runCommand] and [startCommand].
|
||||||
enum OutputMode {
|
enum OutputMode {
|
||||||
/// Forwards standard output and standard error streams to the test process'
|
/// Forwards standard output and standard error streams to the test process'
|
||||||
|
@ -708,7 +708,9 @@ class WebTestsSuite {
|
|||||||
// metriciFile is a transitional file that needs to be deleted once it is parsed.
|
// metriciFile is a transitional file that needs to be deleted once it is parsed.
|
||||||
// TODO(godofredoc): Ensure metricFile is parsed and aggregated before deleting.
|
// TODO(godofredoc): Ensure metricFile is parsed and aggregated before deleting.
|
||||||
// https://github.com/flutter/flutter/issues/146003
|
// https://github.com/flutter/flutter/issues/146003
|
||||||
metricFile.deleteSync();
|
if (!dryRun) {
|
||||||
|
metricFile.deleteSync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The `chromedriver` process created by this test.
|
// The `chromedriver` process created by this test.
|
||||||
|
@ -4,14 +4,12 @@
|
|||||||
|
|
||||||
// Runs the tests for the flutter/flutter repository.
|
// Runs the tests for the flutter/flutter repository.
|
||||||
//
|
//
|
||||||
//
|
|
||||||
// By default, test output is filtered and only errors are shown. (If a
|
// By default, test output is filtered and only errors are shown. (If a
|
||||||
// particular test takes longer than _quietTimeout in utils.dart, the output is
|
// particular test takes longer than _quietTimeout in utils.dart, the output is
|
||||||
// shown then also, in case something has hung.)
|
// shown then also, in case something has hung.)
|
||||||
//
|
//
|
||||||
// --verbose stops the output cleanup and just outputs everything verbatim.
|
// --verbose stops the output cleanup and just outputs everything verbatim.
|
||||||
//
|
//
|
||||||
//
|
|
||||||
// By default, errors are non-fatal; all tests are executed and the output
|
// By default, errors are non-fatal; all tests are executed and the output
|
||||||
// ends with a summary of the errors that were detected.
|
// ends with a summary of the errors that were detected.
|
||||||
//
|
//
|
||||||
@ -19,10 +17,10 @@
|
|||||||
//
|
//
|
||||||
// --abort-on-error causes the script to exit immediately when hitting an error.
|
// --abort-on-error causes the script to exit immediately when hitting an error.
|
||||||
//
|
//
|
||||||
//
|
|
||||||
// By default, all tests are run. However, the tests support being split by
|
// By default, all tests are run. However, the tests support being split by
|
||||||
// shard and subshard. (Inspect the code to see what shards and subshards are
|
// shard and subshard. Inspect the code to see what shards and subshards are
|
||||||
// supported.)
|
// supported, or run with `--dry-run` to get a list of tests that _would_ have
|
||||||
|
// been executed.
|
||||||
//
|
//
|
||||||
// If the CIRRUS_TASK_NAME environment variable exists, it is used to determine
|
// If the CIRRUS_TASK_NAME environment variable exists, it is used to determine
|
||||||
// the shard and sub-shard, by parsing it in the form shard-subshard-platform,
|
// the shard and sub-shard, by parsing it in the form shard-subshard-platform,
|
||||||
@ -43,7 +41,6 @@
|
|||||||
//
|
//
|
||||||
// --test-randomize-ordering-seed=<n> sets the shuffle seed for reproducing runs.
|
// --test-randomize-ordering-seed=<n> sets the shuffle seed for reproducing runs.
|
||||||
//
|
//
|
||||||
//
|
|
||||||
// All other arguments are treated as arguments to pass to the flutter tool when
|
// All other arguments are treated as arguments to pass to the flutter tool when
|
||||||
// running tests.
|
// running tests.
|
||||||
|
|
||||||
@ -96,6 +93,7 @@ const String CIRRUS_TASK_NAME = 'CIRRUS_TASK_NAME';
|
|||||||
Future<void> main(List<String> args) async {
|
Future<void> main(List<String> args) async {
|
||||||
try {
|
try {
|
||||||
printProgress('STARTING ANALYSIS');
|
printProgress('STARTING ANALYSIS');
|
||||||
|
bool dryRunArgSet = false;
|
||||||
for (final String arg in args) {
|
for (final String arg in args) {
|
||||||
if (arg.startsWith('--local-engine=')) {
|
if (arg.startsWith('--local-engine=')) {
|
||||||
localEngineEnv['FLUTTER_LOCAL_ENGINE'] = arg.substring('--local-engine='.length);
|
localEngineEnv['FLUTTER_LOCAL_ENGINE'] = arg.substring('--local-engine='.length);
|
||||||
@ -116,10 +114,16 @@ Future<void> main(List<String> args) async {
|
|||||||
onError = () {
|
onError = () {
|
||||||
system.exit(1);
|
system.exit(1);
|
||||||
};
|
};
|
||||||
|
} else if (arg == '--dry-run') {
|
||||||
|
dryRunArgSet = true;
|
||||||
|
printProgress('--dry-run enabled. Tests will not actually be executed.');
|
||||||
} else {
|
} else {
|
||||||
flutterTestArgs.add(arg);
|
flutterTestArgs.add(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (dryRunArgSet) {
|
||||||
|
enableDryRun();
|
||||||
|
}
|
||||||
if (Platform.environment.containsKey(CIRRUS_TASK_NAME)) {
|
if (Platform.environment.containsKey(CIRRUS_TASK_NAME)) {
|
||||||
printProgress('Running task: ${Platform.environment[CIRRUS_TASK_NAME]}');
|
printProgress('Running task: ${Platform.environment[CIRRUS_TASK_NAME]}');
|
||||||
}
|
}
|
||||||
@ -541,6 +545,9 @@ Future<void> _flutterBuild(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _allTargetsCached(File performanceFile) {
|
bool _allTargetsCached(File performanceFile) {
|
||||||
|
if (dryRun) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
final Map<String, Object?> data = json.decode(performanceFile.readAsStringSync())
|
final Map<String, Object?> data = json.decode(performanceFile.readAsStringSync())
|
||||||
as Map<String, Object?>;
|
as Map<String, Object?>;
|
||||||
final List<Map<String, Object?>> targets = (data['targets']! as List<Object?>)
|
final List<Map<String, Object?>> targets = (data['targets']! as List<Object?>)
|
||||||
|
@ -154,6 +154,15 @@ void main() {
|
|||||||
expectExitCode(result, 255);
|
expectExitCode(result, 255);
|
||||||
expect(result.stdout, contains('Invalid subshard name'));
|
expect(result.stdout, contains('Invalid subshard name'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('--dry-run prints every test that would run', () async {
|
||||||
|
final ProcessResult result = await runScript(
|
||||||
|
<String, String> {},
|
||||||
|
<String>['--dry-run'],
|
||||||
|
);
|
||||||
|
expectExitCode(result, 0);
|
||||||
|
expect(result.stdout, contains('|> bin/flutter'));
|
||||||
|
}, testOn: 'posix');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('selectTestsForSubShard distributes tests amongst subshards correctly', () async {
|
test('selectTestsForSubShard distributes tests amongst subshards correctly', () async {
|
||||||
|
@ -68,9 +68,27 @@ const String CIRRUS_TASK_NAME = 'CIRRUS_TASK_NAME';
|
|||||||
final Map<String,String> localEngineEnv = <String, String>{};
|
final Map<String,String> localEngineEnv = <String, String>{};
|
||||||
|
|
||||||
/// The arguments to pass to `flutter test` (typically the local engine
|
/// The arguments to pass to `flutter test` (typically the local engine
|
||||||
/// configuration) -- prefilled with the arguments passed to test.dart.
|
/// configuration) -- prefilled with the arguments passed to test.dart.
|
||||||
final List<String> flutterTestArgs = <String>[];
|
final List<String> flutterTestArgs = <String>[];
|
||||||
|
|
||||||
|
/// Whether execution should be simulated for debugging purposes.
|
||||||
|
///
|
||||||
|
/// When `true`, calls to [runCommand] print to [io.stdout] instead of running
|
||||||
|
/// the process. This is useful for determing what an invocation of `test.dart`
|
||||||
|
/// _might_ due if not invoked with `--dry-run`, or otherwise determine what the
|
||||||
|
/// different test shards and sub-shards are configured as.
|
||||||
|
bool get dryRun => _dryRun ?? false;
|
||||||
|
|
||||||
|
/// Switches [dryRun] to `true`.
|
||||||
|
///
|
||||||
|
/// Expected to be called at most once during execution of a process.
|
||||||
|
void enableDryRun() {
|
||||||
|
if (_dryRun != null) {
|
||||||
|
throw StateError('Should only be called at most once');
|
||||||
|
}
|
||||||
|
_dryRun = true;
|
||||||
|
}
|
||||||
|
bool? _dryRun;
|
||||||
|
|
||||||
const int kESC = 0x1B;
|
const int kESC = 0x1B;
|
||||||
const int kOpenSquareBracket = 0x5B;
|
const int kOpenSquareBracket = 0x5B;
|
||||||
@ -135,6 +153,10 @@ final List<String> _pendingLogs = <String>[];
|
|||||||
Timer? _hideTimer; // When this is null, the output is verbose.
|
Timer? _hideTimer; // When this is null, the output is verbose.
|
||||||
|
|
||||||
void foundError(List<String> messages) {
|
void foundError(List<String> messages) {
|
||||||
|
if (dryRun) {
|
||||||
|
printProgress(messages.join('\n'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
assert(messages.isNotEmpty);
|
assert(messages.isNotEmpty);
|
||||||
// Make the error message easy to notice in the logs by
|
// Make the error message easy to notice in the logs by
|
||||||
// wrapping it in a red box.
|
// wrapping it in a red box.
|
||||||
@ -413,6 +435,10 @@ Future<void> runDartTest(String workingDirectory, {
|
|||||||
removeLine: useBuildRunner ? (String line) => line.startsWith('[INFO]') : null,
|
removeLine: useBuildRunner ? (String line) => line.startsWith('[INFO]') : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (dryRun) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final TestFileReporterResults test = TestFileReporterResults.fromFile(metricFile); // --file-reporter name
|
final TestFileReporterResults test = TestFileReporterResults.fromFile(metricFile); // --file-reporter name
|
||||||
final File info = fileSystem.file(path.join(flutterRoot, 'error.log'));
|
final File info = fileSystem.file(path.join(flutterRoot, 'error.log'));
|
||||||
info.writeAsStringSync(json.encode(test.errors));
|
info.writeAsStringSync(json.encode(test.errors));
|
||||||
@ -508,7 +534,9 @@ Future<void> runFlutterTest(String workingDirectory, {
|
|||||||
// metriciFile is a transitional file that needs to be deleted once it is parsed.
|
// metriciFile is a transitional file that needs to be deleted once it is parsed.
|
||||||
// TODO(godofredoc): Ensure metricFile is parsed and aggregated before deleting.
|
// TODO(godofredoc): Ensure metricFile is parsed and aggregated before deleting.
|
||||||
// https://github.com/flutter/flutter/issues/146003
|
// https://github.com/flutter/flutter/issues/146003
|
||||||
metricFile.deleteSync();
|
if (!dryRun) {
|
||||||
|
metricFile.deleteSync();
|
||||||
|
}
|
||||||
|
|
||||||
if (outputChecker != null) {
|
if (outputChecker != null) {
|
||||||
final String? message = outputChecker(result);
|
final String? message = outputChecker(result);
|
||||||
@ -619,27 +647,33 @@ List<T> selectIndexOfTotalSubshard<T>(List<T> tests, {String subshardKey = kSubs
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _runFromList(Map<String, ShardRunner> items, String key, String name, int positionInTaskName) async {
|
Future<void> _runFromList(Map<String, ShardRunner> items, String key, String name, int positionInTaskName) async {
|
||||||
String? item = Platform.environment[key];
|
try {
|
||||||
if (item == null && Platform.environment.containsKey(CIRRUS_TASK_NAME)) {
|
String? item = Platform.environment[key];
|
||||||
final List<String> parts = Platform.environment[CIRRUS_TASK_NAME]!.split('-');
|
if (item == null && Platform.environment.containsKey(CIRRUS_TASK_NAME)) {
|
||||||
assert(positionInTaskName < parts.length);
|
final List<String> parts = Platform.environment[CIRRUS_TASK_NAME]!.split('-');
|
||||||
item = parts[positionInTaskName];
|
assert(positionInTaskName < parts.length);
|
||||||
}
|
item = parts[positionInTaskName];
|
||||||
if (item == null) {
|
|
||||||
for (final String currentItem in items.keys) {
|
|
||||||
printProgress('$bold$key=$currentItem$reset');
|
|
||||||
await items[currentItem]!();
|
|
||||||
}
|
}
|
||||||
} else {
|
if (item == null) {
|
||||||
printProgress('$bold$key=$item$reset');
|
for (final String currentItem in items.keys) {
|
||||||
if (!items.containsKey(item)) {
|
printProgress('$bold$key=$currentItem$reset');
|
||||||
foundError(<String>[
|
await items[currentItem]!();
|
||||||
'${red}Invalid $name: $item$reset',
|
}
|
||||||
'The available ${name}s are: ${items.keys.join(", ")}',
|
} else {
|
||||||
]);
|
printProgress('$bold$key=$item$reset');
|
||||||
return;
|
if (!items.containsKey(item)) {
|
||||||
|
foundError(<String>[
|
||||||
|
'${red}Invalid $name: $item$reset',
|
||||||
|
'The available ${name}s are: ${items.keys.join(", ")}',
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await items[item]!();
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
if (!dryRun) {
|
||||||
|
rethrow;
|
||||||
}
|
}
|
||||||
await items[item]!();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user