Line data Source code
1 : // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 : // for details. All rights reserved. Use of this source code is governed by a
3 : // BSD-style license that can be found in the LICENSE file.
4 :
5 : import 'dart:async';
6 :
7 : import 'package:async/async.dart' hide Result;
8 : import 'package:collection/collection.dart';
9 :
10 : import '../backend/live_test.dart';
11 : import '../backend/state.dart';
12 : import 'live_suite.dart';
13 : import 'runner_suite.dart';
14 :
15 : /// An implementation of [LiveSuite] that's controlled by a
16 : /// [LiveSuiteController].
17 : class _LiveSuite extends LiveSuite {
18 : final LiveSuiteController _controller;
19 :
20 10 : RunnerSuite get suite => _controller._suite;
21 :
22 0 : bool get isComplete => _controller._isComplete;
23 :
24 0 : Future get onComplete => _controller._onCompleteGroup.future;
25 :
26 0 : bool get isClosed => _controller._onCloseCompleter.isCompleted;
27 :
28 0 : Future get onClose => _controller._onCloseCompleter.future;
29 :
30 : Stream<LiveTest> get onTestStarted =>
31 15 : _controller._onTestStartedController.stream;
32 :
33 15 : Set<LiveTest> get passed => new UnmodifiableSetView(_controller._passed);
34 :
35 15 : Set<LiveTest> get skipped => new UnmodifiableSetView(_controller._skipped);
36 :
37 15 : Set<LiveTest> get failed => new UnmodifiableSetView(_controller._failed);
38 :
39 0 : LiveTest get active => _controller._active;
40 :
41 5 : _LiveSuite(this._controller);
42 : }
43 :
44 : /// A controller that drives a [LiveSuite].
45 : ///
46 : /// This is a utility class to make it easier for [Engine] to create the
47 : /// [LiveSuite]s exposed by various APIs. The [LiveSuite] is accessible through
48 : /// [LiveSuiteController.liveSuite]. When a live test is run, it should be
49 : /// passed to [reportLiveTest], and once tests are finished being run for this
50 : /// suite, [noMoreLiveTests] should be called. Once the suite should be torn
51 : /// down, [close] should be called.
52 : class LiveSuiteController {
53 : /// The [LiveSuite] controlled by [this].
54 5 : LiveSuite get liveSuite => _liveSuite;
55 : LiveSuite _liveSuite;
56 :
57 : /// The suite that's being run.
58 : final RunnerSuite _suite;
59 :
60 : /// The future group that backs [LiveSuite.onComplete].
61 : ///
62 : /// This contains all the futures from tests that are run in this suite.
63 : final _onCompleteGroup = new FutureGroup();
64 :
65 : /// Whether [_onCompleteGroup]'s future has fired.
66 : var _isComplete = false;
67 :
68 : /// The completer that backs [LiveSuite.onClose].
69 : ///
70 : /// This is completed when the live suite is closed.
71 : final _onCloseCompleter = new Completer();
72 :
73 : /// The controller for [LiveSuite.onTestStarted].
74 : final _onTestStartedController =
75 : new StreamController<LiveTest>.broadcast(sync: true);
76 :
77 : /// The set that backs [LiveTest.passed].
78 : final _passed = new Set<LiveTest>();
79 :
80 : /// The set that backs [LiveTest.skipped].
81 : final _skipped = new Set<LiveTest>();
82 :
83 : /// The set that backs [LiveTest.failed].
84 : final _failed = new Set<LiveTest>();
85 :
86 : /// The test exposed through [LiveTest.active].
87 : LiveTest _active;
88 :
89 : /// Creates a controller for a live suite representing running the tests in
90 : /// [suite].
91 : ///
92 : /// Once this is called, the controller assumes responsibility for closing the
93 : /// suite. The caller should call [LiveSuiteController.close] rather than
94 : /// calling [RunnerSuite.close] directly.
95 5 : LiveSuiteController(this._suite) {
96 10 : _liveSuite = new _LiveSuite(this);
97 :
98 15 : _onCompleteGroup.future.then((_) {
99 5 : _isComplete = true;
100 : }, onError: (_) {});
101 : }
102 :
103 : /// Reports the status of [liveTest] through [liveSuite].
104 : ///
105 : /// The live test is assumed to be a member of this suite. If [countSuccess]
106 : /// is `true` (the default), the test is put into [passed] if it succeeds.
107 : /// Otherwise, it's removed from [liveTests] entirely.
108 : ///
109 : /// Throws a [StateError] if called after [noMoreLiveTests].
110 : void reportLiveTest(LiveTest liveTest, {bool countSuccess: true}) {
111 10 : if (_onTestStartedController.isClosed) {
112 0 : throw new StateError("Can't call reportLiveTest() after noMoreTests().");
113 : }
114 :
115 : assert(liveTest.suite == _suite);
116 : assert(_active == null);
117 :
118 5 : _active = liveTest;
119 :
120 10 : liveTest.onStateChange.listen((state) {
121 10 : if (state.status != Status.complete) return;
122 5 : _active = null;
123 :
124 10 : if (state.result == Result.skipped) {
125 0 : _skipped.add(liveTest);
126 10 : } else if (state.result != Result.success) {
127 0 : _passed.remove(liveTest);
128 0 : _failed.add(liveTest);
129 : } else if (countSuccess) {
130 10 : _passed.add(liveTest);
131 : // A passing test that was once failing was retried
132 10 : _failed.remove(liveTest);
133 : }
134 : });
135 :
136 10 : _onTestStartedController.add(liveTest);
137 :
138 15 : _onCompleteGroup.add(liveTest.onComplete);
139 : }
140 :
141 : /// Indicates that all the live tests that are going to be provided for this
142 : /// suite have already been provided.
143 : void noMoreLiveTests() {
144 10 : _onTestStartedController.close();
145 10 : _onCompleteGroup.close();
146 : }
147 :
148 : /// Closes the underlying suite.
149 10 : Future close() => _closeMemo.runOnce(() async {
150 : try {
151 15 : await _suite.close();
152 : } finally {
153 10 : _onCloseCompleter.complete();
154 : }
155 0 : });
156 : final _closeMemo = new AsyncMemoizer();
157 : }
|