From c4280a6daa660bb29c6ef7b75aa94576b3c3c694 Mon Sep 17 00:00:00 2001 From: poletti-marco <poletti.marco@gmail.com> Date: Thu, 9 May 2019 00:00:30 +0100 Subject: [PATCH] Ensure that the test state transitions to complete before the test is considered complete. (#1028) In some environments the state change events and the "test complete" event are sent over a stream that does not guarantee ordering (e.g. as independent HTTP requests when using package:sse). Without this change, such tests will fail intermittently, when the "test complete" event happens to be delivered before the state change event. --- pkgs/test_api/CHANGELOG.md | 2 ++ .../lib/src/backend/live_test_controller.dart | 33 ++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/pkgs/test_api/CHANGELOG.md b/pkgs/test_api/CHANGELOG.md index 7fa98f1f..0cdb0d00 100644 --- a/pkgs/test_api/CHANGELOG.md +++ b/pkgs/test_api/CHANGELOG.md @@ -2,6 +2,8 @@ * Don't swallow exceptions from callbacks in `expectAsync*`. * Internal cleanup - fix lints. +* Fixed a race condition that caused tests to occasionally fail during + `tearDownAll` with the message `(tearDownAll) - did not complete [E]`. ## 0.2.5 diff --git a/pkgs/test_api/lib/src/backend/live_test_controller.dart b/pkgs/test_api/lib/src/backend/live_test_controller.dart index de8a09fe..43891c27 100644 --- a/pkgs/test_api/lib/src/backend/live_test_controller.dart +++ b/pkgs/test_api/lib/src/backend/live_test_controller.dart @@ -35,11 +35,11 @@ class _LiveTest extends LiveTest { Stream<Message> get onMessage => _controller._onMessageController.stream; - Future get onComplete => _controller.completer.future; + Future<void> get onComplete => _controller.onComplete; - Future run() => _controller._run(); + Future<void> run() => _controller._run(); - Future close() => _controller._close(); + Future<void> close() => _controller._close(); _LiveTest(this._controller); } @@ -101,7 +101,7 @@ class LiveTestController { final _onMessageController = StreamController<Message>.broadcast(sync: true); /// The completer for [LiveTest.onComplete]; - final completer = Completer(); + final completer = Completer<void>(); /// Whether [run] has been called. var _runCalled = false; @@ -109,6 +109,8 @@ class LiveTestController { /// Whether [close] has been called. bool get _isClosed => _onErrorController.isClosed; + final _stateChangedToComplete = Completer<void>(); + /// Creates a new controller for a [LiveTest]. /// /// [test] is the test being run; [suite] is the suite that contains it. @@ -158,6 +160,11 @@ class LiveTestController { _state = newState; _onStateChangeController.add(newState); + + if (newState.status == Status.complete && + !_stateChangedToComplete.isCompleted) { + _stateChangedToComplete.complete(); + } } /// Emits message over [LiveTest.onMessage]. @@ -173,7 +180,7 @@ class LiveTestController { /// A wrapper for [_onRun] that ensures that it follows the guarantees for /// [LiveTest.run]. - Future _run() { + Future<void> _run() { if (_runCalled) { throw StateError("LiveTest.run() may not be called more than once."); } else if (_isClosed) { @@ -186,9 +193,15 @@ class LiveTestController { return liveTest.onComplete; } + /// Returns a future that completes when the test is complete. + /// + /// We also wait for the state to transition to Status.complete. + Future<void> get onComplete => + Future.wait([completer.future, _stateChangedToComplete.future]); + /// A wrapper for [_onClose] that ensures that all controllers are closed. - Future _close() { - if (_isClosed) return completer.future; + Future<void> _close() { + if (_isClosed) return onComplete; _onStateChangeController.close(); _onErrorController.close(); @@ -199,6 +212,10 @@ class LiveTestController { completer.complete(); } - return completer.future; + if (!_stateChangedToComplete.isCompleted) { + _stateChangedToComplete.complete(); + } + + return onComplete; } } -- GitLab