Commit 3528cd6f authored by Brian Slesinsky's avatar Brian Slesinsky Committed by GitHub

flutter test: add --machine flag (#10520)

Currently this just prints the observatory URL as a JSON event.
Refactored the code to make this fit in.
parent fde985b3
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
<excludeFolder url="file://$MODULE_DIR$/.pub" /> <excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/packages" /> <excludeFolder url="file://$MODULE_DIR$/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/packages" />
</content> </content>
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart Packages" level="project" /> <orderEntry type="library" name="Dart Packages" level="project" />
......
...@@ -88,7 +88,6 @@ Future<Null> run(List<String> args) async { ...@@ -88,7 +88,6 @@ Future<Null> run(List<String> args) async {
} }
loader.installHook( loader.installHook(
shellPath: shellPath, shellPath: shellPath,
debuggerMode: false,
); );
PackageMap.globalPackagesPath = PackageMap.globalPackagesPath =
......
...@@ -7,6 +7,12 @@ ...@@ -7,6 +7,12 @@
<excludeFolder url="file://$MODULE_DIR$/bin/packages" /> <excludeFolder url="file://$MODULE_DIR$/bin/packages" />
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/packages" /> <excludeFolder url="file://$MODULE_DIR$/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/android/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/base/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/commands/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/data/dart_dependencies_test/asci_casing/.pub" />
<excludeFolder url="file://$MODULE_DIR$/test/data/dart_dependencies_test/asci_casing/build" />
<excludeFolder url="file://$MODULE_DIR$/test/data/dart_dependencies_test/asci_casing/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/data/dart_dependencies_test/bad_package/.pub" /> <excludeFolder url="file://$MODULE_DIR$/test/data/dart_dependencies_test/bad_package/.pub" />
<excludeFolder url="file://$MODULE_DIR$/test/data/dart_dependencies_test/bad_package/build" /> <excludeFolder url="file://$MODULE_DIR$/test/data/dart_dependencies_test/bad_package/build" />
<excludeFolder url="file://$MODULE_DIR$/test/data/dart_dependencies_test/bad_package/packages" /> <excludeFolder url="file://$MODULE_DIR$/test/data/dart_dependencies_test/bad_package/packages" />
...@@ -30,16 +36,10 @@ ...@@ -30,16 +36,10 @@
<excludeFolder url="file://$MODULE_DIR$/test/data/intellij/plugins/Dart/packages" /> <excludeFolder url="file://$MODULE_DIR$/test/data/intellij/plugins/Dart/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/data/intellij/plugins/packages" /> <excludeFolder url="file://$MODULE_DIR$/test/data/intellij/plugins/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/data/packages" /> <excludeFolder url="file://$MODULE_DIR$/test/data/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/ios/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/packages" /> <excludeFolder url="file://$MODULE_DIR$/test/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/replay/osx/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/replay/osx/simulator_application_binary/file/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/replay/osx/simulator_application_binary/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/replay/osx/simulator_application_binary/platform/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/replay/osx/simulator_application_binary/process/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/replay/osx/simulator_application_binary/vmservice/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/replay/packages" /> <excludeFolder url="file://$MODULE_DIR$/test/replay/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/src/base/packages" /> <excludeFolder url="file://$MODULE_DIR$/test/runner/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/src/ios/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/src/packages" /> <excludeFolder url="file://$MODULE_DIR$/test/src/packages" />
<excludeFolder url="file://$MODULE_DIR$/tool/packages" /> <excludeFolder url="file://$MODULE_DIR$/tool/packages" />
</content> </content>
......
...@@ -83,7 +83,7 @@ Future<Null> main(List<String> args) async { ...@@ -83,7 +83,7 @@ Future<Null> main(List<String> args) async {
new RunCommand(verboseHelp: verboseHelp), new RunCommand(verboseHelp: verboseHelp),
new ScreenshotCommand(), new ScreenshotCommand(),
new StopCommand(), new StopCommand(),
new TestCommand(), new TestCommand(verboseHelp: verboseHelp),
new TraceCommand(), new TraceCommand(),
new UpdatePackagesCommand(hidden: !verboseHelp), new UpdatePackagesCommand(hidden: !verboseHelp),
new UpgradeCommand(), new UpgradeCommand(),
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
import 'dart:async'; import 'dart:async';
import 'package:test/src/executable.dart' as test; // ignore: implementation_imports
import '../artifacts.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
...@@ -14,16 +11,16 @@ import '../base/logger.dart'; ...@@ -14,16 +11,16 @@ import '../base/logger.dart';
import '../base/os.dart'; import '../base/os.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../base/process_manager.dart'; import '../base/process_manager.dart';
import '../base/terminal.dart';
import '../cache.dart'; import '../cache.dart';
import '../dart/package_map.dart';
import '../globals.dart'; import '../globals.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import '../test/coverage_collector.dart'; import '../test/coverage_collector.dart';
import '../test/flutter_platform.dart' as loader; import '../test/event_printer.dart';
import '../test/runner.dart';
import '../test/watcher.dart';
class TestCommand extends FlutterCommand { class TestCommand extends FlutterCommand {
TestCommand() { TestCommand({ bool verboseHelp: false }) {
usesPubOption(); usesPubOption();
argParser.addFlag('start-paused', argParser.addFlag('start-paused',
defaultsTo: false, defaultsTo: false,
...@@ -53,6 +50,11 @@ class TestCommand extends FlutterCommand { ...@@ -53,6 +50,11 @@ class TestCommand extends FlutterCommand {
defaultsTo: 'coverage/lcov.info', defaultsTo: 'coverage/lcov.info',
help: 'Where to store coverage information (if coverage is enabled).' help: 'Where to store coverage information (if coverage is enabled).'
); );
argParser.addFlag('machine',
hide: !verboseHelp,
negatable: false,
help: 'Handle machine structured JSON command input\n'
'and provide output and progress in machine friendly format.');
commandValidator = () { commandValidator = () {
if (!fs.isFileSync('pubspec.yaml')) { if (!fs.isFileSync('pubspec.yaml')) {
throwToolExit( throwToolExit(
...@@ -70,37 +72,12 @@ class TestCommand extends FlutterCommand { ...@@ -70,37 +72,12 @@ class TestCommand extends FlutterCommand {
@override @override
String get description => 'Run Flutter unit tests for the current project.'; String get description => 'Run Flutter unit tests for the current project.';
Iterable<String> _findTests(Directory directory) {
return directory.listSync(recursive: true, followLinks: false)
.where((FileSystemEntity entity) => entity.path.endsWith('_test.dart') &&
fs.isFileSync(entity.path))
.map((FileSystemEntity entity) => fs.path.absolute(entity.path));
}
Directory get _currentPackageTestDir { Directory get _currentPackageTestDir {
// We don't scan the entire package, only the test/ subdirectory, so that // We don't scan the entire package, only the test/ subdirectory, so that
// files with names like like "hit_test.dart" don't get run. // files with names like like "hit_test.dart" don't get run.
return fs.directory('test'); return fs.directory('test');
} }
Future<int> _runTests(List<String> testArgs, Directory testDirectory) async {
final Directory currentDirectory = fs.currentDirectory;
try {
if (testDirectory != null) {
printTrace('switching to directory $testDirectory to run tests');
PackageMap.globalPackagesPath = fs.path.normalize(fs.path.absolute(PackageMap.globalPackagesPath));
fs.currentDirectory = testDirectory;
}
printTrace('running test package with arguments: $testArgs');
await test.main(testArgs);
// test.main() sets dart:io's exitCode global.
printTrace('test package returned with exit code $exitCode');
return exitCode;
} finally {
fs.currentDirectory = currentDirectory;
}
}
Future<bool> _collectCoverageData(CoverageCollector collector, { bool mergeCoverageData: false }) async { Future<bool> _collectCoverageData(CoverageCollector collector, { bool mergeCoverageData: false }) async {
final Status status = logger.startProgress('Collecting coverage information...'); final Status status = logger.startProgress('Collecting coverage information...');
final String coverageData = await collector.finalizeCoverage( final String coverageData = await collector.finalizeCoverage(
...@@ -161,7 +138,7 @@ class TestCommand extends FlutterCommand { ...@@ -161,7 +138,7 @@ class TestCommand extends FlutterCommand {
} }
@override @override
Future<Null> runCommand() async { Future<FlutterCommandResult> runCommand() async {
if (platform.isWindows) { if (platform.isWindows) {
throwToolExit( throwToolExit(
'The test command is currently not supported on Windows: ' 'The test command is currently not supported on Windows: '
...@@ -169,57 +146,57 @@ class TestCommand extends FlutterCommand { ...@@ -169,57 +146,57 @@ class TestCommand extends FlutterCommand {
); );
} }
final List<String> testArgs = <String>[];
commandValidator(); commandValidator();
if (!terminal.supportsColor) Iterable<String> files = argResults.rest.map<String>((String testPath) => fs.path.absolute(testPath)).toList();
testArgs.addAll(<String>['--no-color', '-rexpanded']);
CoverageCollector collector; final bool startPaused = argResults['start-paused'];
if (argResults['coverage'] || argResults['merge-coverage']) { if (startPaused && files.length != 1) {
collector = new CoverageCollector(); throwToolExit(
testArgs.add('--concurrency=1'); 'When using --start-paused, you must specify a single test file to run.',
exitCode: 1);
} }
testArgs.add('--'); Directory workDir;
if (files.isEmpty) {
Directory testDir; workDir = _currentPackageTestDir;
Iterable<String> files = argResults.rest.map<String>((String testPath) => fs.path.absolute(testPath)).toList(); if (!workDir.existsSync())
if (argResults['start-paused']) { throwToolExit('Test directory "${workDir.path}" not found.');
if (files.length != 1) files = _findTests(workDir);
throwToolExit('When using --start-paused, you must specify a single test file to run.', exitCode: 1);
} else if (files.isEmpty) {
testDir = _currentPackageTestDir;
if (!testDir.existsSync())
throwToolExit('Test directory "${testDir.path}" not found.');
files = _findTests(testDir);
if (files.isEmpty) { if (files.isEmpty) {
throwToolExit( throwToolExit(
'Test directory "${testDir.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".'
); );
} }
} }
testArgs.addAll(files);
CoverageCollector collector;
final InternetAddressType serverType = argResults['ipv6'] if (argResults['coverage'] || argResults['merge-coverage']) {
? InternetAddressType.IP_V6 collector = new CoverageCollector();
: InternetAddressType.IP_V4; }
final String shellPath = artifacts.getArtifactPath(Artifact.flutterTester); final bool wantEvents = argResults['machine'];
if (!fs.isFileSync(shellPath)) if (collector != null && wantEvents) {
throwToolExit('Cannot find Flutter shell at $shellPath'); throwToolExit(
loader.installHook( "The test command doesn't support --machine and coverage together");
shellPath: shellPath, }
collector: collector,
debuggerMode: argResults['start-paused'], TestWatcher watcher;
serverType: serverType, if (collector != null) {
); watcher = collector;
} else if (wantEvents) {
watcher = new EventPrinter();
}
Cache.releaseLockEarly(); Cache.releaseLockEarly();
final int result = await _runTests(testArgs, testDir); final int result = await runTests(files,
workDir: workDir,
watcher: watcher,
enableObservatory: collector != null || startPaused,
startPaused: startPaused,
ipv6: argResults['ipv6']);
if (collector != null) { if (collector != null) {
if (!await _collectCoverageData(collector, mergeCoverageData: argResults['merge-coverage'])) if (!await _collectCoverageData(collector, mergeCoverageData: argResults['merge-coverage']))
...@@ -228,5 +205,13 @@ class TestCommand extends FlutterCommand { ...@@ -228,5 +205,13 @@ class TestCommand extends FlutterCommand {
if (result != 0) if (result != 0)
throwToolExit(null); throwToolExit(null);
return const FlutterCommandResult(ExitStatus.success);
} }
} }
Iterable<String> _findTests(Directory directory) {
return directory.listSync(recursive: true, followLinks: false)
.where((FileSystemEntity entity) => entity.path.endsWith('_test.dart') &&
fs.isFileSync(entity.path))
.map((FileSystemEntity entity) => fs.path.absolute(entity.path));
}
...@@ -11,10 +11,18 @@ import '../base/io.dart'; ...@@ -11,10 +11,18 @@ import '../base/io.dart';
import '../dart/package_map.dart'; import '../dart/package_map.dart';
import '../globals.dart'; import '../globals.dart';
import 'watcher.dart';
/// A class that's used to collect coverage data during tests. /// A class that's used to collect coverage data during tests.
class CoverageCollector { class CoverageCollector extends TestWatcher {
Map<String, dynamic> _globalHitmap; Map<String, dynamic> _globalHitmap;
@override
Future<Null> onFinishedTests(ProcessEvent event) async {
printTrace('test ${event.childIndex}: collecting coverage');
await collectCoverage(event.process, event.observatoryUri);
}
void _addHitmap(Map<String, dynamic> hitmap) { void _addHitmap(Map<String, dynamic> hitmap) {
if (_globalHitmap == null) if (_globalHitmap == null)
_globalHitmap = hitmap; _globalHitmap = hitmap;
......
// 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 'dart:convert' show JSON;
import '../base/io.dart' show stdout;
import 'watcher.dart';
/// Prints JSON events when running a test in --machine mode.
class EventPrinter extends TestWatcher {
EventPrinter({StringSink out}) : this._out = out == null ? stdout: out;
final StringSink _out;
@override
void onStartedProcess(ProcessEvent event) {
_sendEvent("test.startedProcess",
<String, dynamic>{"observatoryUri": event.observatoryUri.toString()});
}
void _sendEvent(String name, [dynamic params]) {
final Map<String, dynamic> map = <String, dynamic>{ 'event': name};
if (params != null) {
map['params'] = params;
}
_send(map);
}
void _send(Map<String, dynamic> command) {
final String encoded = JSON.encode(command, toEncodable: _jsonEncodeObject);
_out.writeln('\n[$encoded]');
}
dynamic _jsonEncodeObject(dynamic object) {
if (object is Uri) {
return object.toString();
}
return object;
}
}
...@@ -19,6 +19,7 @@ import '../base/process_manager.dart'; ...@@ -19,6 +19,7 @@ import '../base/process_manager.dart';
import '../dart/package_map.dart'; import '../dart/package_map.dart';
import '../globals.dart'; import '../globals.dart';
import 'coverage_collector.dart'; import 'coverage_collector.dart';
import 'watcher.dart';
/// The timeout we give the test process to connect to the test harness /// The timeout we give the test process to connect to the test harness
/// once the process has entered its main method. /// once the process has entered its main method.
...@@ -53,17 +54,22 @@ final Map<InternetAddressType, InternetAddress> _kHosts = <InternetAddressType, ...@@ -53,17 +54,22 @@ final Map<InternetAddressType, InternetAddress> _kHosts = <InternetAddressType,
void installHook({ void installHook({
@required String shellPath, @required String shellPath,
CoverageCollector collector, CoverageCollector collector,
bool debuggerMode: false, TestWatcher watcher,
bool enableObservatory: false,
bool startPaused: false,
int observatoryPort, int observatoryPort,
int diagnosticPort, int diagnosticPort,
InternetAddressType serverType: InternetAddressType.IP_V4, InternetAddressType serverType: InternetAddressType.IP_V4,
}) { }) {
if (startPaused || observatoryPort != null || diagnosticPort != null)
assert(enableObservatory);
hack.registerPlatformPlugin( hack.registerPlatformPlugin(
<TestPlatform>[TestPlatform.vm], <TestPlatform>[TestPlatform.vm],
() => new _FlutterPlatform( () => new _FlutterPlatform(
shellPath: shellPath, shellPath: shellPath,
collector: collector, watcher: watcher,
debuggerMode: debuggerMode, enableObservatory: enableObservatory,
startPaused: startPaused,
explicitObservatoryPort: observatoryPort, explicitObservatoryPort: observatoryPort,
explicitDiagnosticPort: diagnosticPort, explicitDiagnosticPort: diagnosticPort,
host: _kHosts[serverType], host: _kHosts[serverType],
...@@ -78,8 +84,9 @@ typedef Future<Null> _Finalizer(); ...@@ -78,8 +84,9 @@ typedef Future<Null> _Finalizer();
class _FlutterPlatform extends PlatformPlugin { class _FlutterPlatform extends PlatformPlugin {
_FlutterPlatform({ _FlutterPlatform({
@required this.shellPath, @required this.shellPath,
this.collector, this.watcher,
this.debuggerMode, this.enableObservatory,
this.startPaused,
this.explicitObservatoryPort, this.explicitObservatoryPort,
this.explicitDiagnosticPort, this.explicitDiagnosticPort,
this.host, this.host,
...@@ -88,8 +95,9 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -88,8 +95,9 @@ class _FlutterPlatform extends PlatformPlugin {
} }
final String shellPath; final String shellPath;
final CoverageCollector collector; final TestWatcher watcher;
final bool debuggerMode; final bool enableObservatory;
final bool startPaused;
final int explicitObservatoryPort; final int explicitObservatoryPort;
final int explicitDiagnosticPort; final int explicitDiagnosticPort;
final InternetAddress host; final InternetAddress host;
...@@ -105,7 +113,7 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -105,7 +113,7 @@ class _FlutterPlatform extends PlatformPlugin {
@override @override
StreamChannel<dynamic> loadChannel(String testPath, TestPlatform platform) { StreamChannel<dynamic> loadChannel(String testPath, TestPlatform platform) {
if (explicitObservatoryPort != null || explicitDiagnosticPort != null || debuggerMode) { if (enableObservatory || explicitDiagnosticPort != null) {
if (_testCount > 0) if (_testCount > 0)
throwToolExit('installHook() was called with an observatory port, a diagnostic port, both, or debugger mode enabled, but then more than one test suite was run.'); throwToolExit('installHook() was called with an observatory port, a diagnostic port, both, or debugger mode enabled, but then more than one test suite was run.');
} }
...@@ -190,8 +198,8 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -190,8 +198,8 @@ class _FlutterPlatform extends PlatformPlugin {
shellPath, shellPath,
listenerFile.path, listenerFile.path,
packages: PackageMap.globalPackagesPath, packages: PackageMap.globalPackagesPath,
enableObservatory: collector != null || debuggerMode, enableObservatory: enableObservatory,
startPaused: debuggerMode, startPaused: startPaused,
observatoryPort: explicitObservatoryPort, observatoryPort: explicitObservatoryPort,
diagnosticPort: explicitDiagnosticPort, diagnosticPort: explicitDiagnosticPort,
); );
...@@ -216,19 +224,23 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -216,19 +224,23 @@ class _FlutterPlatform extends PlatformPlugin {
// Pipe stdout and stderr from the subprocess to our printStatus console. // Pipe stdout and stderr from the subprocess to our printStatus console.
// We also keep track of what observatory port the engine used, if any. // We also keep track of what observatory port the engine used, if any.
Uri processObservatoryUri; Uri processObservatoryUri;
_pipeStandardStreamsToConsole( _pipeStandardStreamsToConsole(
process, process,
reportObservatoryUri: (Uri detectedUri) { reportObservatoryUri: (Uri detectedUri) {
assert(processObservatoryUri == null); assert(processObservatoryUri == null);
assert(explicitObservatoryPort == null || assert(explicitObservatoryPort == null ||
explicitObservatoryPort == detectedUri.port); explicitObservatoryPort == detectedUri.port);
if (debuggerMode) { if (startPaused) {
printStatus('The test process has been started.'); printStatus('The test process has been started.');
printStatus('You can now connect to it using observatory. To connect, load the following Web site in your browser:'); printStatus('You can now connect to it using observatory. To connect, load the following Web site in your browser:');
printStatus(' $detectedUri'); printStatus(' $detectedUri');
printStatus('You should first set appropriate breakpoints, then resume the test in the debugger.'); printStatus('You should first set appropriate breakpoints, then resume the test in the debugger.');
} else { } else {
printTrace('test $ourTestCount: using observatory uri $detectedUri from pid ${process.pid} to collect coverage'); printTrace('test $ourTestCount: using observatory uri $detectedUri from pid ${process.pid}');
}
if (watcher != null) {
watcher.onStartedProcess(new ProcessEvent(ourTestCount, process, detectedUri));
} }
processObservatoryUri = detectedUri; processObservatoryUri = detectedUri;
}, },
...@@ -341,9 +353,9 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -341,9 +353,9 @@ class _FlutterPlatform extends PlatformPlugin {
break; break;
} }
if (subprocessActive && collector != null) { if (subprocessActive && watcher != null) {
printTrace('test $ourTestCount: collecting coverage'); await watcher.onFinishedTests(
await collector.collectCoverage(process, processObservatoryUri); new ProcessEvent(ourTestCount, process, processObservatoryUri));
} }
} catch (error, stack) { } catch (error, stack) {
printTrace('test $ourTestCount: error caught during test; ${controllerSinkClosed ? "reporting to console" : "sending to test framework"}'); printTrace('test $ourTestCount: error caught during test; ${controllerSinkClosed ? "reporting to console" : "sending to test framework"}');
......
// 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 'dart:async';
// ignore: implementation_imports
import 'package:test/src/executable.dart' as test;
import '../artifacts.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/terminal.dart';
import '../dart/package_map.dart';
import '../globals.dart';
import '../test/flutter_platform.dart' as loader;
import 'watcher.dart';
/// Runs tests using package:test and the Flutter engine.
Future<int> runTests(
List<String> testFiles, {
Directory workDir,
bool enableObservatory: false,
bool startPaused: false,
bool ipv6: false,
TestWatcher watcher,
}) async {
// Compute the command-line arguments for package:test.
final List<String> testArgs = <String>[];
if (!terminal.supportsColor)
testArgs.addAll(<String>['--no-color', '-rexpanded']);
if (enableObservatory) {
testArgs.add('--concurrency=1');
}
testArgs.add('--');
testArgs.addAll(testFiles);
// Configure package:test to use the Flutter engine for child processes.
final String shellPath = artifacts.getArtifactPath(Artifact.flutterTester);
if (!fs.isFileSync(shellPath))
throwToolExit('Cannot find Flutter shell at $shellPath');
final InternetAddressType serverType =
ipv6 ? InternetAddressType.IP_V6 : InternetAddressType.IP_V4;
loader.installHook(
shellPath: shellPath,
watcher: watcher,
enableObservatory: enableObservatory,
startPaused: startPaused,
serverType: serverType,
);
// Set the package path used for child processes.
// TODO(skybrian): why is this global? Move to installHook?
PackageMap.globalPackagesPath =
fs.path.normalize(fs.path.absolute(PackageMap.globalPackagesPath));
// Call package:test's main method in the appropriate directory.
final Directory saved = fs.currentDirectory;
try {
if (workDir != null) {
printTrace('switching to directory $workDir to run tests');
fs.currentDirectory = workDir;
}
printTrace('running test package with arguments: $testArgs');
await test.main(testArgs);
// test.main() sets dart:io's exitCode global.
// TODO(skybrian): restore previous value?
printTrace('test package returned with exit code $exitCode');
return exitCode;
} finally {
fs.currentDirectory = saved;
}
}
// 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 'dart:async';
import '../base/io.dart' show Process;
/// Callbacks for reporting progress while running tests.
class TestWatcher {
/// Called after a child process starts.
///
/// If startPaused was true, the caller needs to resume in Observatory to
/// start running the tests.
void onStartedProcess(ProcessEvent event) {}
/// Called after the tests finish but before the process exits.
///
/// The child process won't exit until this method completes.
/// Not called if the process died.
Future<Null> onFinishedTests(ProcessEvent event) async {}
}
/// Describes a child process started during testing.
class ProcessEvent {
ProcessEvent(this.childIndex, this.process, this.observatoryUri);
/// The index assigned when the child process was launched.
///
/// Indexes are assigned consecutively starting from zero.
/// When debugging, there should only be one child process so this will
/// always be zero.
final int childIndex;
final Process process;
/// The observatory Uri or null if not debugging.
final Uri observatoryUri;
}
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