Commit 4145f5fc authored by Yegor's avatar Yegor Committed by GitHub

allow passing file path as reference to devicelab task (#6877)

parent 473df6d6
...@@ -7,13 +7,15 @@ This package contains the code for test framework and the tests. More generally ...@@ -7,13 +7,15 @@ This package contains the code for test framework and the tests. More generally
the tests are referred to as "tasks" in the API, but since we primarily use it the tests are referred to as "tasks" in the API, but since we primarily use it
for testing, this document refers to them as "tests". for testing, this document refers to them as "tests".
If you have access to Google's internal network, you can see the continuous build results from the master branch at If you have access to Google's internal network, you can see the continuous
<http://go/flutter-dashboard/build.html>. (There is currently no public view of this data, unfortunately.) build results from the master branch at <http://go/flutter-dashboard/build.html>.
(There is currently no public view of this data, unfortunately.)
# Prerequisites # Prerequisites
You must set the `ANDROID_HOME` environment variable to run tests on Android. If you You must set the `ANDROID_HOME` environment variable to run tests on Android. If
have a local build of the Flutter engine, then you have a copy of the Android SDK at `.../engine/src/third_party/android_tools/sdk`. you have a local build of the Flutter engine, then you have a copy of the
Android SDK at `.../engine/src/third_party/android_tools/sdk`.
# Running tests locally # Running tests locally
...@@ -26,10 +28,16 @@ To run a test, use option `-t` (`--task`): ...@@ -26,10 +28,16 @@ To run a test, use option `-t` (`--task`):
```sh ```sh
# from the .../flutter/dev/devicelab directory # from the .../flutter/dev/devicelab directory
dart bin/run.dart -t {NAME_OF_TEST} dart bin/run.dart -t {NAME_OR_PATH_OF_TEST}
``` ```
You can see the test names in the `manifest.yaml` file in this directory or by looking in `bin/tasks/`. Do not include the `.dart` file extension. For example, `dart bin/run.dart -t complex_layout__start_up`. Where `NAME_OR_PATH_OF_TEST` can be either of:
- the _name_ of a task, which you can find in the `manifest.yaml` file in this
directory. Example: `complex_layout__start_up`.
- the path to a Dart _file_ corresponding to a task, which resides in `bin/tasks`.
Tip: most shells support path auto-completion using the Tab key. Example:
`bin/tasks/complex_layout__start_up.dart`.
To run multiple tests, repeat option `-t` (`--task`) multiple times: To run multiple tests, repeat option `-t` (`--task`) multiple times:
...@@ -54,8 +62,8 @@ Currently there are only two stages defined, `devicelab` and `devicelab_ios`. ...@@ -54,8 +62,8 @@ Currently there are only two stages defined, `devicelab` and `devicelab_ios`.
# Reproducing broken builds locally # Reproducing broken builds locally
If a commit caused a test to fail, If a commit caused a test to fail,
[the dashboard](http://go/flutter-dashboard/build.html) (requires access to the Google network, sorry) might look something [the dashboard](http://go/flutter-dashboard/build.html) (requires access to the
like this: Google network, sorry) might look something like this:
![Broken Test](images/broken-test.png) ![Broken Test](images/broken-test.png)
......
...@@ -7,11 +7,14 @@ import 'dart:convert'; ...@@ -7,11 +7,14 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:args/args.dart'; import 'package:args/args.dart';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/manifest.dart'; import 'package:flutter_devicelab/framework/manifest.dart';
import 'package:flutter_devicelab/framework/runner.dart'; import 'package:flutter_devicelab/framework/runner.dart';
import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/framework/utils.dart';
List<String> _taskNames = <String>[];
/// Runs tasks. /// Runs tasks.
/// ///
/// The tasks are chosen depending on the command-line options /// The tasks are chosen depending on the command-line options
...@@ -28,24 +31,23 @@ Future<Null> main(List<String> rawArgs) async { ...@@ -28,24 +31,23 @@ Future<Null> main(List<String> rawArgs) async {
return null; return null;
} }
List<String> taskNames = <String>[]; if (!args.wasParsed('task')) {
if (args.wasParsed('task')) { if (args.wasParsed('stage')) {
taskNames.addAll(args['task']);
} else if (args.wasParsed('stage')) {
String stageName = args['stage']; String stageName = args['stage'];
List<ManifestTask> tasks = loadTaskManifest().tasks; List<ManifestTask> tasks = loadTaskManifest().tasks;
for (ManifestTask task in tasks) { for (ManifestTask task in tasks) {
if (task.stage == stageName) if (task.stage == stageName)
taskNames.add(task.name); _taskNames.add(task.name);
} }
} else if (args.wasParsed('all')) { } else if (args.wasParsed('all')) {
List<ManifestTask> tasks = loadTaskManifest().tasks; List<ManifestTask> tasks = loadTaskManifest().tasks;
for (ManifestTask task in tasks) { for (ManifestTask task in tasks) {
taskNames.add(task.name); _taskNames.add(task.name);
}
} }
} }
if (taskNames.isEmpty) { if (_taskNames.isEmpty) {
stderr.writeln('Failed to find tasks to run based on supplied options.'); stderr.writeln('Failed to find tasks to run based on supplied options.');
exitCode = 1; exitCode = 1;
return null; return null;
...@@ -53,7 +55,7 @@ Future<Null> main(List<String> rawArgs) async { ...@@ -53,7 +55,7 @@ Future<Null> main(List<String> rawArgs) async {
bool silent = args['silent']; bool silent = args['silent'];
for (String taskName in taskNames) { for (String taskName in _taskNames) {
section('Running task "$taskName"'); section('Running task "$taskName"');
Map<String, dynamic> result = await runTask(taskName, silent: silent); Map<String, dynamic> result = await runTask(taskName, silent: silent);
...@@ -73,9 +75,27 @@ final ArgParser _argParser = new ArgParser() ...@@ -73,9 +75,27 @@ final ArgParser _argParser = new ArgParser()
abbr: 't', abbr: 't',
allowMultiple: true, allowMultiple: true,
splitCommas: true, splitCommas: true,
help: 'Name of the task to run. This option may be repeated to ' help: 'Either:\n'
'specify multiple tasks. A task selected by name does not have to be ' ' - the name of a task defined in manifest.yaml. Example: complex_layout__start_up.\n'
'defined in manifest.yaml. It only needs a Dart executable in bin/tasks.', ' - the path to a Dart file corresponding to a task, which resides in bin/tasks. Example: bin/tasks/complex_layout__start_up.dart.\n'
'\n'
'This option may be repeated to specify multiple tasks.',
callback: (List<String> value) {
for (String nameOrPath in value) {
List<String> fragments = path.split(nameOrPath);
bool isDartFile = fragments.last.endsWith('.dart');
if (fragments.length == 1 && !isDartFile) {
// Not a path
_taskNames.add(nameOrPath);
} else if (!isDartFile || fragments.length != 3 || !_listsEqual(<String>['bin', 'tasks'], fragments.take(2).toList())) {
// Unsupported executable location
throw new FormatException('Invalid value for option -t (--task): $nameOrPath');
} else {
_taskNames.add(path.withoutExtension(fragments.last));
}
}
},
) )
..addOption( ..addOption(
'stage', 'stage',
...@@ -106,3 +126,14 @@ final ArgParser _argParser = new ArgParser() ...@@ -106,3 +126,14 @@ final ArgParser _argParser = new ArgParser()
negatable: true, negatable: true,
defaultsTo: false, defaultsTo: false,
); );
bool _listsEqual(List<dynamic> a, List<dynamic> b) {
if (a.length != b.length) return false;
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i])
return false;
}
return true;
}
...@@ -21,23 +21,31 @@ void main() { ...@@ -21,23 +21,31 @@ void main() {
return scriptProcess.exitCode; return scriptProcess.exitCode;
} }
test('Exits with code 0 when succeeds', () async { test('exits with code 0 when succeeds', () async {
expect(await runScript(<String>['smoke_test_success']), 0); expect(await runScript(<String>['smoke_test_success']), 0);
}); });
test('Exits with code 1 when task throws', () async { test('accepts file paths', () async {
expect(await runScript(<String>['bin/tasks/smoke_test_success.dart']), 0);
});
test('rejects invalid file paths', () async {
expect(await runScript(<String>['lib/framework/adb.dart']), 1);
});
test('exits with code 1 when task throws', () async {
expect(await runScript(<String>['smoke_test_throws']), 1); expect(await runScript(<String>['smoke_test_throws']), 1);
}); });
test('Exits with code 1 when fails', () async { test('exits with code 1 when fails', () async {
expect(await runScript(<String>['smoke_test_failure']), 1); expect(await runScript(<String>['smoke_test_failure']), 1);
}); });
test('Exits with code 1 when fails to connect', () async { test('exits with code 1 when fails to connect', () async {
expect(await runScript(<String>['smoke_test_setup_failure']), 1); expect(await runScript(<String>['smoke_test_setup_failure']), 1);
}, skip: true); // https://github.com/flutter/flutter/issues/5901 }, skip: true); // https://github.com/flutter/flutter/issues/5901
test('Exits with code 1 when results are mixed', () async { test('exits with code 1 when results are mixed', () async {
expect( expect(
await runScript(<String>[ await runScript(<String>[
'smoke_test_failure', 'smoke_test_failure',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment