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 : new Completer().future; | |
kevmoo
2015/10/01 02:21:23
Wha? Please explain...
nweiz
2015/10/01 21:16:27
This is just a future that will never fire.
| |
68 final _onCloseCompleter = new Completer(); | 79 final _onCloseCompleter = new Completer(); |
69 | 80 |
70 /// The test being run. | 81 /// The test being run. |
71 LocalTest get _test => liveTest.test as LocalTest; | 82 LocalTest get _test => liveTest.test as LocalTest; |
72 | 83 |
73 /// The test metadata merged with the suite metadata. | 84 /// The test metadata merged with the suite metadata. |
74 final Metadata metadata; | 85 final Metadata metadata; |
75 | 86 |
76 /// The outstanding callback counter for the current zone. | 87 /// The outstanding callback counter for the current zone. |
77 OutstandingCallbackCounter get _outstandingCallbacks { | 88 OutstandingCallbackCounter get _outstandingCallbacks { |
78 var counter = Zone.current[this]; | 89 var counter = Zone.current[_counterKey]; |
79 if (counter != null) return counter; | 90 if (counter != null) return counter; |
80 throw new StateError("Can't add or remove outstanding callbacks outside " | 91 throw new StateError("Can't add or remove outstanding callbacks outside " |
81 "of a test body."); | 92 "of a test body."); |
82 } | 93 } |
83 | 94 |
95 /// An opaque object used as a key in the zone value map to identify | |
96 /// [_outstandingCallbacks]. | |
97 /// | |
98 /// This is an instance variable to ensure that multiple invokers don't step | |
99 /// on one anothers' toes. | |
100 final _counterKey = new Object(); | |
101 | |
84 /// The current invoker, or `null` if none is defined. | 102 /// The current invoker, or `null` if none is defined. |
85 /// | 103 /// |
86 /// An invoker is only set within the zone scope of a running test. | 104 /// An invoker is only set within the zone scope of a running test. |
87 static Invoker get current { | 105 static Invoker get current { |
88 // TODO(nweiz): Use a private symbol when dart2js supports it (issue 17526). | 106 // TODO(nweiz): Use a private symbol when dart2js supports it (issue 17526). |
89 return Zone.current[#test.invoker]; | 107 return Zone.current[#test.invoker]; |
90 } | 108 } |
91 | 109 |
92 /// The zone that the top level of [_test.body] is running in. | 110 /// The zone that the top level of [_test.body] is running in. |
93 /// | 111 /// |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
151 heartbeat(); | 169 heartbeat(); |
152 | 170 |
153 var counter = new OutstandingCallbackCounter(); | 171 var counter = new OutstandingCallbackCounter(); |
154 runZoned(() { | 172 runZoned(() { |
155 // TODO(nweiz): Use async/await here once issue 23497 has been fixed in | 173 // TODO(nweiz): Use async/await here once issue 23497 has been fixed in |
156 // two stable versions. | 174 // two stable versions. |
157 runZoned(() { | 175 runZoned(() { |
158 new Future.sync(fn).then((_) => counter.removeOutstandingCallback()); | 176 new Future.sync(fn).then((_) => counter.removeOutstandingCallback()); |
159 }, onError: _handleError); | 177 }, onError: _handleError); |
160 }, zoneValues: { | 178 }, zoneValues: { |
161 // Use the invoker as a key so that multiple invokers can have different | 179 _counterKey: counter |
162 // outstanding callback counters at once. | |
163 this: counter | |
164 }); | 180 }); |
165 | 181 |
166 return counter.noOutstandingCallbacks; | 182 return counter.noOutstandingCallbacks; |
167 } | 183 } |
168 | 184 |
185 /// Runs [fn] in a zone where [closed] is always `false`. | |
186 /// | |
187 /// This is useful for running code that should be able to register callbacks | |
188 /// and interact with the test framework normally even when the invoker is | |
189 /// closed, for example cleanup code. | |
190 unclosable(fn()) { | |
191 heartbeat(); | |
192 | |
193 return runZoned(fn, zoneValues: { | |
194 _closableKey: false | |
195 }); | |
196 } | |
197 | |
169 /// Notifies the invoker that progress is being made. | 198 /// Notifies the invoker that progress is being made. |
170 /// | 199 /// |
171 /// Each heartbeat resets the timeout timer. This helps ensure that | 200 /// Each heartbeat resets the timeout timer. This helps ensure that |
172 /// long-running tests that still make progress don't time out. | 201 /// long-running tests that still make progress don't time out. |
173 void heartbeat() { | 202 void heartbeat() { |
174 if (liveTest.isComplete) return; | 203 if (liveTest.isComplete) return; |
175 if (_timeoutTimer != null) _timeoutTimer.cancel(); | 204 if (_timeoutTimer != null) _timeoutTimer.cancel(); |
176 | 205 |
177 var timeout = metadata.timeout.apply(new Duration(seconds: 30)); | 206 var timeout = metadata.timeout.apply(new Duration(seconds: 30)); |
178 if (timeout == null) return; | 207 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)); | 269 new State(Status.complete, liveTest.state.result)); |
241 | 270 |
242 // Use [Timer.run] here to avoid starving the DOM or other | 271 // Use [Timer.run] here to avoid starving the DOM or other |
243 // non-microtask events. | 272 // non-microtask events. |
244 Timer.run(_controller.completer.complete); | 273 Timer.run(_controller.completer.complete); |
245 }); | 274 }); |
246 }, zoneValues: { | 275 }, zoneValues: { |
247 #test.invoker: this, | 276 #test.invoker: this, |
248 // Use the invoker as a key so that multiple invokers can have different | 277 // Use the invoker as a key so that multiple invokers can have different |
249 // outstanding callback counters at once. | 278 // outstanding callback counters at once. |
250 this: outstandingCallbacksForBody | 279 _counterKey: outstandingCallbacksForBody, |
280 _closableKey: true | |
251 }, | 281 }, |
252 zoneSpecification: new ZoneSpecification( | 282 zoneSpecification: new ZoneSpecification( |
253 print: (self, parent, zone, line) => _controller.print(line)), | 283 print: (self, parent, zone, line) => _controller.print(line)), |
254 onError: _handleError); | 284 onError: _handleError); |
255 }); | 285 }); |
256 } | 286 } |
257 } | 287 } |
OLD | NEW |