Implement flutter test -j (#20493)

This commit is contained in:
Ian Hickson 2018-08-15 12:22:30 -07:00 committed by GitHub
parent d3e482eca3
commit 98c117bb38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 33 deletions

View file

@ -3,12 +3,14 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:math' as math;
import 'package:args/args.dart'; import 'package:args/args.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/context_runner.dart'; import 'package:flutter_tools/src/context_runner.dart';
import 'package:flutter_tools/src/dart/package_map.dart'; import 'package:flutter_tools/src/dart/package_map.dart';
@ -121,6 +123,7 @@ Future<Null> run(List<String> args) async {
enableObservatory: collector != null, enableObservatory: collector != null,
previewDart2: true, previewDart2: true,
precompiledDillPath: dillFile.path, precompiledDillPath: dillFile.path,
concurrency: math.max(1, platform.numberOfProcessors - 2),
); );
if (collector != null) { if (collector != null) {

View file

@ -3,9 +3,11 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:math' as math;
import '../base/common.dart'; import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/platform.dart';
import '../cache.dart'; import '../cache.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import '../test/coverage_collector.dart'; import '../test/coverage_collector.dart';
@ -77,7 +79,12 @@ class TestCommand extends FlutterCommand {
negatable: false, negatable: false,
help: 'Whether matchesGoldenFile() calls within your test methods should\n' help: 'Whether matchesGoldenFile() calls within your test methods should\n'
'update the golden files rather than test for an existing match.', 'update the golden files rather than test for an existing match.',
); )
..addOption('concurrency',
abbr: 'j',
defaultsTo: math.max<int>(1, platform.numberOfProcessors - 2).toString(),
help: 'The number of concurrent test processes to run.',
valueHelp: 'jobs');
} }
@override @override
@ -91,10 +98,10 @@ class TestCommand extends FlutterCommand {
await super.validateCommand(); await super.validateCommand();
if (!fs.isFileSync('pubspec.yaml')) { if (!fs.isFileSync('pubspec.yaml')) {
throwToolExit( throwToolExit(
'Error: No pubspec.yaml file found in the current working directory.\n' 'Error: No pubspec.yaml file found in the current working directory.\n'
'Run this command from the root of your project. Test files must be\n' 'Run this command from the root of your project. Test files must be\n'
'called *_test.dart and must reside in the package\'s \'test\'\n' 'called *_test.dart and must reside in the package\'s \'test\'\n'
'directory (or one of its subdirectories).'); 'directory (or one of its subdirectories).');
} }
} }
@ -108,8 +115,16 @@ class TestCommand extends FlutterCommand {
final bool startPaused = argResults['start-paused']; final bool startPaused = argResults['start-paused'];
if (startPaused && files.length != 1) { if (startPaused && files.length != 1) {
throwToolExit( throwToolExit(
'When using --start-paused, you must specify a single test file to run.', 'When using --start-paused, you must specify a single test file to run.',
exitCode: 1); exitCode: 1,
);
}
final int jobs = int.tryParse(argResults['concurrency']);
if (jobs == null || jobs <= 0 || !jobs.isFinite) {
throwToolExit(
'Could not parse -j/--concurrency argument. It must be an integer greater than zero.'
);
} }
Directory workDir; Directory workDir;
@ -123,7 +138,7 @@ class TestCommand extends FlutterCommand {
if (files.isEmpty) { if (files.isEmpty) {
throwToolExit( throwToolExit(
'Test directory "${workDir.path}" does not appear to contain any test files.\n' 'Test directory "${workDir.path}" does not appear to contain any test files.\n'
'Test files must be in that directory and end with the pattern "_test.dart".' 'Test files must be in that directory and end with the pattern "_test.dart".'
); );
} }
} }
@ -135,8 +150,7 @@ class TestCommand extends FlutterCommand {
final bool machine = argResults['machine']; final bool machine = argResults['machine'];
if (collector != null && machine) { if (collector != null && machine) {
throwToolExit( throwToolExit("The test command doesn't support --machine and coverage together");
"The test command doesn't support --machine and coverage together");
} }
TestWatcher watcher; TestWatcher watcher;
@ -161,6 +175,7 @@ class TestCommand extends FlutterCommand {
previewDart2: argResults['preview-dart-2'], previewDart2: argResults['preview-dart-2'],
trackWidgetCreation: argResults['track-widget-creation'], trackWidgetCreation: argResults['track-widget-creation'],
updateGoldens: argResults['update-goldens'], updateGoldens: argResults['update-goldens'],
concurrency: jobs,
); );
if (collector != null) { if (collector != null) {

View file

@ -5,8 +5,8 @@
import 'dart:async'; import 'dart:async';
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
// ignore: implementation_imports import 'package:meta/meta.dart';
import 'package:test/src/executable.dart' as test; import 'package:test/src/executable.dart' as test; // ignore: implementation_imports
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/common.dart'; import '../base/common.dart';
@ -21,20 +21,21 @@ import 'watcher.dart';
/// Runs tests using package:test and the Flutter engine. /// Runs tests using package:test and the Flutter engine.
Future<int> runTests( Future<int> runTests(
List<String> testFiles, { List<String> testFiles, {
Directory workDir, Directory workDir,
List<String> names = const <String>[], List<String> names = const <String>[],
List<String> plainNames = const <String>[], List<String> plainNames = const <String>[],
bool enableObservatory = false, bool enableObservatory = false,
bool startPaused = false, bool startPaused = false,
bool ipv6 = false, bool ipv6 = false,
bool machine = false, bool machine = false,
bool previewDart2 = false, bool previewDart2 = false,
String precompiledDillPath, String precompiledDillPath,
bool trackWidgetCreation = false, bool trackWidgetCreation = false,
bool updateGoldens = false, bool updateGoldens = false,
TestWatcher watcher, TestWatcher watcher,
}) async { @required int concurrency,
}) async {
if (trackWidgetCreation && !previewDart2) { if (trackWidgetCreation && !previewDart2) {
throw new UsageException( throw new UsageException(
'--track-widget-creation is valid only when --preview-dart-2 is specified.', '--track-widget-creation is valid only when --preview-dart-2 is specified.',
@ -54,13 +55,7 @@ Future<int> runTests(
testArgs.addAll(<String>['-r', 'compact']); testArgs.addAll(<String>['-r', 'compact']);
} }
if (enableObservatory) { // (In particular, for collecting code coverage.) testArgs.add('--concurrency=$concurrency');
// Turn on concurrency, but just barely. This is a trade-off between running
// too many tests such that they all time out, and too few tests such that
// the tests overall take too much time. The current number is empirically
// based on what our infrastructure can handle, which isn't ideal...
testArgs.add('--concurrency=2');
}
for (String name in names) { for (String name in names) {
testArgs..add('--name')..add(name); testArgs..add('--name')..add(name);