Provide better error messages in integration tests when apps don't launch successfully (#20813)
* Don't keep parsing results once we've had the event we expected
* Report errors if an app.stop event is received while waiting for another event
* Don't throw if we see app.stop event when we've just sent an app.stop request
* Improve debug print to include --start-paused if being used
* Improve wrapping
diff --git a/packages/flutter_tools/test/integration/test_driver.dart b/packages/flutter_tools/test/integration/test_driver.dart
index ee9a76d..078d3fa 100644
--- a/packages/flutter_tools/test/integration/test_driver.dart
+++ b/packages/flutter_tools/test/integration/test_driver.dart
@@ -76,13 +76,15 @@
Future<void> _setupProcess(List<String> args, {bool withDebugger = false, bool pauseOnExceptions = false}) async {
final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter');
- _debugPrint('Spawning flutter $args in ${_projectFolder.path}');
+ final List<String> flutterArgs = withDebugger
+ ? args.followedBy(<String>['--start-paused']).toList()
+ : args;
+ _debugPrint('Spawning flutter $flutterArgs in ${_projectFolder.path}');
const ProcessManager _processManager = LocalProcessManager();
_proc = await _processManager.start(
<String>[flutterBin]
- .followedBy(args)
- .followedBy(withDebugger ? <String>['--start-paused'] : <String>[])
+ .followedBy(flutterArgs)
.toList(),
workingDirectory: _projectFolder.path,
environment: <String, String>{'FLUTTER_TEST': 'true'});
@@ -263,16 +265,35 @@
return script.sourceLocation(frame.location.token);
}
- Future<Map<String, dynamic>> _waitFor({String event, int id, Duration timeout}) async {
+ Future<Map<String, dynamic>> _waitFor({
+ String event,
+ int id,
+ Duration timeout,
+ bool ignoreAppStopEvent = false,
+ }) async {
final Completer<Map<String, dynamic>> response = new Completer<Map<String, dynamic>>();
- final StreamSubscription<String> sub = _stdout.stream.listen((String line) {
+ StreamSubscription<String> sub;
+ sub = _stdout.stream.listen((String line) async {
final dynamic json = _parseFlutterResponse(line);
if (json == null) {
return;
} else if (
(event != null && json['event'] == event)
|| (id != null && json['id'] == id)) {
+ await sub.cancel();
response.complete(json);
+ } else if (!ignoreAppStopEvent && json['event'] == 'app.stop') {
+ await sub.cancel();
+ final StringBuffer error = new StringBuffer();
+ error.write('Received app.stop event while waiting for ');
+ error.write('${event != null ? '$event event' : 'response to request $id.'}.\n\n');
+ if (json['params'] != null && json['params']['error'] != null) {
+ error.write('${json['params']['error']}\n\n');
+ }
+ if (json['params'] != null && json['params']['trace'] != null) {
+ error.write('${json['params']['trace']}\n\n');
+ }
+ response.completeError(error.toString());
}
});
@@ -327,8 +348,12 @@
_debugPrint(jsonEncoded);
// Set up the response future before we send the request to avoid any
- // races.
- final Future<Map<String, dynamic>> responseFuture = _waitFor(id: requestId);
+ // races. If the method we're calling is app.stop then we tell waitFor not
+ // to throw if it sees an app.stop event before the response to this request.
+ final Future<Map<String, dynamic>> responseFuture = _waitFor(
+ id: requestId,
+ ignoreAppStopEvent: method == 'app.stop',
+ );
_proc.stdin.writeln(jsonEncoded);
final Map<String, dynamic> response = await responseFuture;