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 |
11 import '../frontend/expect.dart'; | 11 import '../frontend/expect.dart'; |
12 import '../utils.dart'; | 12 import '../utils.dart'; |
13 import 'closed_exception.dart'; | 13 import 'closed_exception.dart'; |
14 import 'live_test.dart'; | 14 import 'live_test.dart'; |
15 import 'live_test_controller.dart'; | 15 import 'live_test_controller.dart'; |
16 import 'metadata.dart'; | 16 import 'metadata.dart'; |
17 import 'outstanding_callback_counter.dart'; | 17 import 'outstanding_callback_counter.dart'; |
18 import 'state.dart'; | 18 import 'state.dart'; |
19 import 'suite.dart'; | 19 import 'suite.dart'; |
20 import 'test.dart'; | 20 import 'test.dart'; |
21 | 21 |
22 /// A test in this isolate. | 22 /// A test in this isolate. |
23 class LocalTest implements Test { | 23 class LocalTest implements Test { |
24 final String name; | 24 final String name; |
25 final Metadata metadata; | 25 final Metadata metadata; |
26 | 26 |
27 /// The test body. | 27 /// The test body. |
28 final AsyncFunction _body; | 28 final AsyncFunction _body; |
29 | 29 |
30 /// The callback used to clean up after the test. | 30 LocalTest(this.name, this.metadata, body()) |
31 /// | 31 : _body = body; |
32 /// This is separated out from [_body] because it needs to run once the test's | |
33 /// asynchronous computation has finished, even if that's different from the | |
34 /// completion of the main body of the test. | |
35 final AsyncFunction _tearDown; | |
36 | |
37 LocalTest(this.name, this.metadata, body(), {tearDown()}) | |
38 : _body = body, | |
39 _tearDown = tearDown; | |
40 | 32 |
41 /// Loads a single runnable instance of this test. | 33 /// Loads a single runnable instance of this test. |
42 LiveTest load(Suite suite) { | 34 LiveTest load(Suite suite) { |
43 var invoker = new Invoker._(suite, this); | 35 var invoker = new Invoker._(suite, this); |
44 return invoker.liveTest; | 36 return invoker.liveTest; |
45 } | 37 } |
46 | 38 |
47 Test change({String name, Metadata metadata}) { | 39 Test change({String name, Metadata metadata}) { |
48 if (name == name && metadata == this.metadata) return this; | 40 if (name == name && metadata == this.metadata) return this; |
49 if (name == null) name = this.name; | 41 if (name == null) name = this.name; |
50 if (metadata == null) metadata = this.metadata; | 42 if (metadata == null) metadata = this.metadata; |
51 return new LocalTest(name, metadata, _body, tearDown: _tearDown); | 43 return new LocalTest(name, metadata, _body); |
52 } | 44 } |
53 } | 45 } |
54 | 46 |
55 /// The class responsible for managing the lifecycle of a single local test. | 47 /// The class responsible for managing the lifecycle of a single local test. |
56 /// | 48 /// |
57 /// 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 |
58 /// using [Invoker.current]. It's used to track asynchronous callbacks and | 50 /// using [Invoker.current]. It's used to track asynchronous callbacks and |
59 /// report asynchronous errors. | 51 /// report asynchronous errors. |
60 class Invoker { | 52 class Invoker { |
61 /// The live test being driven by the invoker. | 53 /// The live test being driven by the invoker. |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
140 /// | 132 /// |
141 /// Future calls to [addOutstandingCallback] and [removeOutstandingCallback] | 133 /// Future calls to [addOutstandingCallback] and [removeOutstandingCallback] |
142 /// will be ignored. | 134 /// will be ignored. |
143 void removeAllOutstandingCallbacks() => | 135 void removeAllOutstandingCallbacks() => |
144 _outstandingCallbacks.removeAllOutstandingCallbacks(); | 136 _outstandingCallbacks.removeAllOutstandingCallbacks(); |
145 | 137 |
146 /// Runs [fn] and returns once all (registered) outstanding callbacks it | 138 /// Runs [fn] and returns once all (registered) outstanding callbacks it |
147 /// transitively invokes have completed. | 139 /// transitively invokes have completed. |
148 /// | 140 /// |
149 /// If [fn] itself returns a future, this will automatically wait until that | 141 /// If [fn] itself returns a future, this will automatically wait until that |
150 /// future completes as well. | 142 /// future completes as well. Note that outstanding callbacks registered |
143 /// within [fn] will *not* be registered as outstanding callback outside of | |
144 /// [fn]. | |
151 /// | 145 /// |
152 /// Note that outstanding callbacks registered within [fn] will *not* be | 146 /// If [fn] produces an unhandled error, this marks the current test as |
153 /// registered as outstanding callback outside of [fn]. | 147 /// failed, removes all outstanding callbacks registered within [fn], and the |
148 /// completes returned future. It does not remove any outstanding callbacks | |
Bob Nystrom
2015/09/24 16:05:02
"and the completes returned future" no has grammar
nweiz
2015/09/24 19:56:20
Done.
| |
149 /// registered outside of [fn]. | |
154 Future waitForOutstandingCallbacks(fn()) { | 150 Future waitForOutstandingCallbacks(fn()) { |
155 heartbeat(); | 151 heartbeat(); |
156 | 152 |
157 var counter = new OutstandingCallbackCounter(); | 153 var counter = new OutstandingCallbackCounter(); |
158 runZoned(() { | 154 runZoned(() { |
159 // TODO(nweiz): Use async/await here once issue 23497 has been fixed in | 155 // TODO(nweiz): Use async/await here once issue 23497 has been fixed in |
160 // two stable versions. | 156 // two stable versions. |
161 new Future.sync(fn).then((_) => counter.removeOutstandingCallback()); | 157 runZoned(() { |
158 new Future.sync(fn).then((_) => counter.removeOutstandingCallback()); | |
159 }, onError: _handleError); | |
162 }, zoneValues: { | 160 }, zoneValues: { |
163 // Use the invoker as a key so that multiple invokers can have different | 161 // Use the invoker as a key so that multiple invokers can have different |
164 // outstanding callback counters at once. | 162 // outstanding callback counters at once. |
165 this: counter | 163 this: counter |
166 }); | 164 }); |
167 | 165 |
168 return counter.noOutstandingCallbacks; | 166 return counter.noOutstandingCallbacks; |
169 } | 167 } |
170 | 168 |
171 /// Notifies the invoker that progress is being made. | 169 /// Notifies the invoker that progress is being made. |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
230 // Run the test asynchronously so that the "running" state change has | 228 // Run the test asynchronously so that the "running" state change has |
231 // a chance to hit its event handler(s) before the test produces an | 229 // a chance to hit its event handler(s) before the test produces an |
232 // error. If an error is emitted before the first state change is | 230 // error. If an error is emitted before the first state change is |
233 // handled, we can end up with [onError] callbacks firing before the | 231 // handled, we can end up with [onError] callbacks firing before the |
234 // corresponding [onStateChange], which violates the timing | 232 // corresponding [onStateChange], which violates the timing |
235 // guarantees. | 233 // guarantees. |
236 new Future(_test._body) | 234 new Future(_test._body) |
237 .then((_) => removeOutstandingCallback()); | 235 .then((_) => removeOutstandingCallback()); |
238 | 236 |
239 _outstandingCallbacks.noOutstandingCallbacks.then((_) { | 237 _outstandingCallbacks.noOutstandingCallbacks.then((_) { |
240 if (_test._tearDown == null) return null; | |
241 | |
242 // Reset the outstanding callback counter to wait for callbacks from | |
243 // the test's `tearDown` to complete. | |
244 return waitForOutstandingCallbacks(() => | |
245 runZoned(_test._tearDown, onError: _handleError)); | |
246 }).then((_) { | |
247 if (_timeoutTimer != null) _timeoutTimer.cancel(); | 238 if (_timeoutTimer != null) _timeoutTimer.cancel(); |
248 _controller.setState( | 239 _controller.setState( |
249 new State(Status.complete, liveTest.state.result)); | 240 new State(Status.complete, liveTest.state.result)); |
250 | 241 |
251 // Use [Timer.run] here to avoid starving the DOM or other | 242 // Use [Timer.run] here to avoid starving the DOM or other |
252 // non-microtask events. | 243 // non-microtask events. |
253 Timer.run(_controller.completer.complete); | 244 Timer.run(_controller.completer.complete); |
254 }); | 245 }); |
255 }, zoneValues: { | 246 }, zoneValues: { |
256 #test.invoker: this, | 247 #test.invoker: this, |
257 // Use the invoker as a key so that multiple invokers can have different | 248 // Use the invoker as a key so that multiple invokers can have different |
258 // outstanding callback counters at once. | 249 // outstanding callback counters at once. |
259 this: outstandingCallbacksForBody | 250 this: outstandingCallbacksForBody |
260 }, | 251 }, |
261 zoneSpecification: new ZoneSpecification( | 252 zoneSpecification: new ZoneSpecification( |
262 print: (self, parent, zone, line) => _controller.print(line)), | 253 print: (self, parent, zone, line) => _controller.print(line)), |
263 onError: _handleError); | 254 onError: _handleError); |
264 }); | 255 }); |
265 } | 256 } |
266 } | 257 } |
OLD | NEW |