| 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 'live_test.dart'; | 14 import 'live_test.dart'; |
| 14 import 'live_test_controller.dart'; | 15 import 'live_test_controller.dart'; |
| 15 import 'metadata.dart'; | 16 import 'metadata.dart'; |
| 16 import 'state.dart'; | 17 import 'state.dart'; |
| 17 import 'suite.dart'; | 18 import 'suite.dart'; |
| 18 import 'test.dart'; | 19 import 'test.dart'; |
| 19 | 20 |
| 20 /// A test in this isolate. | 21 /// A test in this isolate. |
| 21 class LocalTest implements Test { | 22 class LocalTest implements Test { |
| 22 final String name; | 23 final String name; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 48 /// 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 |
| 49 /// using [Invoker.current]. It's used to track asynchronous callbacks and | 50 /// using [Invoker.current]. It's used to track asynchronous callbacks and |
| 50 /// report asynchronous errors. | 51 /// report asynchronous errors. |
| 51 class Invoker { | 52 class Invoker { |
| 52 /// The live test being driven by the invoker. | 53 /// The live test being driven by the invoker. |
| 53 /// | 54 /// |
| 54 /// 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. |
| 55 LiveTest get liveTest => _controller.liveTest; | 56 LiveTest get liveTest => _controller.liveTest; |
| 56 LiveTestController _controller; | 57 LiveTestController _controller; |
| 57 | 58 |
| 59 /// Whether the test has been closed. |
| 60 /// |
| 61 /// Once the test is closed, [expect] and [expectAsync] will throw |
| 62 /// [ClosedException]s whenever accessed to help the test stop executing as |
| 63 /// soon as possible. |
| 64 bool get closed => _closed; |
| 65 bool _closed = false; |
| 66 |
| 58 /// The test being run. | 67 /// The test being run. |
| 59 LocalTest get _test => liveTest.test as LocalTest; | 68 LocalTest get _test => liveTest.test as LocalTest; |
| 60 | 69 |
| 61 /// Note that this is meaningless once [_onCompleteCompleter] is complete. | 70 /// Note that this is meaningless once [_onCompleteCompleter] is complete. |
| 62 var _outstandingCallbacks = 0; | 71 var _outstandingCallbacks = 0; |
| 63 | 72 |
| 64 /// The completer to complete once the test body finishes. | 73 /// The completer to complete once the test body finishes. |
| 65 /// | 74 /// |
| 66 /// This is distinct from [_controller.completer] because a tear-down may need | 75 /// This is distinct from [_controller.completer] because a tear-down may need |
| 67 /// to run before the test is truly finished. | 76 /// to run before the test is truly finished. |
| 68 final _completer = new Completer(); | 77 final _completer = new Completer(); |
| 69 | 78 |
| 70 /// The current invoker, or `null` if none is defined. | 79 /// The current invoker, or `null` if none is defined. |
| 71 /// | 80 /// |
| 72 /// An invoker is only set within the zone scope of a running test. | 81 /// An invoker is only set within the zone scope of a running test. |
| 73 static Invoker get current { | 82 static Invoker get current { |
| 74 // TODO(nweiz): Use a private symbol when dart2js supports it (issue 17526). | 83 // TODO(nweiz): Use a private symbol when dart2js supports it (issue 17526). |
| 75 return Zone.current[#test.invoker]; | 84 return Zone.current[#test.invoker]; |
| 76 } | 85 } |
| 77 | 86 |
| 78 Invoker._(Suite suite, LocalTest test) { | 87 Invoker._(Suite suite, LocalTest test) { |
| 79 _controller = new LiveTestController(suite, test, _onRun); | 88 _controller = new LiveTestController(suite, test, _onRun, () { |
| 89 _closed = true; |
| 90 }); |
| 80 } | 91 } |
| 81 | 92 |
| 82 /// Tells the invoker that there's a callback running that it should wait for | 93 /// Tells the invoker that there's a callback running that it should wait for |
| 83 /// before considering the test successful. | 94 /// before considering the test successful. |
| 84 /// | 95 /// |
| 85 /// Each call to [addOutstandingCallback] should be followed by a call to | 96 /// Each call to [addOutstandingCallback] should be followed by a call to |
| 86 /// [removeOutstandingCallback] once the callbak is no longer running. Note | 97 /// [removeOutstandingCallback] once the callbak is no longer running. Note |
| 87 /// that only successful tests wait for outstanding callbacks; as soon as a | 98 /// that only successful tests wait for outstanding callbacks; as soon as a |
| 88 /// test experiences an error, any further calls to [addOutstandingCallback] | 99 /// test experiences an error, any further calls to [addOutstandingCallback] |
| 89 /// or [removeOutstandingCallback] will do nothing. | 100 /// or [removeOutstandingCallback] will do nothing. |
| 101 /// |
| 102 /// Throws a [ClosedException] if this test has been closed. |
| 90 void addOutstandingCallback() { | 103 void addOutstandingCallback() { |
| 104 if (closed) throw new ClosedException(); |
| 91 _outstandingCallbacks++; | 105 _outstandingCallbacks++; |
| 92 } | 106 } |
| 93 | 107 |
| 94 /// Tells the invoker that a callback declared with [addOutstandingCallback] | 108 /// Tells the invoker that a callback declared with [addOutstandingCallback] |
| 95 /// is no longer running. | 109 /// is no longer running. |
| 96 void removeOutstandingCallback() { | 110 void removeOutstandingCallback() { |
| 97 _outstandingCallbacks--; | 111 _outstandingCallbacks--; |
| 98 | 112 |
| 99 if (_outstandingCallbacks != 0) return; | 113 if (_outstandingCallbacks != 0) return; |
| 100 if (_completer.isCompleted) return; | 114 if (_completer.isCompleted) return; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 155 addOutstandingCallback(); | 169 addOutstandingCallback(); |
| 156 | 170 |
| 157 // Run the test asynchronously so that the "running" state change has a | 171 // Run the test asynchronously so that the "running" state change has a |
| 158 // chance to hit its event handler(s) before the test produces an error. | 172 // chance to hit its event handler(s) before the test produces an error. |
| 159 // If an error is emitted before the first state change is handled, we | 173 // If an error is emitted before the first state change is handled, we |
| 160 // can end up with [onError] callbacks firing before the corresponding | 174 // can end up with [onError] callbacks firing before the corresponding |
| 161 // [onStateChange], which violates the timing guarantees. | 175 // [onStateChange], which violates the timing guarantees. |
| 162 new Future(_test._body) | 176 new Future(_test._body) |
| 163 .then((_) => removeOutstandingCallback()); | 177 .then((_) => removeOutstandingCallback()); |
| 164 | 178 |
| 165 // Explicitly handle an error here so that we can return the [Future]. | |
| 166 // If a [Future] returned from an error zone would throw an error | |
| 167 // through the zone boundary, it instead never completes, and we want to | |
| 168 // avoid that. | |
| 169 _completer.future.then((_) { | 179 _completer.future.then((_) { |
| 170 if (_test._tearDown == null) return null; | 180 if (_test._tearDown == null) return null; |
| 171 return new Future.sync(_test._tearDown); | 181 return new Future.sync(_test._tearDown); |
| 172 }).catchError(Zone.current.handleUncaughtError).then((_) { | 182 }).catchError(Zone.current.handleUncaughtError).then((_) { |
| 173 timer.cancel(); | 183 timer.cancel(); |
| 174 _controller.setState( | 184 _controller.setState( |
| 175 new State(Status.complete, liveTest.state.result)); | 185 new State(Status.complete, liveTest.state.result)); |
| 176 | 186 |
| 177 // Use [Timer.run] here to avoid starving the DOM or other | 187 // Use [Timer.run] here to avoid starving the DOM or other |
| 178 // non-microtask events. | 188 // non-microtask events. |
| 179 Timer.run(_controller.completer.complete); | 189 Timer.run(_controller.completer.complete); |
| 180 }); | 190 }); |
| 181 }, | 191 }, |
| 182 zoneSpecification: new ZoneSpecification( | 192 zoneSpecification: new ZoneSpecification( |
| 183 print: (self, parent, zone, line) => _controller.print(line)), | 193 print: (self, parent, zone, line) => _controller.print(line)), |
| 184 zoneValues: {#test.invoker: this}, | 194 zoneValues: {#test.invoker: this}, |
| 185 onError: handleError); | 195 onError: handleError); |
| 186 }); | 196 }); |
| 187 } | 197 } |
| 188 } | 198 } |
| OLD | NEW |