Unverified Commit d5812085 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Try to resolve an intermitted crash during coverage collection (#20506)

* Try to resolve an intermitted crash during coverage collection

The only theory I can come up with is that maybe the test completes
before we finish processing the standard input, so I made the test
harness wait for the observatory URL before considering whether the
test has finished or not.

Also, some code cleanup while I'm at it, e.g. avoiding using "onFoo"
for the names of methods, avoiding back-to-back switch statements with
the same values, avoiding `_` argument names, and using `?.` instead
of `if (foo != null) foo.`.

* Revert back the signature of _pipeStandardStreamsToConsole

* Also remove the other additions to this method.
parent 09542fe3
...@@ -22,7 +22,7 @@ class CoverageCollector extends TestWatcher { ...@@ -22,7 +22,7 @@ class CoverageCollector extends TestWatcher {
Map<String, dynamic> _globalHitmap; Map<String, dynamic> _globalHitmap;
@override @override
Future<void> onFinishedTest(ProcessEvent event) async { Future<void> handleFinishedTest(ProcessEvent event) async {
printTrace('test ${event.childIndex}: collecting coverage'); printTrace('test ${event.childIndex}: collecting coverage');
await collectCoverage(event.process, event.observatoryUri); await collectCoverage(event.process, event.observatoryUri);
} }
......
...@@ -14,7 +14,7 @@ class EventPrinter extends TestWatcher { ...@@ -14,7 +14,7 @@ class EventPrinter extends TestWatcher {
final StringSink _out; final StringSink _out;
@override @override
void onStartedProcess(ProcessEvent event) { void handleStartedProcess(ProcessEvent event) {
_sendEvent('test.startedProcess', _sendEvent('test.startedProcess',
<String, dynamic>{'observatoryUri': event.observatoryUri.toString()}); <String, dynamic>{'observatoryUri': event.observatoryUri.toString()});
} }
......
...@@ -460,12 +460,14 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -460,12 +460,14 @@ class _FlutterPlatform extends PlatformPlugin {
} }
}); });
final Completer<Null> timeout = new Completer<Null>(); final Completer<void> timeout = new Completer<void>();
final Completer<void> gotProcessObservatoryUri = new Completer<void>();
if (!enableObservatory)
gotProcessObservatoryUri.complete();
// 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) {
...@@ -480,10 +482,9 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -480,10 +482,9 @@ class _FlutterPlatform extends PlatformPlugin {
} else { } else {
printTrace('test $ourTestCount: using observatory uri $detectedUri from pid ${process.pid}'); 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;
gotProcessObservatoryUri.complete();
watcher?.handleStartedProcess(new ProcessEvent(ourTestCount, process, processObservatoryUri));
}, },
startTimeoutTimer: () { startTimeoutTimer: () {
new Future<_InitialResult>.delayed(_kTestStartupTimeout).then((_) => timeout.complete()); new Future<_InitialResult>.delayed(_kTestStartupTimeout).then((_) => timeout.complete());
...@@ -498,9 +499,13 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -498,9 +499,13 @@ class _FlutterPlatform extends PlatformPlugin {
printTrace('test $ourTestCount: awaiting initial result for pid ${process.pid}'); printTrace('test $ourTestCount: awaiting initial result for pid ${process.pid}');
final _InitialResult initialResult = await Future.any(<Future<_InitialResult>>[ final _InitialResult initialResult = await Future.any(<Future<_InitialResult>>[
process.exitCode.then<_InitialResult>((int exitCode) => _InitialResult.crashed), process.exitCode.then<_InitialResult>((int exitCode) => _InitialResult.crashed),
timeout.future.then<_InitialResult>((Null _) => _InitialResult.timedOut), timeout.future.then<_InitialResult>((void value) => _InitialResult.timedOut),
new Future<_InitialResult>.delayed(_kTestProcessTimeout, () => _InitialResult.timedOut), new Future<_InitialResult>.delayed(_kTestProcessTimeout, () => _InitialResult.timedOut),
webSocket.future.then<_InitialResult>((WebSocket webSocket) => _InitialResult.connected), gotProcessObservatoryUri.future.then<_InitialResult>((void value) {
return webSocket.future.then<_InitialResult>(
(WebSocket webSocket) => _InitialResult.connected,
);
}),
]); ]);
switch (initialResult) { switch (initialResult) {
...@@ -514,6 +519,7 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -514,6 +519,7 @@ class _FlutterPlatform extends PlatformPlugin {
controller.sink.close(); // ignore: unawaited_futures controller.sink.close(); // ignore: unawaited_futures
printTrace('test $ourTestCount: waiting for controller sink to close'); printTrace('test $ourTestCount: waiting for controller sink to close');
await controller.sink.done; await controller.sink.done;
await watcher?.handleTestCrashed(new ProcessEvent(ourTestCount, process));
break; break;
case _InitialResult.timedOut: case _InitialResult.timedOut:
// Could happen either if the process takes a long time starting // Could happen either if the process takes a long time starting
...@@ -526,12 +532,13 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -526,12 +532,13 @@ class _FlutterPlatform extends PlatformPlugin {
controller.sink.close(); // ignore: unawaited_futures controller.sink.close(); // ignore: unawaited_futures
printTrace('test $ourTestCount: waiting for controller sink to close'); printTrace('test $ourTestCount: waiting for controller sink to close');
await controller.sink.done; await controller.sink.done;
await watcher?.handleTestTimedOut(new ProcessEvent(ourTestCount, process));
break; break;
case _InitialResult.connected: case _InitialResult.connected:
printTrace('test $ourTestCount: process with pid ${process.pid} connected to test harness'); printTrace('test $ourTestCount: process with pid ${process.pid} connected to test harness');
final WebSocket testSocket = await webSocket.future; final WebSocket testSocket = await webSocket.future;
final Completer<Null> harnessDone = new Completer<Null>(); final Completer<void> harnessDone = new Completer<void>();
final StreamSubscription<dynamic> harnessToTest = controller.stream.listen( final StreamSubscription<dynamic> harnessToTest = controller.stream.listen(
(dynamic event) { testSocket.add(json.encode(event)); }, (dynamic event) { testSocket.add(json.encode(event)); },
onDone: harnessDone.complete, onDone: harnessDone.complete,
...@@ -548,7 +555,7 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -548,7 +555,7 @@ class _FlutterPlatform extends PlatformPlugin {
cancelOnError: true, cancelOnError: true,
); );
final Completer<Null> testDone = new Completer<Null>(); final Completer<void> testDone = new Completer<void>();
final StreamSubscription<dynamic> testToHarness = testSocket.listen( final StreamSubscription<dynamic> testToHarness = testSocket.listen(
(dynamic encodedEvent) { (dynamic encodedEvent) {
assert(encodedEvent is String); // we shouldn't ever get binary messages assert(encodedEvent is String); // we shouldn't ever get binary messages
...@@ -571,11 +578,11 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -571,11 +578,11 @@ class _FlutterPlatform extends PlatformPlugin {
printTrace('test $ourTestCount: awaiting test result for pid ${process.pid}'); printTrace('test $ourTestCount: awaiting test result for pid ${process.pid}');
final _TestResult testResult = await Future.any(<Future<_TestResult>>[ final _TestResult testResult = await Future.any(<Future<_TestResult>>[
process.exitCode.then<_TestResult>((int exitCode) { return _TestResult.crashed; }), process.exitCode.then<_TestResult>((int exitCode) { return _TestResult.crashed; }),
harnessDone.future.then<_TestResult>((Null _) { return _TestResult.harnessBailed; }), harnessDone.future.then<_TestResult>((void value) { return _TestResult.harnessBailed; }),
testDone.future.then<_TestResult>((Null _) { return _TestResult.testBailed; }), testDone.future.then<_TestResult>((void value) { return _TestResult.testBailed; }),
]); ]);
await Future.wait(<Future<Null>>[ await Future.wait(<Future<void>>[
harnessToTest.cancel(), harnessToTest.cancel(),
testToHarness.cancel(), testToHarness.cancel(),
]); ]);
...@@ -593,31 +600,18 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -593,31 +600,18 @@ class _FlutterPlatform extends PlatformPlugin {
await controller.sink.done; await controller.sink.done;
break; break;
case _TestResult.harnessBailed: case _TestResult.harnessBailed:
printTrace('test $ourTestCount: process with pid ${process.pid} no longer needed by test harness');
break;
case _TestResult.testBailed: case _TestResult.testBailed:
printTrace('test $ourTestCount: process with pid ${process.pid} no longer needs test harness'); if (testResult == _TestResult.harnessBailed) {
printTrace('test $ourTestCount: process with pid ${process.pid} no longer needed by test harness');
} else {
assert(testResult == _TestResult.testBailed);
printTrace('test $ourTestCount: process with pid ${process.pid} no longer needs test harness');
}
await watcher?.handleFinishedTest(new ProcessEvent(ourTestCount, process, processObservatoryUri));
break; break;
} }
break; break;
} }
if (watcher != null) {
switch (initialResult) {
case _InitialResult.crashed:
await watcher.onTestCrashed(new ProcessEvent(ourTestCount, process));
break;
case _InitialResult.timedOut:
await watcher.onTestTimedOut(new ProcessEvent(ourTestCount, process));
break;
case _InitialResult.connected:
if (subprocessActive) {
await watcher.onFinishedTest(
new ProcessEvent(ourTestCount, process, processObservatoryUri));
}
break;
}
}
} 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"}');
if (!controllerSinkClosed) { if (!controllerSinkClosed) {
...@@ -830,7 +824,6 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -830,7 +824,6 @@ class _FlutterPlatform extends PlatformPlugin {
void reportObservatoryUri(Uri uri), void reportObservatoryUri(Uri uri),
}) { }) {
const String observatoryString = 'Observatory listening on '; const String observatoryString = 'Observatory listening on ';
for (Stream<List<int>> stream in for (Stream<List<int>> stream in
<Stream<List<int>>>[process.stderr, process.stdout]) { <Stream<List<int>>>[process.stderr, process.stdout]) {
stream.transform(utf8.decoder) stream.transform(utf8.decoder)
...@@ -891,18 +884,18 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -891,18 +884,18 @@ class _FlutterPlatform extends PlatformPlugin {
class _FlutterPlatformStreamSinkWrapper<S> implements StreamSink<S> { class _FlutterPlatformStreamSinkWrapper<S> implements StreamSink<S> {
_FlutterPlatformStreamSinkWrapper(this._parent, this._shellProcessClosed); _FlutterPlatformStreamSinkWrapper(this._parent, this._shellProcessClosed);
final StreamSink<S> _parent; final StreamSink<S> _parent;
final Future<Null> _shellProcessClosed; final Future<void> _shellProcessClosed;
@override @override
Future<Null> get done => _done.future; Future<void> get done => _done.future;
final Completer<Null> _done = new Completer<Null>(); final Completer<void> _done = new Completer<void>();
@override @override
Future<dynamic> close() { Future<dynamic> close() {
Future.wait<dynamic>(<Future<dynamic>>[ Future.wait<dynamic>(<Future<dynamic>>[
_parent.close(), _parent.close(),
_shellProcessClosed, _shellProcessClosed,
]).then<Null>( ]).then<void>(
(List<dynamic> value) { (List<dynamic> value) {
_done.complete(); _done.complete();
}, },
......
...@@ -11,20 +11,20 @@ abstract class TestWatcher { ...@@ -11,20 +11,20 @@ abstract class TestWatcher {
/// ///
/// If startPaused was true, the caller needs to resume in Observatory to /// If startPaused was true, the caller needs to resume in Observatory to
/// start running the tests. /// start running the tests.
void onStartedProcess(ProcessEvent event) {} void handleStartedProcess(ProcessEvent event) {}
/// Called after the tests finish but before the process exits. /// Called after the tests finish but before the process exits.
/// ///
/// The child process won't exit until this method completes. /// The child process won't exit until this method completes.
/// Not called if the process died. /// Not called if the process died.
Future<void> onFinishedTest(ProcessEvent event) async {} Future<void> handleFinishedTest(ProcessEvent event) async {}
/// Called when the test process crashed before connecting to test harness. /// Called when the test process crashed before connecting to test harness.
Future<void> onTestCrashed(ProcessEvent event) async {} Future<void> handleTestCrashed(ProcessEvent event) async {}
/// Called if we timed out waiting for the test process to connect to test /// Called if we timed out waiting for the test process to connect to test
/// harness. /// harness.
Future<void> onTestTimedOut(ProcessEvent event) async {} Future<void> handleTestTimedOut(ProcessEvent event) async {}
} }
/// Describes a child process started during testing. /// Describes a child process started during testing.
...@@ -40,6 +40,6 @@ class ProcessEvent { ...@@ -40,6 +40,6 @@ class ProcessEvent {
final Process process; final Process process;
/// The observatory Uri or null if not debugging. /// The observatory URL or null if not debugging.
final Uri observatoryUri; 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