Add --name and --plain-name to 'flutter test' (#11020)
This adds a way to run only a subset of the tests.
(The new flags do the same thing as 'pub run test'.)
diff --git a/dev/automated_tests/flutter_test/filtering_test.dart b/dev/automated_tests/flutter_test/filtering_test.dart
new file mode 100644
index 0000000..1cf3277
--- /dev/null
+++ b/dev/automated_tests/flutter_test/filtering_test.dart
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:test/test.dart';
+
+void main() {
+ test('included', () {
+ expect(2 + 2, 4);
+ });
+ test('excluded', () {
+ throw "this test should have been filtered out";
+ });
+}
diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart
index 2f2e58e..91de53f 100644
--- a/packages/flutter_tools/lib/src/commands/test.dart
+++ b/packages/flutter_tools/lib/src/commands/test.dart
@@ -22,6 +22,18 @@
class TestCommand extends FlutterCommand {
TestCommand({ bool verboseHelp: false }) {
usesPubOption();
+ argParser.addOption('name',
+ help: 'A regular expression matching substrings of the names of tests to run.',
+ valueHelp: 'regexp',
+ allowMultiple: true,
+ splitCommas: false,
+ );
+ argParser.addOption('plain-name',
+ help: 'A plain-text substring of the names of tests to run.',
+ valueHelp: 'substring',
+ allowMultiple: true,
+ splitCommas: false,
+ );
argParser.addFlag('start-paused',
defaultsTo: false,
negatable: false,
@@ -141,6 +153,8 @@
}
commandValidator();
+ final List<String> names = argResults['name'];
+ final List<String> plainNames = argResults['plain-name'];
Iterable<String> files = argResults.rest.map<String>((String testPath) => fs.path.absolute(testPath)).toList();
@@ -189,6 +203,8 @@
final int result = await runTests(files,
workDir: workDir,
+ names: names,
+ plainNames: plainNames,
watcher: watcher,
enableObservatory: collector != null || startPaused,
startPaused: startPaused,
diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart
index c376313..0d127d8 100644
--- a/packages/flutter_tools/lib/src/test/runner.dart
+++ b/packages/flutter_tools/lib/src/test/runner.dart
@@ -21,6 +21,8 @@
Future<int> runTests(
List<String> testFiles, {
Directory workDir,
+ List<String> names: const <String>[],
+ List<String> plainNames: const <String>[],
bool enableObservatory: false,
bool startPaused: false,
bool ipv6: false,
@@ -41,6 +43,14 @@
testArgs.addAll(<String>['-r', 'json']);
}
+ for (String name in names) {
+ testArgs..add("--name")..add(name);
+ }
+
+ for (String plainName in plainNames) {
+ testArgs..add("--plain-name")..add(plainName);
+ }
+
testArgs.add('--');
testArgs.addAll(testFiles);
diff --git a/packages/flutter_tools/test/commands/test_test.dart b/packages/flutter_tools/test/commands/test_test.dart
index bd3c043..450e126 100644
--- a/packages/flutter_tools/test/commands/test_test.dart
+++ b/packages/flutter_tools/test/commands/test_test.dart
@@ -18,63 +18,63 @@
Future<Null> _testExclusionLock;
void main() {
- group('test', () {
+ group('flutter test should', () {
final String automatedTestsDirectory = fs.path.join('..', '..', 'dev', 'automated_tests');
final String flutterTestDirectory = fs.path.join(automatedTestsDirectory, 'flutter_test');
- testUsingContext('Exception handling in test harness', () async {
+ testUsingContext('report nice errors for exceptions thrown within testWidgets()', () async {
Cache.flutterRoot = '../..';
return _testFile('exception_handling', automatedTestsDirectory, flutterTestDirectory);
});
- testUsingContext('TestAsyncUtils guarded function test', () async {
+ testUsingContext('report a nice error when a guarded function was called without await', () async {
Cache.flutterRoot = '../..';
return _testFile('test_async_utils_guarded', automatedTestsDirectory, flutterTestDirectory);
});
- testUsingContext('TestAsyncUtils unguarded function test', () async {
+ testUsingContext('report a nice error when an async function was called without await', () async {
Cache.flutterRoot = '../..';
return _testFile('test_async_utils_unguarded', automatedTestsDirectory, flutterTestDirectory);
});
- testUsingContext('Missing flutter_test dependency', () async {
+ testUsingContext('report a nice error when a pubspec.yaml is missing a flutter_test dependency', () async {
final String missingDependencyTests = fs.path.join('..', '..', 'dev', 'missing_dependency_tests');
Cache.flutterRoot = '../..';
return _testFile('trivial', missingDependencyTests, missingDependencyTests);
});
+
+ testUsingContext('run a test when its name matches a regexp', () async {
+ Cache.flutterRoot = '../..';
+ final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory,
+ extraArgs: const <String>["--name", "inc.*de"]);
+ if (!result.stdout.contains("+1: All tests passed"))
+ fail("unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n");
+ expect(result.exitCode, 0);
+ });
+
+ testUsingContext('run a test when its name contains a string', () async {
+ Cache.flutterRoot = '../..';
+ final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory,
+ extraArgs: const <String>["--plain-name", "include"]);
+ if (!result.stdout.contains("+1: All tests passed"))
+ fail("unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n");
+ expect(result.exitCode, 0);
+ });
+
}, skip: io.Platform.isWindows); // TODO(goderbauer): enable when sky_shell is available
}
Future<Null> _testFile(String testName, String workingDirectory, String testDirectory) async {
- final String fullTestName = fs.path.join(testDirectory, '${testName}_test.dart');
- final File testFile = fs.file(fullTestName);
- expect(testFile.existsSync(), true);
final String fullTestExpectation = fs.path.join(testDirectory, '${testName}_expectation.txt');
final File expectationFile = fs.file(fullTestExpectation);
- expect(expectationFile.existsSync(), true);
+ if (!expectationFile.existsSync())
+ fail("missing expectation file: $expectationFile");
while (_testExclusionLock != null)
await _testExclusionLock;
- ProcessResult exec;
- final Completer<Null> testExclusionCompleter = new Completer<Null>();
- _testExclusionLock = testExclusionCompleter.future;
- try {
- exec = await Process.run(
- fs.path.join(dartSdkPath, 'bin', 'dart'),
- <String>[
- fs.path.absolute(fs.path.join('bin', 'flutter_tools.dart')),
- 'test',
- '--no-color',
- fullTestName,
- ],
- workingDirectory: workingDirectory,
- );
- } finally {
- _testExclusionLock = null;
- testExclusionCompleter.complete();
- }
+ final ProcessResult exec = await _runFlutterTest(testName, workingDirectory, testDirectory);
expect(exec.exitCode, isNonZero);
final List<String> output = exec.stdout.split('\n');
@@ -115,3 +115,34 @@
if (!haveSeenStdErrMarker)
expect(exec.stderr, '');
}
+
+Future<ProcessResult> _runFlutterTest(String testName, String workingDirectory, String testDirectory,
+ {List<String> extraArgs = const <String>[]}) async {
+
+ final String testFilePath = fs.path.join(testDirectory, '${testName}_test.dart');
+ final File testFile = fs.file(testFilePath);
+ if (!testFile.existsSync())
+ fail("missing test file: $testFile");
+
+ final List<String> args = <String>[
+ fs.path.absolute(fs.path.join('bin', 'flutter_tools.dart')),
+ 'test',
+ '--no-color'
+ ]..addAll(extraArgs)..add(testFilePath);
+
+ while (_testExclusionLock != null)
+ await _testExclusionLock;
+
+ final Completer<Null> testExclusionCompleter = new Completer<Null>();
+ _testExclusionLock = testExclusionCompleter.future;
+ try {
+ return await Process.run(
+ fs.path.join(dartSdkPath, 'bin', 'dart'),
+ args,
+ workingDirectory: workingDirectory,
+ );
+ } finally {
+ _testExclusionLock = null;
+ testExclusionCompleter.complete();
+ }
+}