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