| 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.backend.invoker; | 5 library test.backend.invoker; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:stack_trace/stack_trace.dart'; | 9 import 'package:stack_trace/stack_trace.dart'; |
| 10 | 10 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 /// The current invoker is accessible within the zone scope of the running test | 49 /// The current invoker is accessible within the zone scope of the running test |
| 50 /// using [Invoker.current]. It's used to track asynchronous callbacks and | 50 /// using [Invoker.current]. It's used to track asynchronous callbacks and |
| 51 /// report asynchronous errors. | 51 /// report asynchronous errors. |
| 52 class Invoker { | 52 class Invoker { |
| 53 /// The live test being driven by the invoker. | 53 /// The live test being driven by the invoker. |
| 54 /// | 54 /// |
| 55 /// This provides a view into the state of the test being executed. | 55 /// This provides a view into the state of the test being executed. |
| 56 LiveTest get liveTest => _controller.liveTest; | 56 LiveTest get liveTest => _controller.liveTest; |
| 57 LiveTestController _controller; | 57 LiveTestController _controller; |
| 58 | 58 |
| 59 bool get _closable => Zone.current[_closableKey]; |
| 60 |
| 61 /// An opaque object used as a key in the zone value map to identify |
| 62 /// [_closable]. |
| 63 /// |
| 64 /// This is an instance variable to ensure that multiple invokers don't step |
| 65 /// on one anothers' toes. |
| 66 final _closableKey = new Object(); |
| 67 |
| 59 /// Whether the test has been closed. | 68 /// Whether the test has been closed. |
| 60 /// | 69 /// |
| 61 /// Once the test is closed, [expect] and [expectAsync] will throw | 70 /// Once the test is closed, [expect] and [expectAsync] will throw |
| 62 /// [ClosedException]s whenever accessed to help the test stop executing as | 71 /// [ClosedException]s whenever accessed to help the test stop executing as |
| 63 /// soon as possible. | 72 /// soon as possible. |
| 64 bool get closed => _onCloseCompleter.isCompleted; | 73 bool get closed => _closable && _onCloseCompleter.isCompleted; |
| 65 | 74 |
| 66 /// A future that completes once the test has been closed. | 75 /// A future that completes once the test has been closed. |
| 67 Future get onClose => _onCloseCompleter.future; | 76 Future get onClose => _closable |
| 77 ? _onCloseCompleter.future |
| 78 // If we're in an unclosable block, return a future that will never |
| 79 // complete. |
| 80 : new Completer().future; |
| 68 final _onCloseCompleter = new Completer(); | 81 final _onCloseCompleter = new Completer(); |
| 69 | 82 |
| 70 /// The test being run. | 83 /// The test being run. |
| 71 LocalTest get _test => liveTest.test as LocalTest; | 84 LocalTest get _test => liveTest.test as LocalTest; |
| 72 | 85 |
| 73 /// The test metadata merged with the suite metadata. | 86 /// The test metadata merged with the suite metadata. |
| 74 final Metadata metadata; | 87 final Metadata metadata; |
| 75 | 88 |
| 76 /// The outstanding callback counter for the current zone. | 89 /// The outstanding callback counter for the current zone. |
| 77 OutstandingCallbackCounter get _outstandingCallbacks { | 90 OutstandingCallbackCounter get _outstandingCallbacks { |
| 78 var counter = Zone.current[this]; | 91 var counter = Zone.current[_counterKey]; |
| 79 if (counter != null) return counter; | 92 if (counter != null) return counter; |
| 80 throw new StateError("Can't add or remove outstanding callbacks outside " | 93 throw new StateError("Can't add or remove outstanding callbacks outside " |
| 81 "of a test body."); | 94 "of a test body."); |
| 82 } | 95 } |
| 83 | 96 |
| 97 /// An opaque object used as a key in the zone value map to identify |
| 98 /// [_outstandingCallbacks]. |
| 99 /// |
| 100 /// This is an instance variable to ensure that multiple invokers don't step |
| 101 /// on one anothers' toes. |
| 102 final _counterKey = new Object(); |
| 103 |
| 84 /// The current invoker, or `null` if none is defined. | 104 /// The current invoker, or `null` if none is defined. |
| 85 /// | 105 /// |
| 86 /// An invoker is only set within the zone scope of a running test. | 106 /// An invoker is only set within the zone scope of a running test. |
| 87 static Invoker get current { | 107 static Invoker get current { |
| 88 // TODO(nweiz): Use a private symbol when dart2js supports it (issue 17526). | 108 // TODO(nweiz): Use a private symbol when dart2js supports it (issue 17526). |
| 89 return Zone.current[#test.invoker]; | 109 return Zone.current[#test.invoker]; |
| 90 } | 110 } |
| 91 | 111 |
| 92 /// The zone that the top level of [_test.body] is running in. | 112 /// The zone that the top level of [_test.body] is running in. |
| 93 /// | 113 /// |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 heartbeat(); | 171 heartbeat(); |
| 152 | 172 |
| 153 var counter = new OutstandingCallbackCounter(); | 173 var counter = new OutstandingCallbackCounter(); |
| 154 runZoned(() { | 174 runZoned(() { |
| 155 // TODO(nweiz): Use async/await here once issue 23497 has been fixed in | 175 // TODO(nweiz): Use async/await here once issue 23497 has been fixed in |
| 156 // two stable versions. | 176 // two stable versions. |
| 157 runZoned(() { | 177 runZoned(() { |
| 158 new Future.sync(fn).then((_) => counter.removeOutstandingCallback()); | 178 new Future.sync(fn).then((_) => counter.removeOutstandingCallback()); |
| 159 }, onError: _handleError); | 179 }, onError: _handleError); |
| 160 }, zoneValues: { | 180 }, zoneValues: { |
| 161 // Use the invoker as a key so that multiple invokers can have different | 181 _counterKey: counter |
| 162 // outstanding callback counters at once. | |
| 163 this: counter | |
| 164 }); | 182 }); |
| 165 | 183 |
| 166 return counter.noOutstandingCallbacks; | 184 return counter.noOutstandingCallbacks; |
| 167 } | 185 } |
| 168 | 186 |
| 187 /// Runs [fn] in a zone where [closed] is always `false`. |
| 188 /// |
| 189 /// This is useful for running code that should be able to register callbacks |
| 190 /// and interact with the test framework normally even when the invoker is |
| 191 /// closed, for example cleanup code. |
| 192 unclosable(fn()) { |
| 193 heartbeat(); |
| 194 |
| 195 return runZoned(fn, zoneValues: { |
| 196 _closableKey: false |
| 197 }); |
| 198 } |
| 199 |
| 169 /// Notifies the invoker that progress is being made. | 200 /// Notifies the invoker that progress is being made. |
| 170 /// | 201 /// |
| 171 /// Each heartbeat resets the timeout timer. This helps ensure that | 202 /// Each heartbeat resets the timeout timer. This helps ensure that |
| 172 /// long-running tests that still make progress don't time out. | 203 /// long-running tests that still make progress don't time out. |
| 173 void heartbeat() { | 204 void heartbeat() { |
| 174 if (liveTest.isComplete) return; | 205 if (liveTest.isComplete) return; |
| 175 if (_timeoutTimer != null) _timeoutTimer.cancel(); | 206 if (_timeoutTimer != null) _timeoutTimer.cancel(); |
| 176 | 207 |
| 177 var timeout = metadata.timeout.apply(new Duration(seconds: 30)); | 208 var timeout = metadata.timeout.apply(new Duration(seconds: 30)); |
| 178 if (timeout == null) return; | 209 if (timeout == null) return; |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 240 new State(Status.complete, liveTest.state.result)); | 271 new State(Status.complete, liveTest.state.result)); |
| 241 | 272 |
| 242 // Use [Timer.run] here to avoid starving the DOM or other | 273 // Use [Timer.run] here to avoid starving the DOM or other |
| 243 // non-microtask events. | 274 // non-microtask events. |
| 244 Timer.run(_controller.completer.complete); | 275 Timer.run(_controller.completer.complete); |
| 245 }); | 276 }); |
| 246 }, zoneValues: { | 277 }, zoneValues: { |
| 247 #test.invoker: this, | 278 #test.invoker: this, |
| 248 // Use the invoker as a key so that multiple invokers can have different | 279 // Use the invoker as a key so that multiple invokers can have different |
| 249 // outstanding callback counters at once. | 280 // outstanding callback counters at once. |
| 250 this: outstandingCallbacksForBody | 281 _counterKey: outstandingCallbacksForBody, |
| 282 _closableKey: true |
| 251 }, | 283 }, |
| 252 zoneSpecification: new ZoneSpecification( | 284 zoneSpecification: new ZoneSpecification( |
| 253 print: (self, parent, zone, line) => _controller.print(line)), | 285 print: (self, parent, zone, line) => _controller.print(line)), |
| 254 onError: _handleError); | 286 onError: _handleError); |
| 255 }); | 287 }); |
| 256 } | 288 } |
| 257 } | 289 } |
| OLD | NEW |