| OLD | NEW |
| 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; |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 208 return success; | 208 return success; |
| 209 } | 209 } |
| 210 | 210 |
| 211 /// Runs all the entries in [entries] in sequence. | 211 /// Runs all the entries in [entries] in sequence. |
| 212 Future _runGroup(RunnerSuite suite, Group group) async { | 212 Future _runGroup(RunnerSuite suite, Group group) async { |
| 213 if (group.metadata.skip) { | 213 if (group.metadata.skip) { |
| 214 await _runLiveTest(_skippedTest(suite, group)); | 214 await _runLiveTest(_skippedTest(suite, group)); |
| 215 return; | 215 return; |
| 216 } | 216 } |
| 217 | 217 |
| 218 for (var entry in group.entries) { | 218 var setUpAllSucceeded = true; |
| 219 if (_closed) return; | 219 if (group.setUpAll != null) { |
| 220 var liveTest = group.setUpAll.load(suite); |
| 221 await _runLiveTest(liveTest, countSuccess: false); |
| 222 setUpAllSucceeded = liveTest.state.result == Result.success; |
| 223 } |
| 220 | 224 |
| 221 if (entry is Group) { | 225 if (!_closed && setUpAllSucceeded) { |
| 222 await _runGroup(suite, entry); | 226 for (var entry in group.entries) { |
| 223 } else if (entry.metadata.skip) { | 227 if (_closed) return; |
| 224 await _runLiveTest(_skippedTest(suite, entry)); | 228 |
| 225 } else { | 229 if (entry is Group) { |
| 226 var test = entry as Test; | 230 await _runGroup(suite, entry); |
| 227 await _runLiveTest(test.load(suite)); | 231 } else if (entry.metadata.skip) { |
| 232 await _runLiveTest(_skippedTest(suite, entry)); |
| 233 } else { |
| 234 var test = entry as Test; |
| 235 await _runLiveTest(test.load(suite)); |
| 236 } |
| 228 } | 237 } |
| 229 } | 238 } |
| 239 |
| 240 // Even if we're closed or setUpAll failed, we want to run all the teardowns |
| 241 // to ensure that any state is properly cleaned up. |
| 242 if (group.tearDownAll != null) { |
| 243 var liveTest = group.tearDownAll.load(suite); |
| 244 await _runLiveTest(liveTest, countSuccess: false); |
| 245 if (_closed) await liveTest.close(); |
| 246 } |
| 230 } | 247 } |
| 231 | 248 |
| 232 /// Returns a dummy [LiveTest] for a test or group marked as "skip". | 249 /// Returns a dummy [LiveTest] for a test or group marked as "skip". |
| 233 LiveTest _skippedTest(RunnerSuite suite, GroupEntry entry) { | 250 LiveTest _skippedTest(RunnerSuite suite, GroupEntry entry) { |
| 234 // The netry name will be `null` for the root group. | 251 // The netry name will be `null` for the root group. |
| 235 var test = new LocalTest(entry.name ?? "(suite)", entry.metadata, () {}); | 252 var test = new LocalTest(entry.name ?? "(suite)", entry.metadata, () {}); |
| 236 | 253 |
| 237 var controller; | 254 var controller; |
| 238 controller = new LiveTestController(suite, test, () { | 255 controller = new LiveTestController(suite, test, () { |
| 239 controller.setState(const State(Status.running, Result.success)); | 256 controller.setState(const State(Status.running, Result.success)); |
| 240 controller.setState(const State(Status.complete, Result.success)); | 257 controller.setState(const State(Status.complete, Result.success)); |
| 241 controller.completer.complete(); | 258 controller.completer.complete(); |
| 242 }, () {}); | 259 }, () {}); |
| 243 return controller.liveTest; | 260 return controller.liveTest; |
| 244 } | 261 } |
| 245 | 262 |
| 246 /// Runs [liveTest]. | 263 /// Runs [liveTest]. |
| 247 Future _runLiveTest(LiveTest liveTest) async { | 264 /// |
| 265 /// If [countSuccess] is `true` (the default), the test is put into [passed] |
| 266 /// if it succeeds. Otherwise, it's removed from [liveTests] entirely. |
| 267 Future _runLiveTest(LiveTest liveTest, {bool countSuccess: true}) async { |
| 248 _liveTests.add(liveTest); | 268 _liveTests.add(liveTest); |
| 249 _active.add(liveTest); | 269 _active.add(liveTest); |
| 250 | 270 |
| 251 // If there were no active non-load tests, the current active test would | 271 // If there were no active non-load tests, the current active test would |
| 252 // have been a load test. In that case, remove it, since now we have a | 272 // have been a load test. In that case, remove it, since now we have a |
| 253 // non-load test to add. | 273 // non-load test to add. |
| 254 if (_active.isNotEmpty && _active.first.suite is LoadSuite) { | 274 if (_active.isNotEmpty && _active.first.suite is LoadSuite) { |
| 255 _liveTests.remove(_active.removeFirst()); | 275 _liveTests.remove(_active.removeFirst()); |
| 256 } | 276 } |
| 257 | 277 |
| 258 liveTest.onStateChange.listen((state) { | 278 liveTest.onStateChange.listen((state) { |
| 259 if (state.status != Status.complete) return; | 279 if (state.status != Status.complete) return; |
| 260 _active.remove(liveTest); | 280 _active.remove(liveTest); |
| 261 | 281 |
| 262 // If we're out of non-load tests, surface a load test. | 282 // If we're out of non-load tests, surface a load test. |
| 263 if (_active.isEmpty && _activeLoadTests.isNotEmpty) { | 283 if (_active.isEmpty && _activeLoadTests.isNotEmpty) { |
| 264 _active.add(_activeLoadTests.first); | 284 _active.add(_activeLoadTests.first); |
| 265 _liveTests.add(_activeLoadTests.first); | 285 _liveTests.add(_activeLoadTests.first); |
| 266 } | 286 } |
| 267 | 287 |
| 268 if (state.result != Result.success) { | 288 if (state.result != Result.success) { |
| 269 _passed.remove(liveTest); | 289 _passed.remove(liveTest); |
| 270 _failed.add(liveTest); | 290 _failed.add(liveTest); |
| 271 } else if (liveTest.test.metadata.skip) { | 291 } else if (liveTest.test.metadata.skip) { |
| 272 _skipped.add(liveTest); | 292 _skipped.add(liveTest); |
| 293 } else if (countSuccess) { |
| 294 _passed.add(liveTest); |
| 273 } else { | 295 } else { |
| 274 _passed.add(liveTest); | 296 _liveTests.remove(liveTest); |
| 275 } | 297 } |
| 276 }); | 298 }); |
| 277 | 299 |
| 278 _onTestStartedController.add(liveTest); | 300 _onTestStartedController.add(liveTest); |
| 279 | 301 |
| 280 // First, schedule a microtask to ensure that [onTestStarted] fires before | 302 // First, schedule a microtask to ensure that [onTestStarted] fires before |
| 281 // the first [LiveTest.onStateChange] event. Once the test finishes, use | 303 // the first [LiveTest.onStateChange] event. Once the test finishes, use |
| 282 // [new Future] to do a coarse-grained event loop pump to avoid starving | 304 // [new Future] to do a coarse-grained event loop pump to avoid starving |
| 283 // non-microtask events. | 305 // non-microtask events. |
| 284 await new Future.microtask(liveTest.run); | 306 await new Future.microtask(liveTest.run); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 335 /// | 357 /// |
| 336 /// Any actively-running tests are also closed. VM tests are allowed to finish | 358 /// Any actively-running tests are also closed. VM tests are allowed to finish |
| 337 /// running so that any modifications they've made to the filesystem can be | 359 /// running so that any modifications they've made to the filesystem can be |
| 338 /// cleaned up. | 360 /// cleaned up. |
| 339 /// | 361 /// |
| 340 /// **Note that closing the engine is not the same as closing [suiteSink].** | 362 /// **Note that closing the engine is not the same as closing [suiteSink].** |
| 341 /// Closing [suiteSink] indicates that no more input will be provided, closing | 363 /// Closing [suiteSink] indicates that no more input will be provided, closing |
| 342 /// the engine indicates that no more output should be emitted. | 364 /// the engine indicates that no more output should be emitted. |
| 343 Future close() async { | 365 Future close() async { |
| 344 _closed = true; | 366 _closed = true; |
| 345 if (_closedBeforeDone == null) _closedBeforeDone = true; | 367 if (_closedBeforeDone != null) _closedBeforeDone = true; |
| 346 _suiteController.close(); | 368 _suiteController.close(); |
| 347 | 369 |
| 348 // Close the running tests first so that we're sure to wait for them to | 370 // Close the running tests first so that we're sure to wait for them to |
| 349 // finish before we close their suites and cause them to become unloaded. | 371 // finish before we close their suites and cause them to become unloaded. |
| 350 var allLiveTests = liveTests.toSet()..addAll(_activeLoadTests); | 372 var allLiveTests = liveTests.toSet()..addAll(_activeLoadTests); |
| 351 await Future.wait(allLiveTests.map((liveTest) => liveTest.close())); | 373 var futures = allLiveTests.map((liveTest) => liveTest.close()).toList(); |
| 352 | 374 |
| 353 var allSuites = allLiveTests.map((liveTest) => liveTest.suite).toSet(); | 375 // Closing the load pool will close the test suites as soon as their tests |
| 354 await Future.wait(allSuites.map((suite) => suite.close())); | 376 // are done. For browser suites this is effectively immediate since their |
| 377 // tests shut down as soon as they're closed, but for VM suites we may need |
| 378 // to wait for tearDowns or tearDownAlls to run. |
| 379 futures.add(_loadPool.close()); |
| 380 await Future.wait(futures, eagerError: true); |
| 355 } | 381 } |
| 356 } | 382 } |
| OLD | NEW |