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

Side by Side Diff: pkg/scheduled_test/lib/src/schedule.dart

Issue 12377093: Add a ScheduledProcess class to pkg/scheduled_test. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 9 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 | Annotate | Revision Log
OLDNEW
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:unittest/unittest.dart' as unittest; 10 import 'package:unittest/unittest.dart' as unittest;
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
53 /// such task. This will be `null` both before the schedule starts running and 53 /// such task. This will be `null` both before the schedule starts running and
54 /// after it's finished. 54 /// after it's finished.
55 Task get currentTask => _currentTask; 55 Task get currentTask => _currentTask;
56 Task _currentTask; 56 Task _currentTask;
57 57
58 /// The current state of the schedule. 58 /// The current state of the schedule.
59 ScheduleState get state => _state; 59 ScheduleState get state => _state;
60 ScheduleState _state = ScheduleState.SET_UP; 60 ScheduleState _state = ScheduleState.SET_UP;
61 61
62 // TODO(nweiz): make this a read-only view once issue 8321 is fixed. 62 // TODO(nweiz): make this a read-only view once issue 8321 is fixed.
63
64 /// Errors thrown by the task queues. 63 /// Errors thrown by the task queues.
65 /// 64 ///
66 /// When running tasks in [tasks], this will always be empty. If an error 65 /// When running tasks in [tasks], this will always be empty. If an error
67 /// occurs in [tasks], it will be added to this list and then [onException] 66 /// occurs in [tasks], it will be added to this list and then [onException]
68 /// will be run. If an error occurs there as well, it will be added to this 67 /// will be run. If an error occurs there as well, it will be added to this
69 /// list and [onComplete] will be run. Errors thrown during [onComplete] will 68 /// list and [onComplete] will be run. Errors thrown during [onComplete] will
70 /// also be added to this list, although no scheduled tasks will be run 69 /// also be added to this list, although no scheduled tasks will be run
71 /// afterwards. 70 /// afterwards.
72 /// 71 ///
73 /// Any out-of-band callbacks that throw errors will also have those errors 72 /// Any out-of-band callbacks that throw errors will also have those errors
74 /// added to this list. 73 /// added to this list.
75 final errors = <ScheduleError>[]; 74 final errors = <ScheduleError>[];
76 75
76 // TODO(nweiz): make this a read-only view once issue 8321 is fixed.
77 /// Additional debugging info registered via [addDebugInfo].
78 final debugInfo = <String>[];
79
77 /// The task queue that's currently being run. One of [tasks], [onException], 80 /// The task queue that's currently being run. One of [tasks], [onException],
78 /// or [onComplete]. This starts as [tasks], and can only be `null` after the 81 /// or [onComplete]. This starts as [tasks], and can only be `null` after the
79 /// schedule is done. 82 /// schedule is done.
80 TaskQueue get currentQueue => 83 TaskQueue get currentQueue =>
81 _state == ScheduleState.DONE ? null : _currentQueue; 84 _state == ScheduleState.DONE ? null : _currentQueue;
82 TaskQueue _currentQueue; 85 TaskQueue _currentQueue;
83 86
84 /// The time to wait before terminating a task queue for inactivity. Defaults 87 /// The time to wait before terminating a task queue for inactivity. Defaults
85 /// to 30 seconds. This can be set to `null` to disable timeouts entirely. 88 /// to 30 seconds. This can be set to `null` to disable timeouts entirely.
86 /// 89 ///
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 "${errorString()}"); 181 "${errorString()}");
179 } else if (state == ScheduleState.SET_UP) { 182 } else if (state == ScheduleState.SET_UP) {
180 // If we're setting up, throwing the error will pipe it into the main 183 // If we're setting up, throwing the error will pipe it into the main
181 // error-handling code. 184 // error-handling code.
182 throw scheduleError; 185 throw scheduleError;
183 } else { 186 } else {
184 _currentQueue._signalError(scheduleError); 187 _currentQueue._signalError(scheduleError);
185 } 188 }
186 } 189 }
187 190
191 /// Adds [info] to the debugging output that will be printed if the test
192 /// fails. Unlike [signalError], this won't cause the test to fail, nor will
193 /// it short-circuit the current [TaskQueue]; it's just useful for providing
194 /// additional information that may not fit cleanly into an existing error.
195 void addDebugInfo(String info) => debugInfo.add(info);
196
188 /// Notifies the schedule of an error that occurred in a task or out-of-band 197 /// Notifies the schedule of an error that occurred in a task or out-of-band
189 /// callback after the appropriate queue has timed out. If this schedule is 198 /// callback after the appropriate queue has timed out. If this schedule is
190 /// still running, the error will be added to the errors list to be shown 199 /// still running, the error will be added to the errors list to be shown
191 /// along with the timeout error; otherwise, a top-level error will be thrown. 200 /// along with the timeout error; otherwise, a top-level error will be thrown.
192 void _signalPostTimeoutError(error, [stackTrace]) { 201 void _signalPostTimeoutError(error, [stackTrace]) {
193 var scheduleError = new ScheduleError.from(this, error, 202 var scheduleError = new ScheduleError.from(this, error,
194 stackTrace: stackTrace); 203 stackTrace: stackTrace);
195 _addError(scheduleError); 204 _addError(scheduleError);
196 if (_state == ScheduleState.DONE) { 205 if (_state == ScheduleState.DONE) {
197 throw new StateError( 206 throw new StateError(
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 // Don't top-level the error, since it's already been signaled to the 274 // Don't top-level the error, since it's already been signaled to the
266 // schedule. 275 // schedule.
267 future.catchError((_) => null); 276 future.catchError((_) => null);
268 277
269 return future; 278 return future;
270 } 279 }
271 280
272 /// Returns a string representation of all errors registered on this schedule. 281 /// Returns a string representation of all errors registered on this schedule.
273 String errorString() { 282 String errorString() {
274 if (errors.isEmpty) return "The schedule had no errors."; 283 if (errors.isEmpty) return "The schedule had no errors.";
275 if (errors.length == 1) return errors.first.toString(); 284 if (errors.length == 1 && debugInfo.isEmpty) return errors.first.toString();
276 var errorStrings = errors.map((e) => e.toString()).join("\n================" 285
277 "================================================================\n"); 286 var border = "\n==========================================================="
278 return "The schedule had ${errors.length} errors:\n$errorStrings"; 287 "=====================\n";
288 var errorStrings = errors.map((e) => e.toString()).join(border);
289 var message = "The schedule had ${errors.length} errors:\n$errorStrings";
290
291 if (!debugInfo.isEmpty) {
292 message = "$message$border\nDebug info:\n${debugInfo.join(border)}";
293 }
294
295 return message;
279 } 296 }
280 297
281 /// Notifies the schedule that progress is being made on an asynchronous task. 298 /// Notifies the schedule that progress is being made on an asynchronous task.
282 /// This resets the timeout timer, and can be used in long-running tasks to 299 /// This resets the timeout timer, and can be used in long-running tasks to
283 /// keep them from timing out. 300 /// keep them from timing out.
284 void heartbeat() { 301 void heartbeat() {
285 if (_timeoutTimer != null) _timeoutTimer.cancel(); 302 if (_timeoutTimer != null) _timeoutTimer.cancel();
286 if (_timeout == null) { 303 if (_timeout == null) {
287 _timeoutTimer = null; 304 _timeoutTimer = null;
288 } else { 305 } else {
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 381
365 /// An out-of-band error signaled by [_schedule]. If this is non-null, it 382 /// An out-of-band error signaled by [_schedule]. If this is non-null, it
366 /// indicates that the queue should stop as soon as possible and re-throw this 383 /// indicates that the queue should stop as soon as possible and re-throw this
367 /// error. 384 /// error.
368 ScheduleError _error; 385 ScheduleError _error;
369 386
370 /// The [SubstituteFuture] for the currently-running task in the queue, or 387 /// The [SubstituteFuture] for the currently-running task in the queue, or
371 /// null if no task is currently running. 388 /// null if no task is currently running.
372 SubstituteFuture _taskFuture; 389 SubstituteFuture _taskFuture;
373 390
374 TaskQueue._(this.name, this._schedule); 391 /// 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
393 /// with that error.
394 ///
395 /// The returned [Future] can complete before outstanding out-of-band
396 /// callbacks have finished running.
397 Future get onTasksComplete => _onTasksCompleteCompleter.future;
398 final _onTasksCompleteCompleter = new Completer();
399
400 TaskQueue._(this.name, this._schedule) {
401 // Avoid top-leveling errors that are passed to onTasksComplete if there are no
Bob Nystrom 2013/03/04 23:52:00 Long line.
nweiz 2013/03/05 02:16:09 Done.
402 // listeners.
403 onTasksComplete.catchError((_) {});
404 }
375 405
376 /// Whether this queue is currently running. 406 /// Whether this queue is currently running.
377 bool get isRunning => _schedule.state == ScheduleState.RUNNING && 407 bool get isRunning => _schedule.state == ScheduleState.RUNNING &&
378 _schedule.currentQueue == this; 408 _schedule.currentQueue == this;
379 409
380 /// Schedules a task, [fn], to run asynchronously as part of this queue. Tasks 410 /// Schedules a task, [fn], to run asynchronously as part of this queue. Tasks
381 /// will be run in the order they're scheduled. In [fn] returns a [Future], 411 /// will be run in the order they're scheduled. In [fn] returns a [Future],
382 /// tasks after it won't be run until that [Future] completes. 412 /// tasks after it won't be run until that [Future] completes.
383 /// 413 ///
384 /// The return value will be completed once the scheduled task has finished 414 /// The return value will be completed once the scheduled task has finished
(...skipping 11 matching lines...) Expand all
396 /// top-level tasks which run in sequence. 426 /// top-level tasks which run in sequence.
397 Future schedule(fn(), [String description]) { 427 Future schedule(fn(), [String description]) {
398 if (isRunning) { 428 if (isRunning) {
399 var task = _schedule.currentTask; 429 var task = _schedule.currentTask;
400 var wrappedFn = () => _schedule.wrapFuture( 430 var wrappedFn = () => _schedule.wrapFuture(
401 new Future.immediate(null).then((_) => fn())); 431 new Future.immediate(null).then((_) => fn()));
402 if (task == null) return wrappedFn(); 432 if (task == null) return wrappedFn();
403 return task.runChild(wrappedFn, description); 433 return task.runChild(wrappedFn, description);
404 } 434 }
405 435
406 var task = new Task(fn, description, this); 436 var task = new Task(() {
437 return new Future.of(fn).catchError((e) {
438 throw new ScheduleError.from(_schedule, e);
439 });
440 }, description, this);
407 _contents.add(task); 441 _contents.add(task);
408 return task.result; 442 return task.result;
409 } 443 }
410 444
411 /// Runs all the tasks in this queue in order. 445 /// Runs all the tasks in this queue in order.
412 Future _run() { 446 Future _run() {
413 _schedule._currentQueue = this; 447 _schedule._currentQueue = this;
414 _schedule.heartbeat(); 448 _schedule.heartbeat();
415 return Future.forEach(_contents, (task) { 449 return Future.forEach(_contents, (task) {
416 _schedule._currentTask = task; 450 _schedule._currentTask = task;
417 if (_error != null) throw _error; 451 if (_error != null) throw _error;
418 452
419 _taskFuture = new SubstituteFuture(task.fn()); 453 _taskFuture = new SubstituteFuture(task.fn());
420 return _taskFuture.whenComplete(() { 454 return _taskFuture.whenComplete(() {
421 _taskFuture = null; 455 _taskFuture = null;
422 _schedule.heartbeat(); 456 _schedule.heartbeat();
423 }).catchError((e) { 457 }).catchError((e) {
424 if (_error != null) _schedule._addError(_error); 458 var error = new ScheduleError.from(_schedule, e);
425 throw new ScheduleError.from(_schedule, e); 459 _signalError(error);
460 throw _error;
461 });
462 }).then((_) {
463 _onTasksCompleteCompleter.complete();
464 }).catchError((e) {
465 _onTasksCompleteCompleter.completeError(e);
466 throw e;
467 }).whenComplete(() {
468 _schedule._currentTask = null;
469 return _schedule._awaitNoPendingCallbacks().catchError((e) {
470 // Signal the error rather than passing it through directly so that if a
471 // timeout happens after an in-task error, both are reported.
472 _signalError(new ScheduleError.from(_schedule, e));
426 }); 473 });
427 }).whenComplete(() { 474 }).whenComplete(() {
428 _schedule._currentTask = null;
429 return _schedule._awaitNoPendingCallbacks();
430 }).then((_) {
431 _schedule.heartbeat(); 475 _schedule.heartbeat();
476 // If the tasks were otherwise successful, make sure we throw any
477 // out-of-band errors. If a task failed, make sure we throw the most
478 // recent error.
432 if (_error != null) throw _error; 479 if (_error != null) throw _error;
433 }); 480 });
434 } 481 }
435 482
436 /// Signals that an out-of-band error has been detected and the queue should 483 /// Signals that an out-of-band error has been detected and the queue should
437 /// stop running as soon as possible. 484 /// stop running as soon as possible.
438 void _signalError(ScheduleError error) { 485 void _signalError(ScheduleError error) {
439 // If multiple errors are detected while a task is running, make sure the 486 // If multiple errors are detected while a task is running, make sure the
440 // earlier ones are recorded in the schedule. 487 // earlier ones are recorded in the schedule.
441 if (_error != null) _schedule._addError(_error); 488 if (_error != null) _schedule._addError(_error);
(...skipping 28 matching lines...) Expand all
470 return _contents.map((task) { 517 return _contents.map((task) {
471 var lines = task.toString().split("\n"); 518 var lines = task.toString().split("\n");
472 var firstLine = task == highlight ? 519 var firstLine = task == highlight ?
473 "> ${lines.first}" : "* ${lines.first}"; 520 "> ${lines.first}" : "* ${lines.first}";
474 lines = new List.from(lines.skip(1).map((line) => "| $line")); 521 lines = new List.from(lines.skip(1).map((line) => "| $line"));
475 lines.insertRange(0, 1, firstLine); 522 lines.insertRange(0, 1, firstLine);
476 return lines.join("\n"); 523 return lines.join("\n");
477 }).join("\n"); 524 }).join("\n");
478 } 525 }
479 } 526 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698