Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1465)

Side by Side Diff: lib/src/runner/engine.dart

Issue 1196413003: Add a LoadSuite class. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library test.runner.engine; 5 library test.runner.engine;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection'; 8 import 'dart:collection';
9 9
10 import 'package:async/async.dart' hide Result; 10 import 'package:async/async.dart' hide Result;
11 import 'package:collection/collection.dart'; 11 import 'package:collection/collection.dart';
12 import 'package:pool/pool.dart'; 12 import 'package:pool/pool.dart';
13 13
14 import '../backend/live_test.dart'; 14 import '../backend/live_test.dart';
15 import '../backend/live_test_controller.dart'; 15 import '../backend/live_test_controller.dart';
16 import '../backend/state.dart'; 16 import '../backend/state.dart';
17 import '../backend/suite.dart'; 17 import '../backend/suite.dart';
18 import '../backend/test.dart'; 18 import '../backend/test.dart';
19 import '../util/delegating_sink.dart'; 19 import '../util/delegating_sink.dart';
20 import 'load_suite.dart';
20 21
21 /// An [Engine] manages a run that encompasses multiple test suites. 22 /// An [Engine] manages a run that encompasses multiple test suites.
22 /// 23 ///
24 /// Test suites are provided by passing them into [suiteSink]. Once all suites
25 /// have been provided, the user should close [suiteSink] to indicate this.
26 /// [run] won't terminate until [suiteSink] is closed. Suites will be run in the
27 /// order they're provided to [suiteSink]. Tests within those suites will
28 /// likewise be run in the order of [Suite.tests].
29 ///
23 /// The current status of every test is visible via [liveTests]. [onTestStarted] 30 /// The current status of every test is visible via [liveTests]. [onTestStarted]
24 /// can also be used to be notified when a test is about to be run. 31 /// can also be used to be notified when a test is about to be run.
25 /// 32 ///
26 /// Suites will be run in the order they're provided to [new Engine]. Tests 33 /// The engine has some special logic for [LoadSuite]s and the tests they
27 /// within those suites will likewise be run in the order of [Suite.tests]. 34 /// contain, referred to as "load tests". Load tests exist to provide visibility
35 /// into the process of loading test files, but as long as that process is
36 /// proceeding normally users usually don't care about it, so the engine only
37 /// surfaces running load tests (that is, includes them in [liveTests] and other
38 /// collections) under specific circumstances.
39 ///
40 /// If only load tests are running, exactly one load test will be in [active]
41 /// and [liveTests]. If this test passes, it will be removed from both [active]
42 /// and [liveTests] and *will not* be added to [passed]. If at any point a load
43 /// test fails, it will be added to [failed] and [liveTests].
44 ///
45 /// Load tests will always be emitted through [onTestStarted] so users can watch
46 /// their event streams once they start running.
28 class Engine { 47 class Engine {
29 /// Whether [run] has been called yet. 48 /// Whether [run] has been called yet.
30 var _runCalled = false; 49 var _runCalled = false;
31 50
32 /// Whether [close] has been called. 51 /// Whether [close] has been called.
33 var _closed = false; 52 var _closed = false;
34 53
35 /// Whether [close] was called before all the tests finished running. 54 /// Whether [close] was called before all the tests finished running.
36 /// 55 ///
37 /// This is `null` if close hasn't been called and the tests are still 56 /// This is `null` if close hasn't been called and the tests are still
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
90 /// The set of tests that have completed and been marked as skipped. 109 /// The set of tests that have completed and been marked as skipped.
91 Set<LiveTest> get skipped => new UnmodifiableSetView(_skipped); 110 Set<LiveTest> get skipped => new UnmodifiableSetView(_skipped);
92 final _skipped = new Set<LiveTest>(); 111 final _skipped = new Set<LiveTest>();
93 112
94 /// The set of tests that have completed and been marked as failing or error. 113 /// The set of tests that have completed and been marked as failing or error.
95 Set<LiveTest> get failed => new UnmodifiableSetView(_failed); 114 Set<LiveTest> get failed => new UnmodifiableSetView(_failed);
96 final _failed = new Set<LiveTest>(); 115 final _failed = new Set<LiveTest>();
97 116
98 /// The tests that are still running, in the order they begain running. 117 /// The tests that are still running, in the order they begain running.
99 List<LiveTest> get active => new UnmodifiableListView(_active); 118 List<LiveTest> get active => new UnmodifiableListView(_active);
100 final _active = new List<LiveTest>(); 119 final _active = new QueueList<LiveTest>();
120
121 /// The tests from [LoadSuite]s that are still running, in the order they
122 /// began running.
123 ///
124 /// This is separate from [active] because load tests aren't always surfaced.
125 final _activeLoadTests = new List<LiveTest>();
101 126
102 /// Creates an [Engine] that will run all tests provided via [suiteSink]. 127 /// Creates an [Engine] that will run all tests provided via [suiteSink].
103 /// 128 ///
104 /// [concurrency] controls how many suites are run at once. 129 /// [concurrency] controls how many suites are run at once.
105 Engine({int concurrency}) 130 Engine({int concurrency})
106 : _pool = new Pool(concurrency == null ? 1 : concurrency) { 131 : _pool = new Pool(concurrency == null ? 1 : concurrency) {
107 _group.future.then((_) { 132 _group.future.then((_) {
108 if (_closedBeforeDone == null) _closedBeforeDone = false; 133 if (_closedBeforeDone == null) _closedBeforeDone = false;
109 }).catchError((_) { 134 }).catchError((_) {
110 // Don't top-level errors. They'll be thrown via [success] anyway. 135 // Don't top-level errors. They'll be thrown via [success] anyway.
(...skipping 17 matching lines...) Expand all
128 /// This returns `true` if all tests succeed, and `false` otherwise. It will 153 /// This returns `true` if all tests succeed, and `false` otherwise. It will
129 /// only return once all tests have finished running and [suiteSink] has been 154 /// only return once all tests have finished running and [suiteSink] has been
130 /// closed. 155 /// closed.
131 Future<bool> run() { 156 Future<bool> run() {
132 if (_runCalled) { 157 if (_runCalled) {
133 throw new StateError("Engine.run() may not be called more than once."); 158 throw new StateError("Engine.run() may not be called more than once.");
134 } 159 }
135 _runCalled = true; 160 _runCalled = true;
136 161
137 _suiteController.stream.listen((suite) { 162 _suiteController.stream.listen((suite) {
163 if (suite is LoadSuite) {
164 _group.add(_addLoadSuite(suite));
165 return;
166 }
167
138 _group.add(_pool.withResource(() { 168 _group.add(_pool.withResource(() {
139 if (_closed) return null; 169 if (_closed) return null;
140 170
141 // TODO(nweiz): Use a real for loop when issue 23394 is fixed. 171 // TODO(nweiz): Use a real for loop when issue 23394 is fixed.
142 return Future.forEach(suite.tests, (test) async { 172 return Future.forEach(suite.tests, (test) async {
143 if (_closed) return; 173 if (_closed) return;
144 174
145 var liveTest = test.metadata.skip 175 var liveTest = test.metadata.skip
146 ? _skippedTest(suite, test) 176 ? _skippedTest(suite, test)
147 : test.load(suite); 177 : test.load(suite);
148 _liveTests.add(liveTest); 178 _liveTests.add(liveTest);
149 _active.add(liveTest); 179 _active.add(liveTest);
150 180
181 // If there were no active non-load tests, the current active test
182 // would have been a load test. In that case, remove it, since now we
183 // have a non-load test to add.
184 if (_active.isNotEmpty && _active.first.suite is LoadSuite) {
185 _liveTests.remove(_active.removeFirst());
186 }
187
151 liveTest.onStateChange.listen((state) { 188 liveTest.onStateChange.listen((state) {
152 if (state.status != Status.complete) return; 189 if (state.status != Status.complete) return;
153 _active.remove(liveTest); 190 _active.remove(liveTest);
154 191
192 // If we're out of non-load tests, surface a load test.
193 if (_active.isEmpty && _activeLoadTests.isNotEmpty) {
194 _active.add(_activeLoadTests.first);
195 _liveTests.add(_activeLoadTests.first);
196 }
197
155 if (state.result != Result.success) { 198 if (state.result != Result.success) {
156 _passed.remove(liveTest); 199 _passed.remove(liveTest);
157 _failed.add(liveTest); 200 _failed.add(liveTest);
158 } else if (liveTest.test.metadata.skip) { 201 } else if (liveTest.test.metadata.skip) {
159 _skipped.add(liveTest); 202 _skipped.add(liveTest);
160 } else { 203 } else {
161 _passed.add(liveTest); 204 _passed.add(liveTest);
162 } 205 }
163 }); 206 });
164 207
(...skipping 16 matching lines...) Expand all
181 LiveTest _skippedTest(Suite suite, Test test) { 224 LiveTest _skippedTest(Suite suite, Test test) {
182 var controller; 225 var controller;
183 controller = new LiveTestController(suite, test, () { 226 controller = new LiveTestController(suite, test, () {
184 controller.setState(const State(Status.running, Result.success)); 227 controller.setState(const State(Status.running, Result.success));
185 controller.setState(const State(Status.complete, Result.success)); 228 controller.setState(const State(Status.complete, Result.success));
186 controller.completer.complete(); 229 controller.completer.complete();
187 }, () {}); 230 }, () {});
188 return controller.liveTest; 231 return controller.liveTest;
189 } 232 }
190 233
234 /// Adds listeners for [suite].
235 ///
236 /// Load suites have specific logic apart from normal test suites.
237 Future _addLoadSuite(LoadSuite suite) async {
238 var liveTest = await suite.tests.single.load(suite);
239
240 _activeLoadTests.add(liveTest);
241
242 // Only surface the load test if there are no other tests currently running.
243 if (_active.isEmpty) {
244 _liveTests.add(liveTest);
245 _active.add(liveTest);
246 }
247
248 liveTest.onStateChange.listen((state) {
249 if (state.status != Status.complete) return;
250 _activeLoadTests.remove(liveTest);
251
252 // Only one load test will be active at any given time, and it will always
253 // be the only active test. Remove it and, if possible, surface another
254 // load test.
255 if (_active.isNotEmpty && _active.first.suite == suite) {
256 _active.remove(liveTest);
257 _liveTests.remove(liveTest);
258
259 if (_activeLoadTests.isNotEmpty) {
260 _active.add(_activeLoadTests.last);
261 _liveTests.add(_activeLoadTests.last);
262 }
263 }
264
265 // Surface the load test if it fails so that the user can see the failure.
266 if (state.result == Result.success) return;
267 _failed.add(liveTest);
268 _liveTests.add(liveTest);
269 });
270
271 // Run the test immediately. We don't want loading to be blocked on suites
272 // that are already running.
273 _onTestStartedController.add(liveTest);
274 await liveTest.run();
275 }
276
191 /// Signals that the caller is done paying attention to test results and the 277 /// Signals that the caller is done paying attention to test results and the
192 /// engine should release any resources it has allocated. 278 /// engine should release any resources it has allocated.
193 /// 279 ///
194 /// Any actively-running tests are also closed. VM tests are allowed to finish 280 /// Any actively-running tests are also closed. VM tests are allowed to finish
195 /// running so that any modifications they've made to the filesystem can be 281 /// running so that any modifications they've made to the filesystem can be
196 /// cleaned up. 282 /// cleaned up.
197 /// 283 ///
198 /// **Note that closing the engine is not the same as closing [suiteSink].** 284 /// **Note that closing the engine is not the same as closing [suiteSink].**
199 /// Closing [suiteSink] indicates that no more input will be provided, closing 285 /// Closing [suiteSink] indicates that no more input will be provided, closing
200 /// the engine indicates that no more output should be emitted. 286 /// the engine indicates that no more output should be emitted.
201 Future close() { 287 Future close() {
202 _closed = true; 288 _closed = true;
203 if (_closedBeforeDone == null) _closedBeforeDone = true; 289 if (_closedBeforeDone == null) _closedBeforeDone = true;
204 _suiteController.close(); 290 _suiteController.close();
205 291
206 return Future.wait(liveTests.map((liveTest) => liveTest.close())); 292 var allLiveTests = liveTests.toSet()..addAll(_activeLoadTests);
293 return Future.wait(allLiveTests.map((liveTest) => liveTest.close()));
207 } 294 }
208 } 295 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698