blob: e33bfdea9f36787b1f2c5686f3a42d3ce24f3af5 [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. 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 'package:async/async.dart' hide Result;
import 'package:collection/collection.dart';
import 'package:test_api/src/backend/live_test.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/state.dart'; // ignore: implementation_imports
import 'runner_suite.dart';
import 'live_suite.dart';
/// An implementation of [LiveSuite] that's controlled by a
/// [LiveSuiteController].
class _LiveSuite extends LiveSuite {
final LiveSuiteController _controller;
RunnerSuite get suite => _controller._suite;
bool get isComplete => _controller._isComplete;
Future get onComplete => _controller._onCompleteGroup.future;
bool get isClosed => _controller._onCloseCompleter.isCompleted;
Future get onClose => _controller._onCloseCompleter.future;
Stream<LiveTest> get onTestStarted =>;
Set<LiveTest> get passed => UnmodifiableSetView(_controller._passed);
Set<LiveTest> get skipped => UnmodifiableSetView(_controller._skipped);
Set<LiveTest> get failed => UnmodifiableSetView(_controller._failed);
LiveTest get active => _controller._active;
/// A controller that drives a [LiveSuite].
/// This is a utility class to make it easier for [Engine] to create the
/// [LiveSuite]s exposed by various APIs. The [LiveSuite] is accessible through
/// [LiveSuiteController.liveSuite]. When a live test is run, it should be
/// passed to [reportLiveTest], and once tests are finished being run for this
/// suite, [noMoreLiveTests] should be called. Once the suite should be torn
/// down, [close] should be called.
class LiveSuiteController {
/// The [LiveSuite] controlled by [this].
LiveSuite get liveSuite => _liveSuite;
LiveSuite _liveSuite;
/// The suite that's being run.
final RunnerSuite _suite;
/// The future group that backs [LiveSuite.onComplete].
/// This contains all the futures from tests that are run in this suite.
final _onCompleteGroup = FutureGroup();
/// Whether [_onCompleteGroup]'s future has fired.
var _isComplete = false;
/// The completer that backs [LiveSuite.onClose].
/// This is completed when the live suite is closed.
final _onCloseCompleter = Completer();
/// The controller for [LiveSuite.onTestStarted].
final _onTestStartedController =
StreamController<LiveTest>.broadcast(sync: true);
/// The set that backs [LiveTest.passed].
final _passed = Set<LiveTest>();
/// The set that backs [LiveTest.skipped].
final _skipped = Set<LiveTest>();
/// The set that backs [LiveTest.failed].
final _failed = Set<LiveTest>();
/// The test exposed through [].
LiveTest _active;
/// Creates a controller for a live suite representing running the tests in
/// [suite].
/// Once this is called, the controller assumes responsibility for closing the
/// suite. The caller should call [LiveSuiteController.close] rather than
/// calling [RunnerSuite.close] directly.
LiveSuiteController(this._suite) {
_liveSuite = _LiveSuite(this);
_onCompleteGroup.future.then((_) {
_isComplete = true;
}, onError: (_) {});
/// Reports the status of [liveTest] through [liveSuite].
/// The live test is assumed to be a member of this suite. If [countSuccess]
/// is `true` (the default), the test is put into [passed] if it succeeds.
/// Otherwise, it's removed from [liveTests] entirely.
/// Throws a [StateError] if called after [noMoreLiveTests].
void reportLiveTest(LiveTest liveTest, {bool countSuccess = true}) {
if (_onTestStartedController.isClosed) {
throw StateError("Can't call reportLiveTest() after noMoreTests().");
assert(liveTest.suite == _suite);
assert(_active == null);
_active = liveTest;
liveTest.onStateChange.listen((state) {
if (state.status != Status.complete) return;
_active = null;
if (state.result == Result.skipped) {
} else if (state.result != Result.success) {
} else if (countSuccess) {
// A passing test that was once failing was retried
/// Indicates that all the live tests that are going to be provided for this
/// suite have already been provided.
void noMoreLiveTests() {
/// Closes the underlying suite.
Future close() => _closeMemo.runOnce(() async {
try {
await _suite.close();
} finally {
final _closeMemo = AsyncMemoizer();