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 |