add --exit and --match-host-platform defaults to devicelab runner (#37832)
diff --git a/dev/devicelab/README.md b/dev/devicelab/README.md
index 882726d..c3231f5 100644
--- a/dev/devicelab/README.md
+++ b/dev/devicelab/README.md
@@ -122,6 +122,9 @@
../../bin/cache/dart-sdk/bin/dart bin/run.dart -a
```
+This defaults to only running tests supported by your host device's platform
+(`--match-host-platform`) and exiting after the first failure (`--exit`).
+
## Running specific tests
To run a test, use option `-t` (`--task`):
diff --git a/dev/devicelab/bin/run.dart b/dev/devicelab/bin/run.dart
index 2d74927..b9f70e6 100644
--- a/dev/devicelab/bin/run.dart
+++ b/dev/devicelab/bin/run.dart
@@ -32,21 +32,23 @@
}
if (!args.wasParsed('task')) {
- if (args.wasParsed('stage')) {
- final String stageName = args['stage'];
- final List<ManifestTask> tasks = loadTaskManifest().tasks;
- for (ManifestTask task in tasks) {
- if (task.stage == stageName)
- _taskNames.add(task.name);
- }
- } else if (args.wasParsed('all')) {
- final List<ManifestTask> tasks = loadTaskManifest().tasks;
- for (ManifestTask task in tasks) {
- _taskNames.add(task.name);
- }
+ if (args.wasParsed('stage') || args.wasParsed('all')) {
+ addTasks(
+ tasks: loadTaskManifest().tasks,
+ args: args,
+ taskNames: _taskNames,
+ );
}
}
+ if (args.wasParsed('list')) {
+ for (int i = 0; i < _taskNames.length; i++) {
+ print('${(i + 1).toString().padLeft(3)} - ${_taskNames[i]}');
+ }
+ exitCode = 0;
+ return;
+ }
+
if (_taskNames.isEmpty) {
stderr.writeln('Failed to find tasks to run based on supplied options.');
exitCode = 1;
@@ -66,12 +68,39 @@
localEngineSrcPath: localEngineSrcPath,
);
- if (!result['success'])
- exitCode = 1;
-
print('Task result:');
print(const JsonEncoder.withIndent(' ').convert(result));
section('Finished task "$taskName"');
+
+ if (!result['success']) {
+ exitCode = 1;
+ if (args['exit']) {
+ return;
+ }
+ }
+ }
+}
+
+void addTasks({
+ List<ManifestTask> tasks,
+ ArgResults args,
+ List<String> taskNames,
+}) {
+ if (args.wasParsed('continue-from')) {
+ final int index = tasks.indexWhere((ManifestTask task) => task.name == args['continue-from']);
+ if (index == -1) {
+ throw Exception('Invalid task name "${args['continue-from']}"');
+ }
+ tasks.removeRange(0, index);
+ }
+ // Only start skipping if user specified a task to continue from
+ final String stage = args['stage'];
+ for (ManifestTask task in tasks) {
+ final bool isQualifyingStage = stage == null || task.stage == stage;
+ final bool isQualifyingHost = !args['match-host-platform'] || task.isSupportedByHost();
+ if (isQualifyingHost && isQualifyingStage) {
+ taskNames.add(task.name);
+ }
}
}
@@ -103,16 +132,58 @@
}
},
)
- ..addOption(
- 'stage',
- abbr: 's',
- help: 'Name of the stage. Runs all tasks for that stage. '
- 'The tasks and their stages are read from manifest.yaml.',
- )
..addFlag(
'all',
abbr: 'a',
- help: 'Runs all tasks defined in manifest.yaml.',
+ help: 'Runs all tasks defined in manifest.yaml in alphabetical order.',
+ )
+ ..addOption(
+ 'continue-from',
+ abbr: 'c',
+ help: 'With --all or --stage, continue from the given test.',
+ )
+ ..addFlag(
+ 'exit',
+ defaultsTo: true,
+ help: 'Exit on the first test failure.',
+ )
+ ..addOption(
+ 'local-engine',
+ help: 'Name of a build output within the engine out directory, if you\n'
+ 'are building Flutter locally. Use this to select a specific\n'
+ 'version of the engine if you have built multiple engine targets.\n'
+ 'This path is relative to --local-engine-src-path/out.',
+ )
+ ..addFlag(
+ 'list',
+ abbr: 'l',
+ help: 'Don\'t actually run the tasks, but list out the tasks that would\n'
+ 'have been run, in the order they would have run.',
+ )
+ ..addOption(
+ 'local-engine-src-path',
+ help: 'Path to your engine src directory, if you are building Flutter\n'
+ 'locally. Defaults to \$FLUTTER_ENGINE if set, or tries to guess at\n'
+ 'the location based on the value of the --flutter-root option.',
+ )
+ ..addFlag(
+ 'match-host-platform',
+ defaultsTo: true,
+ help: 'Only run tests that match the host platform (e.g. do not run a\n'
+ 'test with a `required_agent_capabilities` value of "mac/android"\n'
+ 'on a windows host). Each test publishes its'
+ '`required_agent_capabilities`\nin the `manifest.yaml` file.',
+ )
+ ..addOption(
+ 'stage',
+ abbr: 's',
+ help: 'Name of the stage. Runs all tasks for that stage. The tasks and\n'
+ 'their stages are read from manifest.yaml.',
+ )
+ ..addFlag(
+ 'silent',
+ negatable: true,
+ defaultsTo: false,
)
..addMultiOption(
'test',
@@ -125,24 +196,6 @@
);
}
},
- )
- ..addFlag(
- 'silent',
- negatable: true,
- defaultsTo: false,
- )
- ..addOption(
- 'local-engine',
- help: 'Name of a build output within the engine out directory, if you are '
- 'building Flutter locally. Use this to select a specific version of '
- 'the engine if you have built multiple engine targets. This path is '
- 'relative to --local-engine-src-path/out.',
- )
- ..addOption(
- 'local-engine-src-path',
- help: 'Path to your engine src directory, if you are building Flutter '
- 'locally. Defaults to \$FLUTTER_ENGINE if set, or tries to guess at '
- 'the location based on the value of the --flutter-root option.',
);
bool _listsEqual(List<dynamic> a, List<dynamic> b) {
diff --git a/dev/devicelab/lib/framework/manifest.dart b/dev/devicelab/lib/framework/manifest.dart
index dc0eca3..9dc5a76 100644
--- a/dev/devicelab/lib/framework/manifest.dart
+++ b/dev/devicelab/lib/framework/manifest.dart
@@ -3,10 +3,14 @@
// found in the LICENSE file.
import 'package:meta/meta.dart';
+import 'package:platform/platform.dart';
import 'package:yaml/yaml.dart';
import 'utils.dart';
+Platform get platform => _platform ??= const LocalPlatform();
+Platform _platform;
+
/// Loads manifest data from `manifest.yaml` file or from [yaml], if present.
Manifest loadTaskManifest([ String yaml ]) {
final dynamic manifestYaml = yaml == null
@@ -52,7 +56,7 @@
final String stage;
/// Capabilities required of the build agent to be able to perform this task.
- final List<dynamic> requiredAgentCapabilities;
+ final List<String> requiredAgentCapabilities;
/// Whether this test is flaky.
///
@@ -61,6 +65,20 @@
/// An optional custom timeout specified in minutes.
final int timeoutInMinutes;
+
+ /// Whether the task is supported by the current host platform
+ bool isSupportedByHost() {
+ final Set<String> supportedHosts = Set<String>.from(
+ requiredAgentCapabilities.map<String>(
+ (String str) => str.split('/')[0]
+ )
+ );
+ String hostPlatform = platform.operatingSystem;
+ if (hostPlatform == 'macos') {
+ hostPlatform = 'mac'; // package:platform uses 'macos' while manifest.yaml uses 'mac'
+ }
+ return supportedHosts.contains(hostPlatform);
+ }
}
/// Thrown when the manifest YAML is not valid.
diff --git a/dev/devicelab/lib/tasks/sample_catalog_generator.dart b/dev/devicelab/lib/tasks/sample_catalog_generator.dart
index 376cc67..53b225d 100644
--- a/dev/devicelab/lib/tasks/sample_catalog_generator.dart
+++ b/dev/devicelab/lib/tasks/sample_catalog_generator.dart
@@ -39,7 +39,7 @@
await saveCatalogScreenshots(
directory: dir('${flutterDirectory.path}/examples/catalog/.generated'),
commit: commit,
- token: authorizationToken,
+ token: authorizationToken, // TODO(fujino): workaround auth token for local runs
prefix: isIosDevice ? 'ios_' : '',
);
});