Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(463)

Side by Side Diff: lib/src/backend/invoker.dart

Issue 1580243002: Wait for a timed-out test's tear-down logic. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « CHANGELOG.md ('k') | lib/src/frontend/timeout.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 LocalTest get _test => liveTest.test as LocalTest; 85 LocalTest get _test => liveTest.test as LocalTest;
86 86
87 /// The outstanding callback counter for the current zone. 87 /// The outstanding callback counter for the current zone.
88 OutstandingCallbackCounter get _outstandingCallbacks { 88 OutstandingCallbackCounter get _outstandingCallbacks {
89 var counter = Zone.current[_counterKey]; 89 var counter = Zone.current[_counterKey];
90 if (counter != null) return counter; 90 if (counter != null) return counter;
91 throw new StateError("Can't add or remove outstanding callbacks outside " 91 throw new StateError("Can't add or remove outstanding callbacks outside "
92 "of a test body."); 92 "of a test body.");
93 } 93 }
94 94
95 /// All the zones created by [waitForOutstandingCallbacks], in the order they
96 /// were created.
97 ///
98 /// This is used to throw timeout errors in the most recent zone.
99 final _outstandingCallbackZones = <Zone>[];
100
95 /// An opaque object used as a key in the zone value map to identify 101 /// An opaque object used as a key in the zone value map to identify
96 /// [_outstandingCallbacks]. 102 /// [_outstandingCallbacks].
97 /// 103 ///
98 /// This is an instance variable to ensure that multiple invokers don't step 104 /// This is an instance variable to ensure that multiple invokers don't step
99 /// on one anothers' toes. 105 /// on one anothers' toes.
100 final _counterKey = new Object(); 106 final _counterKey = new Object();
101 107
102 /// The current invoker, or `null` if none is defined. 108 /// The current invoker, or `null` if none is defined.
103 /// 109 ///
104 /// An invoker is only set within the zone scope of a running test. 110 /// An invoker is only set within the zone scope of a running test.
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 /// 163 ///
158 /// If [fn] itself returns a future, this will automatically wait until that 164 /// If [fn] itself returns a future, this will automatically wait until that
159 /// future completes as well. Note that outstanding callbacks registered 165 /// future completes as well. Note that outstanding callbacks registered
160 /// within [fn] will *not* be registered as outstanding callback outside of 166 /// within [fn] will *not* be registered as outstanding callback outside of
161 /// [fn]. 167 /// [fn].
162 /// 168 ///
163 /// If [fn] produces an unhandled error, this marks the current test as 169 /// If [fn] produces an unhandled error, this marks the current test as
164 /// failed, removes all outstanding callbacks registered within [fn], and 170 /// failed, removes all outstanding callbacks registered within [fn], and
165 /// completes the returned future. It does not remove any outstanding 171 /// completes the returned future. It does not remove any outstanding
166 /// callbacks registered outside of [fn]. 172 /// callbacks registered outside of [fn].
173 ///
174 /// If the test times out, the *most recent* call to
175 /// [waitForOutstandingCallbacks] will treat that error as occurring within
176 /// [fn]—that is, it will complete immediately.
167 Future waitForOutstandingCallbacks(fn()) { 177 Future waitForOutstandingCallbacks(fn()) {
168 heartbeat(); 178 heartbeat();
169 179
180 var zone;
170 var counter = new OutstandingCallbackCounter(); 181 var counter = new OutstandingCallbackCounter();
171 runZoned(() { 182 runZoned(() {
172 // TODO(nweiz): Use async/await here once issue 23497 has been fixed in 183 // TODO(nweiz): Use async/await here once issue 23497 has been fixed in
173 // two stable versions. 184 // two stable versions.
174 runZoned(() { 185 runZoned(() {
186 zone = Zone.current;
187 _outstandingCallbackZones.add(zone);
175 new Future.sync(fn).then((_) => counter.removeOutstandingCallback()); 188 new Future.sync(fn).then((_) => counter.removeOutstandingCallback());
176 }, onError: _handleError); 189 }, onError: _handleError);
177 }, zoneValues: { 190 }, zoneValues: {
178 _counterKey: counter 191 _counterKey: counter
179 }); 192 });
180 193
181 return counter.noOutstandingCallbacks; 194 return counter.noOutstandingCallbacks.whenComplete(() {
195 _outstandingCallbackZones.remove(zone);
196 });
182 } 197 }
183 198
184 /// Runs [fn] in a zone where [closed] is always `false`. 199 /// Runs [fn] in a zone where [closed] is always `false`.
185 /// 200 ///
186 /// This is useful for running code that should be able to register callbacks 201 /// This is useful for running code that should be able to register callbacks
187 /// and interact with the test framework normally even when the invoker is 202 /// and interact with the test framework normally even when the invoker is
188 /// closed, for example cleanup code. 203 /// closed, for example cleanup code.
189 unclosable(fn()) { 204 unclosable(fn()) {
190 heartbeat(); 205 heartbeat();
191 206
192 return runZoned(fn, zoneValues: { 207 return runZoned(fn, zoneValues: {
193 _closableKey: false 208 _closableKey: false
194 }); 209 });
195 } 210 }
196 211
197 /// Notifies the invoker that progress is being made. 212 /// Notifies the invoker that progress is being made.
198 /// 213 ///
199 /// Each heartbeat resets the timeout timer. This helps ensure that 214 /// Each heartbeat resets the timeout timer. This helps ensure that
200 /// long-running tests that still make progress don't time out. 215 /// long-running tests that still make progress don't time out.
201 void heartbeat() { 216 void heartbeat() {
202 if (liveTest.isComplete) return; 217 if (liveTest.isComplete) return;
203 if (_timeoutTimer != null) _timeoutTimer.cancel(); 218 if (_timeoutTimer != null) _timeoutTimer.cancel();
204 219
205 var timeout = liveTest.test.metadata.timeout 220 var timeout = liveTest.test.metadata.timeout
206 .apply(new Duration(seconds: 30)); 221 .apply(new Duration(seconds: 30));
207 if (timeout == null) return; 222 if (timeout == null) return;
208 _timeoutTimer = _invokerZone.createTimer(timeout, 223 _timeoutTimer = _invokerZone.createTimer(timeout, () {
209 Zone.current.bindCallback(() { 224 _outstandingCallbackZones.last.run(() {
210 if (liveTest.isComplete) return; 225 if (liveTest.isComplete) return;
211 _handleError( 226 _handleError(
212 new TimeoutException( 227 new TimeoutException(
213 "Test timed out after ${niceDuration(timeout)}.", timeout)); 228 "Test timed out after ${niceDuration(timeout)}.", timeout));
214 })); 229 });
230 });
215 } 231 }
216 232
217 /// Notifies the invoker of an asynchronous error. 233 /// Notifies the invoker of an asynchronous error.
218 void _handleError(error, [StackTrace stackTrace]) { 234 void _handleError(error, [StackTrace stackTrace]) {
219 if (stackTrace == null) stackTrace = new Chain.current(); 235 if (stackTrace == null) stackTrace = new Chain.current();
220 236
221 var afterSuccess = liveTest.isComplete && 237 var afterSuccess = liveTest.isComplete &&
222 liveTest.state.result == Result.success; 238 liveTest.state.result == Result.success;
223 239
224 if (error is! TestFailure) { 240 if (error is! TestFailure) {
(...skipping 19 matching lines...) Expand all
244 void _onRun() { 260 void _onRun() {
245 _controller.setState(const State(Status.running, Result.success)); 261 _controller.setState(const State(Status.running, Result.success));
246 262
247 var outstandingCallbacksForBody = new OutstandingCallbackCounter(); 263 var outstandingCallbacksForBody = new OutstandingCallbackCounter();
248 264
249 // TODO(nweiz): Use async/await here once issue 23497 has been fixed in two 265 // TODO(nweiz): Use async/await here once issue 23497 has been fixed in two
250 // stable versions. 266 // stable versions.
251 Chain.capture(() { 267 Chain.capture(() {
252 runZonedWithValues(() { 268 runZonedWithValues(() {
253 _invokerZone = Zone.current; 269 _invokerZone = Zone.current;
254 270 _outstandingCallbackZones.add(Zone.current);
255 heartbeat();
256 271
257 // Run the test asynchronously so that the "running" state change has 272 // Run the test asynchronously so that the "running" state change has
258 // a chance to hit its event handler(s) before the test produces an 273 // a chance to hit its event handler(s) before the test produces an
259 // error. If an error is emitted before the first state change is 274 // error. If an error is emitted before the first state change is
260 // handled, we can end up with [onError] callbacks firing before the 275 // handled, we can end up with [onError] callbacks firing before the
261 // corresponding [onStateChange], which violates the timing 276 // corresponding [onStateChange], which violates the timing
262 // guarantees. 277 // guarantees.
263 new Future(_test._body) 278 new Future(_test._body)
264 .then((_) => removeOutstandingCallback()); 279 .then((_) => removeOutstandingCallback());
265 280
(...skipping 12 matching lines...) Expand all
278 // outstanding callback counters at once. 293 // outstanding callback counters at once.
279 _counterKey: outstandingCallbacksForBody, 294 _counterKey: outstandingCallbacksForBody,
280 _closableKey: true 295 _closableKey: true
281 }, 296 },
282 zoneSpecification: new ZoneSpecification( 297 zoneSpecification: new ZoneSpecification(
283 print: (self, parent, zone, line) => _controller.print(line)), 298 print: (self, parent, zone, line) => _controller.print(line)),
284 onError: _handleError); 299 onError: _handleError);
285 }); 300 });
286 } 301 }
287 } 302 }
OLDNEW
« no previous file with comments | « CHANGELOG.md ('k') | lib/src/frontend/timeout.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698