OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 schedule; | 5 library schedule; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:collection'; | 8 import 'dart:collection'; |
9 | 9 |
10 import 'package:stack_trace/stack_trace.dart'; | 10 import 'package:stack_trace/stack_trace.dart'; |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
104 Duration get timeout => _timeout; | 104 Duration get timeout => _timeout; |
105 Duration _timeout = new Duration(seconds: 5); | 105 Duration _timeout = new Duration(seconds: 5); |
106 set timeout(Duration duration) { | 106 set timeout(Duration duration) { |
107 _timeout = duration; | 107 _timeout = duration; |
108 heartbeat(); | 108 heartbeat(); |
109 } | 109 } |
110 | 110 |
111 /// The timer for keeping track of task timeouts. This may be null. | 111 /// The timer for keeping track of task timeouts. This may be null. |
112 Timer _timeoutTimer; | 112 Timer _timeoutTimer; |
113 | 113 |
114 /// If `true`, then new [Task]s will capture the current stack trace before | |
115 /// running. This can be set to `false` to speed up running tests since | |
116 /// capturing stack traces is currently quite slow. Even when set to `false`, | |
117 /// stack traces from *thrown* exceptions will be caught. This only disables | |
118 /// the eager collection of stack traces *before* an error occurs. Defaults | |
119 /// to `true`. | |
120 bool captureStackTraces = true; | |
121 | |
122 /// Creates a new schedule with empty task queues. | 114 /// Creates a new schedule with empty task queues. |
123 Schedule() { | 115 Schedule() { |
124 _tasks = new TaskQueue._("tasks", this); | 116 _tasks = new TaskQueue._("tasks", this); |
125 _onComplete = new TaskQueue._("onComplete", this); | 117 _onComplete = new TaskQueue._("onComplete", this); |
126 _onException = new TaskQueue._("onException", this); | 118 _onException = new TaskQueue._("onException", this); |
127 _currentQueue = _tasks; | 119 _currentQueue = _tasks; |
128 | 120 |
129 heartbeat(); | 121 heartbeat(); |
130 } | 122 } |
131 | 123 |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
347 | 339 |
348 /// A queue of asynchronous tasks to execute in order. | 340 /// A queue of asynchronous tasks to execute in order. |
349 class TaskQueue { | 341 class TaskQueue { |
350 /// The tasks in the queue. | 342 /// The tasks in the queue. |
351 List<Task> get contents => new UnmodifiableListView<Task>(_contents); | 343 List<Task> get contents => new UnmodifiableListView<Task>(_contents); |
352 final _contents = new Queue<Task>(); | 344 final _contents = new Queue<Task>(); |
353 | 345 |
354 /// The name of the queue, for debugging purposes. | 346 /// The name of the queue, for debugging purposes. |
355 final String name; | 347 final String name; |
356 | 348 |
357 /// If `true`, then new [Task]s in this queue will capture the current stack | |
358 /// trace before running. | |
359 bool get captureStackTraces => _schedule.captureStackTraces; | |
360 | |
361 /// The [Schedule] that created this queue. | 349 /// The [Schedule] that created this queue. |
362 final Schedule _schedule; | 350 final Schedule _schedule; |
363 | 351 |
364 /// An out-of-band error signaled by [_schedule]. If this is non-null, it | 352 /// An out-of-band error signaled by [_schedule]. If this is non-null, it |
365 /// indicates that the queue should stop as soon as possible and re-throw this | 353 /// indicates that the queue should stop as soon as possible and re-throw this |
366 /// error. | 354 /// error. |
367 ScheduleError _error; | 355 ScheduleError _error; |
368 | 356 |
369 /// The [SubstituteFuture] for the currently-running task in the queue, or | 357 /// The [SubstituteFuture] for the currently-running task in the queue, or |
370 /// null if no task is currently running. | 358 /// null if no task is currently running. |
371 SubstituteFuture _taskFuture; | 359 SubstituteFuture _taskFuture; |
372 | 360 |
373 /// The toal number of out-of-band callbacks that have been registered on | 361 /// The toal number of out-of-band callbacks that have been registered on |
374 /// [this]. | 362 /// [this]. |
375 int _totalCallbacks = 0; | 363 int _totalCallbacks = 0; |
376 | 364 |
377 /// Whether to stop running after the current task. | 365 /// Whether to stop running after the current task. |
378 bool _aborted = false; | 366 bool _aborted = false; |
379 | 367 |
380 /// The descriptions of all callbacks that are blocking the completion of | 368 /// The descriptions of all callbacks that are blocking the completion of |
381 /// [this]. | 369 /// [this]. |
382 List<String> get pendingCallbacks => | 370 List<PendingCallback> get pendingCallbacks => |
383 new UnmodifiableListView<String>(_pendingCallbacks); | 371 new UnmodifiableListView<PendingCallback>(_pendingCallbacks); |
384 final _pendingCallbacks = new Queue<String>(); | 372 final _pendingCallbacks = new Queue<PendingCallback>(); |
385 | 373 |
386 /// A completer that will be completed once [_pendingCallbacks] becomes empty | 374 /// A completer that will be completed once [_pendingCallbacks] becomes empty |
387 /// after the queue finishes running its tasks. | 375 /// after the queue finishes running its tasks. |
388 Future get _noPendingCallbacks => _noPendingCallbacksCompleter.future; | 376 Future get _noPendingCallbacks => _noPendingCallbacksCompleter.future; |
389 final Completer _noPendingCallbacksCompleter = new Completer(); | 377 final Completer _noPendingCallbacksCompleter = new Completer(); |
390 | 378 |
391 /// A [Future] that completes when the tasks in [this] are all complete. If an | 379 /// A [Future] that completes when the tasks in [this] are all complete. If an |
392 /// error occurs while running this queue, the returned [Future] will complete | 380 /// error occurs while running this queue, the returned [Future] will complete |
393 /// with that error. | 381 /// with that error. |
394 /// | 382 /// |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 /// chain. This will also block [this] from completing until the returned | 486 /// chain. This will also block [this] from completing until the returned |
499 /// function has been called. It's used to ensure that out-of-band callbacks | 487 /// function has been called. It's used to ensure that out-of-band callbacks |
500 /// are properly handled by the scheduled test. | 488 /// are properly handled by the scheduled test. |
501 Function _wrapAsync(fn(arg), String description) { | 489 Function _wrapAsync(fn(arg), String description) { |
502 assert(_schedule.state == ScheduleState.SET_UP || isRunning); | 490 assert(_schedule.state == ScheduleState.SET_UP || isRunning); |
503 | 491 |
504 // It's possible that the queue timed out before [fn] finished. | 492 // It's possible that the queue timed out before [fn] finished. |
505 bool _timedOut() => | 493 bool _timedOut() => |
506 _schedule.currentQueue != this || pendingCallbacks.isEmpty; | 494 _schedule.currentQueue != this || pendingCallbacks.isEmpty; |
507 | 495 |
508 if (description == null) { | 496 _totalCallbacks++; |
509 description = "Out-of-band operation #${_totalCallbacks}"; | 497 var trace = new Trace.current(); |
510 } | 498 var pendingCallback = new PendingCallback._(() { |
| 499 var fullDescription = description; |
| 500 if (fullDescription == null) { |
| 501 fullDescription = "Out-of-band operation #${_totalCallbacks}"; |
| 502 } |
511 | 503 |
512 if (captureStackTraces) { | 504 var stackString = prefixLines(terseTraceString(trace)); |
513 var stackString = prefixLines(terseTraceString(new Trace.current())); | 505 fullDescription += "\n\nStack trace:\n$stackString"; |
514 description += "\n\nStack trace:\n$stackString"; | 506 }); |
515 } | 507 _pendingCallbacks.add(pendingCallback); |
516 | 508 |
517 _totalCallbacks++; | |
518 | |
519 _pendingCallbacks.add(description); | |
520 return (arg) { | 509 return (arg) { |
521 try { | 510 try { |
522 return fn(arg); | 511 return fn(arg); |
523 } catch (e, stackTrace) { | 512 } catch (e, stackTrace) { |
524 var error = new ScheduleError.from( | 513 var error = new ScheduleError.from( |
525 _schedule, e, stackTrace: stackTrace); | 514 _schedule, e, stackTrace: stackTrace); |
526 if (_timedOut()) { | 515 if (_timedOut()) { |
527 _schedule._signalPostTimeoutError(error); | 516 _schedule._signalPostTimeoutError(error); |
528 } else { | 517 } else { |
529 _schedule.signalError(error); | 518 _schedule.signalError(error); |
530 } | 519 } |
531 } finally { | 520 } finally { |
532 if (_timedOut()) return; | 521 if (_timedOut()) return; |
533 | 522 |
534 _pendingCallbacks.remove(description); | 523 _pendingCallbacks.remove(pendingCallback); |
535 if (_pendingCallbacks.isEmpty && !isRunningTasks) { | 524 if (_pendingCallbacks.isEmpty && !isRunningTasks) { |
536 _noPendingCallbacksCompleter.complete(); | 525 _noPendingCallbacksCompleter.complete(); |
537 } | 526 } |
538 } | 527 } |
539 }; | 528 }; |
540 } | 529 } |
541 | 530 |
542 /// Signals that an out-of-band error has been detected and the queue should | 531 /// Signals that an out-of-band error has been detected and the queue should |
543 /// stop running as soon as possible. | 532 /// stop running as soon as possible. |
544 void _signalError(ScheduleError error) { | 533 void _signalError(ScheduleError error) { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
598 return prefixLines(childString, | 587 return prefixLines(childString, |
599 firstPrefix: " $prefix ", prefix: " | "); | 588 firstPrefix: " $prefix ", prefix: " | "); |
600 }).join('\n'); | 589 }).join('\n'); |
601 taskString = '$taskString\n$childrenString'; | 590 taskString = '$taskString\n$childrenString'; |
602 } | 591 } |
603 | 592 |
604 return taskString; | 593 return taskString; |
605 }).join("\n"); | 594 }).join("\n"); |
606 } | 595 } |
607 } | 596 } |
| 597 |
| 598 /// A thunk for lazily resolving the description of a [PendingCallback]. |
| 599 typedef String _DescriptionThunk(); |
| 600 |
| 601 /// An identifier for an out-of-band callback running during a schedule. |
| 602 class PendingCallback { |
| 603 final _DescriptionThunk _thunk; |
| 604 String _description; |
| 605 |
| 606 /// The string description of the callback. |
| 607 String get description { |
| 608 if (_description == null) _description = _thunk(); |
| 609 return _description; |
| 610 } |
| 611 |
| 612 String toString() => description; |
| 613 |
| 614 PendingCallback._(this._thunk); |
| 615 } |
OLD | NEW |