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

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

Issue 1887853002: Add a LiveSuite class. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Code review changes Created 4 years, 8 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
« no previous file with comments | « lib/src/backend/invoker.dart ('k') | lib/src/runner/live_suite.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 import 'dart:async'; 5 import 'dart:async';
6 import 'dart:collection'; 6 import 'dart:collection';
7 7
8 import 'package:async/async.dart' hide Result; 8 import 'package:async/async.dart' hide Result;
9 import 'package:collection/collection.dart'; 9 import 'package:collection/collection.dart';
10 import 'package:pool/pool.dart'; 10 import 'package:pool/pool.dart';
11 11
12 import '../backend/group.dart'; 12 import '../backend/group.dart';
13 import '../backend/group_entry.dart'; 13 import '../backend/group_entry.dart';
14 import '../backend/invoker.dart'; 14 import '../backend/invoker.dart';
15 import '../backend/live_test.dart'; 15 import '../backend/live_test.dart';
16 import '../backend/live_test_controller.dart'; 16 import '../backend/live_test_controller.dart';
17 import '../backend/state.dart'; 17 import '../backend/state.dart';
18 import '../backend/test.dart'; 18 import '../backend/test.dart';
19 import '../util/iterable_set.dart';
20 import 'live_suite.dart';
21 import 'live_suite_controller.dart';
19 import 'load_suite.dart'; 22 import 'load_suite.dart';
20 import 'runner_suite.dart'; 23 import 'runner_suite.dart';
21 24
22 /// An [Engine] manages a run that encompasses multiple test suites. 25 /// An [Engine] manages a run that encompasses multiple test suites.
23 /// 26 ///
24 /// Test suites are provided by passing them into [suiteSink]. Once all suites 27 /// Test suites are provided by passing them into [suiteSink]. Once all suites
25 /// have been provided, the user should close [suiteSink] to indicate this. 28 /// 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 29 /// [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 30 /// order they're provided to [suiteSink]. Tests within those suites will
28 /// likewise be run in the order they're declared. 31 /// likewise be run in the order they're declared.
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
95 Sink<RunnerSuite> get suiteSink => new DelegatingSink(_suiteController.sink); 98 Sink<RunnerSuite> get suiteSink => new DelegatingSink(_suiteController.sink);
96 final _suiteController = new StreamController<RunnerSuite>(); 99 final _suiteController = new StreamController<RunnerSuite>();
97 100
98 /// All the [RunnerSuite]s added to [suiteSink] so far. 101 /// All the [RunnerSuite]s added to [suiteSink] so far.
99 /// 102 ///
100 /// Note that if a [LoadSuite] is added, this will only contain that suite, 103 /// Note that if a [LoadSuite] is added, this will only contain that suite,
101 /// not the suite it loads. 104 /// not the suite it loads.
102 Set<RunnerSuite> get addedSuites => new UnmodifiableSetView(_addedSuites); 105 Set<RunnerSuite> get addedSuites => new UnmodifiableSetView(_addedSuites);
103 final _addedSuites = new Set<RunnerSuite>(); 106 final _addedSuites = new Set<RunnerSuite>();
104 107
105 /// A broadcast that emits each [RunnerSuite] as it's added to the engine via 108 /// A broadcast stream that emits each [RunnerSuite] as it's added to the
106 /// [suiteSink]. 109 /// engine via [suiteSink].
107 /// 110 ///
108 /// Note that if a [LoadSuite] is added, this will only return that suite, not 111 /// Note that if a [LoadSuite] is added, this will only return that suite, not
109 /// the suite it loads. 112 /// the suite it loads.
110 /// 113 ///
111 /// This is guaranteed to fire after the suite is added to [addedSuites]. 114 /// This is guaranteed to fire after the suite is added to [addedSuites].
112 Stream<RunnerSuite> get onSuiteAdded => _onSuiteAddedController.stream; 115 Stream<RunnerSuite> get onSuiteAdded => _onSuiteAddedController.stream;
113 final _onSuiteAddedController = new StreamController<RunnerSuite>.broadcast(); 116 final _onSuiteAddedController = new StreamController<RunnerSuite>.broadcast();
114 117
115 /// All the currently-known tests that have run, are running, or will run. 118 /// All the currently-known suites that have run or are running.
119 ///
120 /// These are [LiveSuite]s, representing the in-progress state of each suite
121 /// as its component tests are being run.
122 ///
123 /// Note that unlike [addedSuites], for suites that are loaded using
124 /// [LoadSuite]s, both the [LoadSuite] and the suite it loads will eventually
125 /// be in this set.
126 Set<LiveSuite> get liveSuites => new UnmodifiableSetView(_liveSuites);
127 final _liveSuites = new Set<LiveSuite>();
128
129 /// A broadcast stream that emits each [LiveSuite] as it's loaded.
130 ///
131 /// Note that unlike [onSuiteAdded], for suites that are loaded using
132 /// [LoadSuite]s, both the [LoadSuite] and the suite it loads will eventually
133 /// be emitted by this stream.
134 Stream<LiveSuite> get onSuiteStarted => _onSuiteStartedController.stream;
135 final _onSuiteStartedController = new StreamController<LiveSuite>.broadcast();
136
137 /// All the currently-known tests that have run or are running.
116 /// 138 ///
117 /// These are [LiveTest]s, representing the in-progress state of each test. 139 /// These are [LiveTest]s, representing the in-progress state of each test.
118 /// Tests that have not yet begun running are marked [Status.pending]; tests 140 /// Tests that have not yet begun running are marked [Status.pending]; tests
119 /// that have finished are marked [Status.complete]. 141 /// that have finished are marked [Status.complete].
120 /// 142 ///
121 /// This is guaranteed to contain the same tests as the union of [passed], 143 /// This is guaranteed to contain the same tests as the union of [passed],
122 /// [skipped], [failed], and [active]. 144 /// [skipped], [failed], and [active].
123 /// 145 ///
124 /// [LiveTest.run] must not be called on these tests. 146 /// [LiveTest.run] must not be called on these tests.
125 List<LiveTest> get liveTests => new UnmodifiableListView(_liveTests); 147 Set<LiveTest> get liveTests => new GroupSet.from(
126 final _liveTests = new List<LiveTest>(); 148 [passed, skipped, failed, new IterableSet(active)],
149 disjoint: true);
127 150
128 /// A stream that emits each [LiveTest] as it's about to start running. 151 /// A stream that emits each [LiveTest] as it's about to start running.
129 /// 152 ///
130 /// This is guaranteed to fire before [LiveTest.onStateChange] first fires. 153 /// This is guaranteed to fire before [LiveTest.onStateChange] first fires.
131 Stream<LiveTest> get onTestStarted => _onTestStartedController.stream; 154 Stream<LiveTest> get onTestStarted => _onTestStartedGroup.stream;
132 final _onTestStartedController = new StreamController<LiveTest>.broadcast(); 155 final _onTestStartedGroup = new StreamGroup<LiveTest>.broadcast();
133 156
134 /// The set of tests that have completed and been marked as passing. 157 /// The set of tests that have completed and been marked as passing.
135 Set<LiveTest> get passed => new UnmodifiableSetView(_passed); 158 Set<LiveTest> get passed => _passedGroup.set;
136 final _passed = new Set<LiveTest>(); 159 final _passedGroup = new SetGroup<LiveTest>(disjoint: true);
137 160
138 /// The set of tests that have completed and been marked as skipped. 161 /// The set of tests that have completed and been marked as skipped.
139 Set<LiveTest> get skipped => new UnmodifiableSetView(_skipped); 162 Set<LiveTest> get skipped => _skippedGroup.set;
140 final _skipped = new Set<LiveTest>(); 163 final _skippedGroup = new SetGroup<LiveTest>(disjoint: true);
141 164
142 /// The set of tests that have completed and been marked as failing or error. 165 /// The set of tests that have completed and been marked as failing or error.
143 Set<LiveTest> get failed => new UnmodifiableSetView(_failed); 166 Set<LiveTest> get failed => _failedGroup.set;
144 final _failed = new Set<LiveTest>(); 167 final _failedGroup = new SetGroup<LiveTest>(disjoint: true);
145 168
146 /// The tests that are still running, in the order they begain running. 169 /// The tests that are still running, in the order they begain running.
147 List<LiveTest> get active => new UnmodifiableListView(_active); 170 List<LiveTest> get active => new UnmodifiableListView(_active);
148 final _active = new QueueList<LiveTest>(); 171 final _active = new QueueList<LiveTest>();
149 172
150 /// The set of tests that have been marked for restarting. 173 /// The set of tests that have been marked for restarting.
151 /// 174 ///
152 /// This is always a subset of [active]. Once a test in here has finished 175 /// This is always a subset of [active]. Once a test in here has finished
153 /// running, it's run again. 176 /// running, it's run again.
154 final _restarted = new Set<LiveTest>(); 177 final _restarted = new Set<LiveTest>();
(...skipping 15 matching lines...) Expand all
170 /// 193 ///
171 /// [concurrency] controls how many suites are run at once, and defaults to 1. 194 /// [concurrency] controls how many suites are run at once, and defaults to 1.
172 /// [maxSuites] controls how many suites are *loaded* at once, and defaults to 195 /// [maxSuites] controls how many suites are *loaded* at once, and defaults to
173 /// four times [concurrency]. 196 /// four times [concurrency].
174 Engine({int concurrency, int maxSuites}) 197 Engine({int concurrency, int maxSuites})
175 : _runPool = new Pool(concurrency == null ? 1 : concurrency), 198 : _runPool = new Pool(concurrency == null ? 1 : concurrency),
176 _loadPool = new Pool(maxSuites == null 199 _loadPool = new Pool(maxSuites == null
177 ? (concurrency == null ? 2 : concurrency * 2) 200 ? (concurrency == null ? 2 : concurrency * 2)
178 : maxSuites) { 201 : maxSuites) {
179 _group.future.then((_) { 202 _group.future.then((_) {
203 _onTestStartedGroup.close();
204 _onSuiteStartedController.close();
180 if (_closedBeforeDone == null) _closedBeforeDone = false; 205 if (_closedBeforeDone == null) _closedBeforeDone = false;
181 }).catchError((_) { 206 }).catchError((_) {
182 // Don't top-level errors. They'll be thrown via [success] anyway. 207 // Don't top-level errors. They'll be thrown via [success] anyway.
183 }); 208 });
184 } 209 }
185 210
186 /// Creates an [Engine] that will run all tests in [suites]. 211 /// Creates an [Engine] that will run all tests in [suites].
187 /// 212 ///
188 /// [concurrency] controls how many suites are run at once. An engine 213 /// [concurrency] controls how many suites are run at once. An engine
189 /// constructed this way will automatically close its [suiteSink], meaning 214 /// constructed this way will automatically close its [suiteSink], meaning
(...skipping 16 matching lines...) Expand all
206 } 231 }
207 _runCalled = true; 232 _runCalled = true;
208 233
209 _suiteController.stream.listen((suite) { 234 _suiteController.stream.listen((suite) {
210 _addedSuites.add(suite); 235 _addedSuites.add(suite);
211 _onSuiteAddedController.add(suite); 236 _onSuiteAddedController.add(suite);
212 237
213 _group.add(new Future.sync(() async { 238 _group.add(new Future.sync(() async {
214 var loadResource = await _loadPool.request(); 239 var loadResource = await _loadPool.request();
215 240
241 var controller;
216 if (suite is LoadSuite) { 242 if (suite is LoadSuite) {
217 suite = await _addLoadSuite(suite); 243 controller = await _addLoadSuite(suite);
218 if (suite == null) { 244 if (controller == null) {
219 loadResource.release(); 245 loadResource.release();
220 return; 246 return;
221 } 247 }
248 } else {
249 controller = new LiveSuiteController(suite);
222 } 250 }
223 251
252 _addLiveSuite(controller.liveSuite);
253
224 await _runPool.withResource(() async { 254 await _runPool.withResource(() async {
225 if (_closed) return; 255 if (_closed) return;
226 await _runGroup(suite, suite.group, []); 256 await _runGroup(controller, controller.liveSuite.suite.group, []);
227 loadResource.allowRelease(() => suite.close()); 257 loadResource.allowRelease(() => controller.close());
228 }); 258 });
229 })); 259 }));
230 }, onDone: () { 260 }, onDone: () {
231 _onSuiteAddedController.close(); 261 _onSuiteAddedController.close();
232 _group.close(); 262 _group.close();
233 }); 263 });
234 264
235 return success; 265 return success;
236 } 266 }
237 267
238 /// Runs all the entries in [entries] in sequence. 268 /// Runs all the entries in [group] in sequence.
239 /// 269 ///
270 /// [suiteController] is the controller fo the suite that contains [group].
240 /// [parents] is a list of groups that contain [group]. It may be modified, 271 /// [parents] is a list of groups that contain [group]. It may be modified,
241 /// but it's guaranteed to be in its original state once this function has 272 /// but it's guaranteed to be in its original state once this function has
242 /// finished. 273 /// finished.
243 Future _runGroup(RunnerSuite suite, Group group, List<Group> parents) async { 274 Future _runGroup(LiveSuiteController suiteController, Group group,
275 List<Group> parents) async {
244 parents.add(group); 276 parents.add(group);
245 try { 277 try {
246 if (group.metadata.skip) { 278 if (group.metadata.skip) {
247 await _runLiveTest(_skippedTest(suite, group, parents)); 279 await _runSkippedTest(suiteController, group, parents);
248 return; 280 return;
249 } 281 }
250 282
251 var setUpAllSucceeded = true; 283 var setUpAllSucceeded = true;
252 if (group.setUpAll != null) { 284 if (group.setUpAll != null) {
253 var liveTest = group.setUpAll.load(suite, groups: parents); 285 var liveTest = group.setUpAll.load(suiteController.liveSuite.suite,
254 await _runLiveTest(liveTest, countSuccess: false); 286 groups: parents);
287 await _runLiveTest(suiteController, liveTest, countSuccess: false);
255 setUpAllSucceeded = liveTest.state.result == Result.success; 288 setUpAllSucceeded = liveTest.state.result == Result.success;
256 } 289 }
257 290
258 if (!_closed && setUpAllSucceeded) { 291 if (!_closed && setUpAllSucceeded) {
259 for (var entry in group.entries) { 292 for (var entry in group.entries) {
260 if (_closed) return; 293 if (_closed) return;
261 294
262 if (entry is Group) { 295 if (entry is Group) {
263 await _runGroup(suite, entry, parents); 296 await _runGroup(suiteController, entry, parents);
264 } else if (entry.metadata.skip) { 297 } else if (entry.metadata.skip) {
265 await _runLiveTest(_skippedTest(suite, entry, parents)); 298 await _runSkippedTest(suiteController, entry, parents);
266 } else { 299 } else {
267 var test = entry as Test; 300 var test = entry as Test;
268 await _runLiveTest(test.load(suite, groups: parents)); 301 await _runLiveTest(
302 suiteController,
303 test.load(suiteController.liveSuite.suite, groups: parents));
269 } 304 }
270 } 305 }
271 } 306 }
272 307
273 // Even if we're closed or setUpAll failed, we want to run all the 308 // Even if we're closed or setUpAll failed, we want to run all the
274 // teardowns to ensure that any state is properly cleaned up. 309 // teardowns to ensure that any state is properly cleaned up.
275 if (group.tearDownAll != null) { 310 if (group.tearDownAll != null) {
276 var liveTest = group.tearDownAll.load(suite, groups: parents); 311 var liveTest = group.tearDownAll.load(suiteController.liveSuite.suite,
277 await _runLiveTest(liveTest, countSuccess: false); 312 groups: parents);
313 await _runLiveTest(suiteController, liveTest, countSuccess: false);
278 if (_closed) await liveTest.close(); 314 if (_closed) await liveTest.close();
279 } 315 }
280 } finally { 316 } finally {
281 parents.remove(group); 317 parents.remove(group);
282 } 318 }
283 } 319 }
284 320
285 /// Returns a dummy [LiveTest] for a test or group marked as "skip". 321 /// Runs [liveTest] using [suiteController].
286 ///
287 /// [parents] is a list of groups that contain [entry].
288 LiveTest _skippedTest(RunnerSuite suite, GroupEntry entry,
289 List<Group> parents) {
290 // The netry name will be `null` for the root group.
291 var test = new LocalTest(entry.name ?? "(suite)", entry.metadata, () {});
292
293 var controller;
294 controller = new LiveTestController(suite, test, () {
295 controller.setState(const State(Status.running, Result.success));
296 controller.setState(const State(Status.complete, Result.success));
297 controller.completer.complete();
298 }, () {}, groups: parents);
299 return controller.liveTest;
300 }
301
302 /// Runs [liveTest].
303 /// 322 ///
304 /// If [countSuccess] is `true` (the default), the test is put into [passed] 323 /// If [countSuccess] is `true` (the default), the test is put into [passed]
305 /// if it succeeds. Otherwise, it's removed from [liveTests] entirely. 324 /// if it succeeds. Otherwise, it's removed from [liveTests] entirely.
306 Future _runLiveTest(LiveTest liveTest, {bool countSuccess: true}) async { 325 Future _runLiveTest(LiveSuiteController suiteController, LiveTest liveTest,
307 _liveTests.add(liveTest); 326 {bool countSuccess: true}) async {
308 _active.add(liveTest); 327 _active.add(liveTest);
309 328
310 // If there were no active non-load tests, the current active test would 329 // If there were no active non-load tests, the current active test would
311 // have been a load test. In that case, remove it, since now we have a 330 // have been a load test. In that case, remove it, since now we have a
312 // non-load test to add. 331 // non-load test to add.
313 if (_active.isNotEmpty && _active.first.suite is LoadSuite) { 332 if (_active.first.suite is LoadSuite) _active.removeFirst();
314 _liveTests.remove(_active.removeFirst());
315 }
316 333
317 liveTest.onStateChange.listen((state) { 334 liveTest.onStateChange.listen((state) {
318 if (state.status != Status.complete) return; 335 if (state.status != Status.complete) return;
319 _active.remove(liveTest); 336 _active.remove(liveTest);
320 337
321 // If we're out of non-load tests, surface a load test. 338 // If we're out of non-load tests, surface a load test.
322 if (_active.isEmpty && _activeLoadTests.isNotEmpty) { 339 if (_active.isEmpty && _activeLoadTests.isNotEmpty) {
323 _active.add(_activeLoadTests.first); 340 _active.add(_activeLoadTests.first);
324 _liveTests.add(_activeLoadTests.first);
325 }
326
327 if (state.result != Result.success) {
328 _passed.remove(liveTest);
329 _failed.add(liveTest);
330 } else if (liveTest.test.metadata.skip) {
331 _skipped.add(liveTest);
332 } else if (countSuccess) {
333 _passed.add(liveTest);
334 } else {
335 _liveTests.remove(liveTest);
336 } 341 }
337 }); 342 });
338 343
339 _onTestStartedController.add(liveTest); 344 suiteController.reportLiveTest(liveTest, countSuccess: countSuccess);
340 345
341 // First, schedule a microtask to ensure that [onTestStarted] fires before 346 // Schedule a microtask to ensure that [onTestStarted] fires before the
342 // the first [LiveTest.onStateChange] event. Once the test finishes, use 347 // first [LiveTest.onStateChange] event.
343 // [new Future] to do a coarse-grained event loop pump to avoid starving
344 // non-microtask events.
345 await new Future.microtask(liveTest.run); 348 await new Future.microtask(liveTest.run);
349
350 // Once the test finishes, use [new Future] to do a coarse-grained event
351 // loop pump to avoid starving non-microtask events.
346 await new Future(() {}); 352 await new Future(() {});
347 353
348 if (!_restarted.contains(liveTest)) return; 354 if (!_restarted.contains(liveTest)) return;
349 await _runLiveTest(liveTest.copy(), countSuccess: countSuccess); 355 await _runLiveTest(suiteController, liveTest.copy(),
356 countSuccess: countSuccess);
350 _restarted.remove(liveTest); 357 _restarted.remove(liveTest);
351 } 358 }
352 359
360 /// Runs a dummy [LiveTest] for a test or group marked as "skip".
361 ///
362 /// [suiteController] is the controller for the suite that contains [entry].
363 /// [parents] is a list of groups that contain [entry].
364 Future _runSkippedTest(LiveSuiteController suiteController, GroupEntry entry,
365 List<Group> parents) {
366 // The netry name will be `null` for the root group.
367 var test = new LocalTest(entry.name ?? "(suite)", entry.metadata, () {});
368
369 var controller;
370 controller = new LiveTestController(
371 suiteController.liveSuite.suite, test, () {
372 controller.setState(const State(Status.running, Result.success));
373 controller.setState(const State(Status.complete, Result.success));
374 controller.completer.complete();
375 }, () {}, groups: parents);
376
377 return _runLiveTest(suiteController, controller.liveTest);
378 }
379
353 /// Closes [liveTest] and tells the engine to re-run it once it's done 380 /// Closes [liveTest] and tells the engine to re-run it once it's done
354 /// running. 381 /// running.
355 /// 382 ///
356 /// Returns the same future as [LiveTest.close]. 383 /// Returns the same future as [LiveTest.close].
357 Future restartTest(LiveTest liveTest) async { 384 Future restartTest(LiveTest liveTest) async {
358 if (_activeLoadTests.contains(liveTest)) { 385 if (_activeLoadTests.contains(liveTest)) {
359 throw new ArgumentError("Can't restart a load test."); 386 throw new ArgumentError("Can't restart a load test.");
360 } 387 }
361 388
362 if (!_active.contains(liveTest)) { 389 if (!_active.contains(liveTest)) {
363 throw new StateError("Can't restart inactive test " 390 throw new StateError("Can't restart inactive test "
364 "\"${liveTest.test.name}\"."); 391 "\"${liveTest.test.name}\".");
365 } 392 }
366 393
367 _restarted.add(liveTest); 394 _restarted.add(liveTest);
368 _active.remove(liveTest); 395 _active.remove(liveTest);
369 await liveTest.close(); 396 await liveTest.close();
370 } 397 }
371 398
372 /// Adds listeners for [suite]. 399 /// Runs [suite] and returns the [LiveSuiteController] for the suite it loads.
373 /// 400 ///
374 /// Load suites have specific logic apart from normal test suites. 401 /// Returns `null` if the suite fails to load.
375 Future<RunnerSuite> _addLoadSuite(LoadSuite suite) async { 402 Future<LiveSuiteController> _addLoadSuite(LoadSuite suite) async {
403 var controller = new LiveSuiteController(suite);
404 _addLiveSuite(controller.liveSuite);
405
376 var liveTest = await suite.test.load(suite); 406 var liveTest = await suite.test.load(suite);
377
378 _activeLoadTests.add(liveTest); 407 _activeLoadTests.add(liveTest);
379 408
380 // Only surface the load test if there are no other tests currently running. 409 // Only surface the load test if there are no other tests currently running.
381 if (_active.isEmpty) { 410 if (_active.isEmpty) _active.add(liveTest);
382 _liveTests.add(liveTest);
383 _active.add(liveTest);
384 }
385 411
386 liveTest.onStateChange.listen((state) { 412 liveTest.onStateChange.listen((state) {
387 if (state.status != Status.complete) return; 413 if (state.status != Status.complete) return;
388 _activeLoadTests.remove(liveTest); 414 _activeLoadTests.remove(liveTest);
389 415
390 // Only one load test will be active at any given time, and it will always 416 // Only one load test will be active at any given time, and it will always
391 // be the only active test. Remove it and, if possible, surface another 417 // be the only active test. Remove it and, if possible, surface another
392 // load test. 418 // load test.
393 if (_active.isNotEmpty && _active.first.suite == suite) { 419 if (_active.isNotEmpty && _active.first.suite == suite) {
394 _active.remove(liveTest); 420 _active.remove(liveTest);
395 _liveTests.remove(liveTest); 421 if (_activeLoadTests.isNotEmpty) _active.add(_activeLoadTests.last);
396
397 if (_activeLoadTests.isNotEmpty) {
398 _active.add(_activeLoadTests.last);
399 _liveTests.add(_activeLoadTests.last);
400 }
401 } 422 }
402
403 // Surface the load test if it fails so that the user can see the failure.
404 if (state.result == Result.success) return;
405 _failed.add(liveTest);
406 _liveTests.add(liveTest);
407 }); 423 });
408 424
409 // Run the test immediately. We don't want loading to be blocked on suites 425 controller.reportLiveTest(liveTest, countSuccess: false);
410 // that are already running. 426 controller.noMoreLiveTests();
411 _onTestStartedController.add(liveTest);
412 await liveTest.run();
413 427
414 return suite.suite; 428 // Schedule a microtask to ensure that [onTestStarted] fires before the
429 // first [LiveTest.onStateChange] event.
430 new Future.microtask(liveTest.run);
431
432 var innerSuite = await suite.suite;
433 if (innerSuite == null) return null;
434
435 var innerController = new LiveSuiteController(innerSuite);
436 innerController.liveSuite.onClose.then((_) {
437 // When the main suite is closed, close the load suite and its test as
438 // well. This doesn't release any resources, but it does close streams
439 // which indicates that the load test won't experience an error in the
440 // future.
441 liveTest.close();
442 controller.close();
443 });
444
445 return innerController;
446 }
447
448 /// Add [liveSuite] and the information it exposes to the engine's
449 /// informational streams and collections.
450 void _addLiveSuite(LiveSuite liveSuite) {
451 _liveSuites.add(liveSuite);
452 _onSuiteStartedController.add(liveSuite);
453
454 _onTestStartedGroup.add(liveSuite.onTestStarted);
455 _passedGroup.add(liveSuite.passed);
456 _skippedGroup.add(liveSuite.skipped);
457 _failedGroup.add(liveSuite.failed);
415 } 458 }
416 459
417 /// Signals that the caller is done paying attention to test results and the 460 /// Signals that the caller is done paying attention to test results and the
418 /// engine should release any resources it has allocated. 461 /// engine should release any resources it has allocated.
419 /// 462 ///
420 /// Any actively-running tests are also closed. VM tests are allowed to finish 463 /// Any actively-running tests are also closed. VM tests are allowed to finish
421 /// running so that any modifications they've made to the filesystem can be 464 /// running so that any modifications they've made to the filesystem can be
422 /// cleaned up. 465 /// cleaned up.
423 /// 466 ///
424 /// **Note that closing the engine is not the same as closing [suiteSink].** 467 /// **Note that closing the engine is not the same as closing [suiteSink].**
(...skipping 11 matching lines...) Expand all
436 var futures = allLiveTests.map((liveTest) => liveTest.close()).toList(); 479 var futures = allLiveTests.map((liveTest) => liveTest.close()).toList();
437 480
438 // Closing the load pool will close the test suites as soon as their tests 481 // Closing the load pool will close the test suites as soon as their tests
439 // are done. For browser suites this is effectively immediate since their 482 // are done. For browser suites this is effectively immediate since their
440 // tests shut down as soon as they're closed, but for VM suites we may need 483 // tests shut down as soon as they're closed, but for VM suites we may need
441 // to wait for tearDowns or tearDownAlls to run. 484 // to wait for tearDowns or tearDownAlls to run.
442 futures.add(_loadPool.close()); 485 futures.add(_loadPool.close());
443 await Future.wait(futures, eagerError: true); 486 await Future.wait(futures, eagerError: true);
444 } 487 }
445 } 488 }
OLDNEW
« no previous file with comments | « lib/src/backend/invoker.dart ('k') | lib/src/runner/live_suite.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698