| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 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 | 
|  | 3 // BSD-style license that can be found in the LICENSE file. | 
|  | 4 | 
|  | 5 library test.runner.engine; | 
|  | 6 | 
|  | 7 import 'dart:async'; | 
|  | 8 import 'dart:collection'; | 
|  | 9 | 
|  | 10 import 'package:async/async.dart' hide Result; | 
|  | 11 import 'package:collection/collection.dart'; | 
|  | 12 import 'package:pool/pool.dart'; | 
|  | 13 | 
|  | 14 import '../backend/live_test.dart'; | 
|  | 15 import '../backend/live_test_controller.dart'; | 
|  | 16 import '../backend/state.dart'; | 
|  | 17 import '../backend/test.dart'; | 
|  | 18 import 'load_suite.dart'; | 
|  | 19 import 'runner_suite.dart'; | 
|  | 20 | 
|  | 21 /// An [Engine] manages a run that encompasses multiple test suites. | 
|  | 22 /// | 
|  | 23 /// Test suites are provided by passing them into [suiteSink]. Once all suites | 
|  | 24 /// have been provided, the user should close [suiteSink] to indicate this. | 
|  | 25 /// [run] won't terminate until [suiteSink] is closed. Suites will be run in the | 
|  | 26 /// order they're provided to [suiteSink]. Tests within those suites will | 
|  | 27 /// likewise be run in the order of [Suite.tests]. | 
|  | 28 /// | 
|  | 29 /// The current status of every test is visible via [liveTests]. [onTestStarted] | 
|  | 30 /// can also be used to be notified when a test is about to be run. | 
|  | 31 /// | 
|  | 32 /// The engine has some special logic for [LoadSuite]s and the tests they | 
|  | 33 /// contain, referred to as "load tests". Load tests exist to provide visibility | 
|  | 34 /// into the process of loading test files, but as long as that process is | 
|  | 35 /// proceeding normally users usually don't care about it, so the engine only | 
|  | 36 /// surfaces running load tests (that is, includes them in [liveTests] and other | 
|  | 37 /// collections) under specific circumstances. | 
|  | 38 /// | 
|  | 39 /// If only load tests are running, exactly one load test will be in [active] | 
|  | 40 /// and [liveTests]. If this test passes, it will be removed from both [active] | 
|  | 41 /// and [liveTests] and *will not* be added to [passed]. If at any point a load | 
|  | 42 /// test fails, it will be added to [failed] and [liveTests]. | 
|  | 43 /// | 
|  | 44 /// The test suite loaded by a load suite will be automatically be run by the | 
|  | 45 /// engine; it doesn't need to be added to [suiteSink] manually. | 
|  | 46 /// | 
|  | 47 /// Load tests will always be emitted through [onTestStarted] so users can watch | 
|  | 48 /// their event streams once they start running. | 
|  | 49 class Engine { | 
|  | 50   /// Whether [run] has been called yet. | 
|  | 51   var _runCalled = false; | 
|  | 52 | 
|  | 53   /// Whether [close] has been called. | 
|  | 54   var _closed = false; | 
|  | 55 | 
|  | 56   /// Whether [close] was called before all the tests finished running. | 
|  | 57   /// | 
|  | 58   /// This is `null` if close hasn't been called and the tests are still | 
|  | 59   /// running, `true` if close was called before the tests finished running, and | 
|  | 60   /// `false` if the tests finished running before close was called. | 
|  | 61   var _closedBeforeDone; | 
|  | 62 | 
|  | 63   /// A pool that limits the number of test suites running concurrently. | 
|  | 64   final Pool _runPool; | 
|  | 65 | 
|  | 66   /// A pool that limits the number of test suites loaded concurrently. | 
|  | 67   /// | 
|  | 68   /// Once this reaches its limit, loading any additional test suites will cause | 
|  | 69   /// previous suites to be unloaded in the order they completed. | 
|  | 70   final Pool _loadPool; | 
|  | 71 | 
|  | 72   /// Whether all tests passed. | 
|  | 73   /// | 
|  | 74   /// This fires once all tests have completed and [suiteSink] has been closed. | 
|  | 75   /// This will be `null` if [close] was called before all the tests finished | 
|  | 76   /// running. | 
|  | 77   Future<bool> get success async { | 
|  | 78     await _group.future; | 
|  | 79     if (_closedBeforeDone) return null; | 
|  | 80     return liveTests.every((liveTest) => | 
|  | 81         liveTest.state.result == Result.success); | 
|  | 82   } | 
|  | 83 | 
|  | 84   /// A group of futures for each test suite. | 
|  | 85   final _group = new FutureGroup(); | 
|  | 86 | 
|  | 87   /// A sink used to pass [RunnerSuite]s in to the engine to run. | 
|  | 88   /// | 
|  | 89   /// Suites may be added as quickly as they're available; the Engine will only | 
|  | 90   /// run as many as necessary at a time based on its concurrency settings. | 
|  | 91   /// | 
|  | 92   /// Suites added to the sink will be closed by the engine based on its | 
|  | 93   /// internal logic. | 
|  | 94   Sink<RunnerSuite> get suiteSink => new DelegatingSink(_suiteController.sink); | 
|  | 95   final _suiteController = new StreamController<RunnerSuite>(); | 
|  | 96 | 
|  | 97   /// All the currently-known tests that have run, are running, or will run. | 
|  | 98   /// | 
|  | 99   /// These are [LiveTest]s, representing the in-progress state of each test. | 
|  | 100   /// Tests that have not yet begun running are marked [Status.pending]; tests | 
|  | 101   /// that have finished are marked [Status.complete]. | 
|  | 102   /// | 
|  | 103   /// This is guaranteed to contain the same tests as the union of [passed], | 
|  | 104   /// [skipped], [failed], and [active]. | 
|  | 105   /// | 
|  | 106   /// [LiveTest.run] must not be called on these tests. | 
|  | 107   List<LiveTest> get liveTests => new UnmodifiableListView(_liveTests); | 
|  | 108   final _liveTests = new List<LiveTest>(); | 
|  | 109 | 
|  | 110   /// A stream that emits each [LiveTest] as it's about to start running. | 
|  | 111   /// | 
|  | 112   /// This is guaranteed to fire before [LiveTest.onStateChange] first fires. | 
|  | 113   Stream<LiveTest> get onTestStarted => _onTestStartedController.stream; | 
|  | 114   final _onTestStartedController = new StreamController<LiveTest>.broadcast(); | 
|  | 115 | 
|  | 116   /// The set of tests that have completed and been marked as passing. | 
|  | 117   Set<LiveTest> get passed => new UnmodifiableSetView(_passed); | 
|  | 118   final _passed = new Set<LiveTest>(); | 
|  | 119 | 
|  | 120   /// The set of tests that have completed and been marked as skipped. | 
|  | 121   Set<LiveTest> get skipped => new UnmodifiableSetView(_skipped); | 
|  | 122   final _skipped = new Set<LiveTest>(); | 
|  | 123 | 
|  | 124   /// The set of tests that have completed and been marked as failing or error. | 
|  | 125   Set<LiveTest> get failed => new UnmodifiableSetView(_failed); | 
|  | 126   final _failed = new Set<LiveTest>(); | 
|  | 127 | 
|  | 128   /// The tests that are still running, in the order they begain running. | 
|  | 129   List<LiveTest> get active => new UnmodifiableListView(_active); | 
|  | 130   final _active = new QueueList<LiveTest>(); | 
|  | 131 | 
|  | 132   /// The tests from [LoadSuite]s that are still running, in the order they | 
|  | 133   /// began running. | 
|  | 134   /// | 
|  | 135   /// This is separate from [active] because load tests aren't always surfaced. | 
|  | 136   final _activeLoadTests = new List<LiveTest>(); | 
|  | 137 | 
|  | 138   /// Whether this engine is idle—that is, not currently executing a test. | 
|  | 139   bool get isIdle => _group.isIdle; | 
|  | 140 | 
|  | 141   /// A broadcast stream that fires an event whenever [isIdle] switches from | 
|  | 142   /// `false` to `true`. | 
|  | 143   Stream get onIdle => _group.onIdle; | 
|  | 144 | 
|  | 145   /// Creates an [Engine] that will run all tests provided via [suiteSink]. | 
|  | 146   /// | 
|  | 147   /// [concurrency] controls how many suites are run at once, and defaults to 1. | 
|  | 148   /// [maxSuites] controls how many suites are *loaded* at once, and defaults to | 
|  | 149   /// four times [concurrency]. | 
|  | 150   Engine({int concurrency, int maxSuites}) | 
|  | 151       : _runPool = new Pool(concurrency == null ? 1 : concurrency), | 
|  | 152         _loadPool = new Pool(maxSuites == null | 
|  | 153             ? (concurrency == null ? 2 : concurrency * 2) | 
|  | 154             : maxSuites) { | 
|  | 155     _group.future.then((_) { | 
|  | 156       if (_closedBeforeDone == null) _closedBeforeDone = false; | 
|  | 157     }).catchError((_) { | 
|  | 158       // Don't top-level errors. They'll be thrown via [success] anyway. | 
|  | 159     }); | 
|  | 160   } | 
|  | 161 | 
|  | 162   /// Creates an [Engine] that will run all tests in [suites]. | 
|  | 163   /// | 
|  | 164   /// [concurrency] controls how many suites are run at once. An engine | 
|  | 165   /// constructed this way will automatically close its [suiteSink], meaning | 
|  | 166   /// that no further suites may be provided. | 
|  | 167   factory Engine.withSuites(List<RunnerSuite> suites, {int concurrency}) { | 
|  | 168     var engine = new Engine(concurrency: concurrency); | 
|  | 169     for (var suite in suites) engine.suiteSink.add(suite); | 
|  | 170     engine.suiteSink.close(); | 
|  | 171     return engine; | 
|  | 172   } | 
|  | 173 | 
|  | 174   /// Runs all tests in all suites defined by this engine. | 
|  | 175   /// | 
|  | 176   /// This returns `true` if all tests succeed, and `false` otherwise. It will | 
|  | 177   /// only return once all tests have finished running and [suiteSink] has been | 
|  | 178   /// closed. | 
|  | 179   Future<bool> run() { | 
|  | 180     if (_runCalled) { | 
|  | 181       throw new StateError("Engine.run() may not be called more than once."); | 
|  | 182     } | 
|  | 183     _runCalled = true; | 
|  | 184 | 
|  | 185     _suiteController.stream.listen((suite) { | 
|  | 186       _group.add(new Future.sync(() async { | 
|  | 187         var loadResource = await _loadPool.request(); | 
|  | 188 | 
|  | 189         if (suite is LoadSuite) { | 
|  | 190           suite = await _addLoadSuite(suite); | 
|  | 191           if (suite == null) { | 
|  | 192             loadResource.release(); | 
|  | 193             return; | 
|  | 194           } | 
|  | 195         } | 
|  | 196 | 
|  | 197         await _runPool.withResource(() async { | 
|  | 198           if (_closed) return null; | 
|  | 199 | 
|  | 200           // TODO(nweiz): Use a real for loop when issue 23394 is fixed. | 
|  | 201           await Future.forEach(suite.tests, (test) async { | 
|  | 202             if (_closed) return; | 
|  | 203 | 
|  | 204             var liveTest = test.metadata.skip | 
|  | 205                 ? _skippedTest(suite, test) | 
|  | 206                 : test.load(suite); | 
|  | 207             _liveTests.add(liveTest); | 
|  | 208             _active.add(liveTest); | 
|  | 209 | 
|  | 210             // If there were no active non-load tests, the current active test | 
|  | 211             // would have been a load test. In that case, remove it, since now w
     e | 
|  | 212             // have a non-load test to add. | 
|  | 213             if (_active.isNotEmpty && _active.first.suite is LoadSuite) { | 
|  | 214               _liveTests.remove(_active.removeFirst()); | 
|  | 215             } | 
|  | 216 | 
|  | 217             liveTest.onStateChange.listen((state) { | 
|  | 218               if (state.status != Status.complete) return; | 
|  | 219               _active.remove(liveTest); | 
|  | 220 | 
|  | 221               // If we're out of non-load tests, surface a load test. | 
|  | 222               if (_active.isEmpty && _activeLoadTests.isNotEmpty) { | 
|  | 223                 _active.add(_activeLoadTests.first); | 
|  | 224                 _liveTests.add(_activeLoadTests.first); | 
|  | 225               } | 
|  | 226 | 
|  | 227               if (state.result != Result.success) { | 
|  | 228                 _passed.remove(liveTest); | 
|  | 229                 _failed.add(liveTest); | 
|  | 230               } else if (liveTest.test.metadata.skip) { | 
|  | 231                 _skipped.add(liveTest); | 
|  | 232               } else { | 
|  | 233                 _passed.add(liveTest); | 
|  | 234               } | 
|  | 235             }); | 
|  | 236 | 
|  | 237             _onTestStartedController.add(liveTest); | 
|  | 238 | 
|  | 239             // First, schedule a microtask to ensure that [onTestStarted] fires | 
|  | 240             // before the first [LiveTest.onStateChange] event. Once the test | 
|  | 241             // finishes, use [new Future] to do a coarse-grained event loop pump | 
|  | 242             // to avoid starving non-microtask events. | 
|  | 243             await new Future.microtask(liveTest.run); | 
|  | 244             await new Future(() {}); | 
|  | 245           }); | 
|  | 246 | 
|  | 247           loadResource.allowRelease(() => suite.close()); | 
|  | 248         }); | 
|  | 249       })); | 
|  | 250     }, onDone: _group.close); | 
|  | 251 | 
|  | 252     return success; | 
|  | 253   } | 
|  | 254 | 
|  | 255   /// Returns a dummy [LiveTest] for a test marked as "skip". | 
|  | 256   LiveTest _skippedTest(RunnerSuite suite, Test test) { | 
|  | 257     var controller; | 
|  | 258     controller = new LiveTestController(suite, test, () { | 
|  | 259       controller.setState(const State(Status.running, Result.success)); | 
|  | 260       controller.setState(const State(Status.complete, Result.success)); | 
|  | 261       controller.completer.complete(); | 
|  | 262     }, () {}); | 
|  | 263     return controller.liveTest; | 
|  | 264   } | 
|  | 265 | 
|  | 266   /// Adds listeners for [suite]. | 
|  | 267   /// | 
|  | 268   /// Load suites have specific logic apart from normal test suites. | 
|  | 269   Future<RunnerSuite> _addLoadSuite(LoadSuite suite) async { | 
|  | 270     var liveTest = await suite.tests.single.load(suite); | 
|  | 271 | 
|  | 272     _activeLoadTests.add(liveTest); | 
|  | 273 | 
|  | 274     // Only surface the load test if there are no other tests currently running. | 
|  | 275     if (_active.isEmpty) { | 
|  | 276       _liveTests.add(liveTest); | 
|  | 277       _active.add(liveTest); | 
|  | 278     } | 
|  | 279 | 
|  | 280     liveTest.onStateChange.listen((state) { | 
|  | 281       if (state.status != Status.complete) return; | 
|  | 282       _activeLoadTests.remove(liveTest); | 
|  | 283 | 
|  | 284       // Only one load test will be active at any given time, and it will always | 
|  | 285       // be the only active test. Remove it and, if possible, surface another | 
|  | 286       // load test. | 
|  | 287       if (_active.isNotEmpty && _active.first.suite == suite) { | 
|  | 288         _active.remove(liveTest); | 
|  | 289         _liveTests.remove(liveTest); | 
|  | 290 | 
|  | 291         if (_activeLoadTests.isNotEmpty) { | 
|  | 292           _active.add(_activeLoadTests.last); | 
|  | 293           _liveTests.add(_activeLoadTests.last); | 
|  | 294         } | 
|  | 295       } | 
|  | 296 | 
|  | 297       // Surface the load test if it fails so that the user can see the failure. | 
|  | 298       if (state.result == Result.success) return; | 
|  | 299       _failed.add(liveTest); | 
|  | 300       _liveTests.add(liveTest); | 
|  | 301     }); | 
|  | 302 | 
|  | 303     // Run the test immediately. We don't want loading to be blocked on suites | 
|  | 304     // that are already running. | 
|  | 305     _onTestStartedController.add(liveTest); | 
|  | 306     await liveTest.run(); | 
|  | 307 | 
|  | 308     return suite.suite; | 
|  | 309   } | 
|  | 310 | 
|  | 311   /// Signals that the caller is done paying attention to test results and the | 
|  | 312   /// engine should release any resources it has allocated. | 
|  | 313   /// | 
|  | 314   /// Any actively-running tests are also closed. VM tests are allowed to finish | 
|  | 315   /// running so that any modifications they've made to the filesystem can be | 
|  | 316   /// cleaned up. | 
|  | 317   /// | 
|  | 318   /// **Note that closing the engine is not the same as closing [suiteSink].** | 
|  | 319   /// Closing [suiteSink] indicates that no more input will be provided, closing | 
|  | 320   /// the engine indicates that no more output should be emitted. | 
|  | 321   Future close() async { | 
|  | 322     _closed = true; | 
|  | 323     if (_closedBeforeDone == null) _closedBeforeDone = true; | 
|  | 324     _suiteController.close(); | 
|  | 325 | 
|  | 326     // Close the running tests first so that we're sure to wait for them to | 
|  | 327     // finish before we close their suites and cause them to become unloaded. | 
|  | 328     var allLiveTests = liveTests.toSet()..addAll(_activeLoadTests); | 
|  | 329     await Future.wait(allLiveTests.map((liveTest) => liveTest.close())); | 
|  | 330 | 
|  | 331     var allSuites = allLiveTests.map((liveTest) => liveTest.suite).toSet(); | 
|  | 332     await Future.wait(allSuites.map((suite) => suite.close())); | 
|  | 333   } | 
|  | 334 } | 
| OLD | NEW | 
|---|