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

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

Issue 12208096: Make the scheduled_test schedule keep track of multiple errors. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix out-of-band unwrapped error reporting. Created 7 years, 10 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;
11 11
12 import 'schedule_error.dart'; 12 import 'schedule_error.dart';
13 import 'task.dart'; 13 import 'task.dart';
14 14
15 /// The schedule of tasks to run for a single test. This has three separate task 15 /// The schedule of tasks to run for a single test. This has three separate task
16 /// queues: [tasks], [onComplete], and [onException]. It also provides 16 /// queues: [tasks], [onComplete], and [onException]. It also provides
17 /// visibility into the current state of the schedule. 17 /// visibility into the current state of the schedule.
18 class Schedule { 18 class Schedule {
19 /// The main task queue for the schedule. These tasks are run before the other 19 /// The main task queue for the schedule. These tasks are run before the other
20 /// queues and generally constitute the main test body. 20 /// queues and generally constitute the main test body.
21 TaskQueue get tasks => _tasks; 21 TaskQueue get tasks => _tasks;
22 TaskQueue _tasks; 22 TaskQueue _tasks;
23 23
24 /// The queue of tasks to run if an error is caught while running [tasks]. The 24 /// The queue of tasks to run if an error is caught while running [tasks]. The
25 /// error will be available via [error]. These tasks won't be run if no error 25 /// error will be available in [errors]. These tasks won't be run if no error
26 /// occurs. Note that expectation failures count as errors. 26 /// occurs. Note that expectation failures count as errors.
27 /// 27 ///
28 /// This queue runs before [onComplete], and errors in [onComplete] will not 28 /// This queue runs before [onComplete], and errors in [onComplete] will not
29 /// cause this queue to be run. 29 /// cause this queue to be run.
30 /// 30 ///
31 /// If an error occurs in a task in this queue, all further tasks will be 31 /// If an error occurs in a task in this queue, all further tasks will be
32 /// skipped. 32 /// skipped.
33 TaskQueue get onException => _onException; 33 TaskQueue get onException => _onException;
34 TaskQueue _onException; 34 TaskQueue _onException;
35 35
36 /// The queue of tasks to run after [tasks] and possibly [onException] have 36 /// The queue of tasks to run after [tasks] and possibly [onException] have
37 /// run. This queue will run whether or not an error occurred. If one did, it 37 /// run. This queue will run whether or not an error occurred. If one did, it
38 /// will be available via [error]. Note that expectation failures count as 38 /// will be available in [errors]. Note that expectation failures count as
39 /// errors. 39 /// errors.
40 /// 40 ///
41 /// This queue runs after [onException]. If an error occurs while running 41 /// This queue runs after [onException]. If an error occurs while running
42 /// [onException], that error will be available via [error] in place of the 42 /// [onException], that error will be available in [errors] after the original
43 /// original error. 43 /// error.
44 /// 44 ///
45 /// If an error occurs in a task in this queue, all further tasks will be 45 /// If an error occurs in a task in this queue, all further tasks will be
46 /// skipped. 46 /// skipped.
47 TaskQueue get onComplete => _onComplete; 47 TaskQueue get onComplete => _onComplete;
48 TaskQueue _onComplete; 48 TaskQueue _onComplete;
49 49
50 /// Returns the [Task] that's currently executing, or `null` if there is no 50 /// Returns the [Task] that's currently executing, or `null` if there is no
51 /// such task. This will be `null` both before the schedule starts running and 51 /// such task. This will be `null` both before the schedule starts running and
52 /// after it's finished. 52 /// after it's finished.
53 Task get currentTask => _currentTask; 53 Task get currentTask => _currentTask;
54 Task _currentTask; 54 Task _currentTask;
55 55
56 /// Whether the schedule has finished running. This is only set once 56 /// Whether the schedule has finished running. This is only set once
57 /// [onComplete] has finished running. It will be set whether or not an 57 /// [onComplete] has finished running. It will be set whether or not an
58 /// exception has occurred. 58 /// exception has occurred.
59 bool get done => _done; 59 bool get done => _done;
60 bool _done = false; 60 bool _done = false;
61 61
62 /// The error thrown by the task queue. This will only be set while running 62 // TODO(nweiz): make this a read-only view once issue 8321 is fixed.
63 /// [onException] and [onComplete], since an error in [tasks] will cause it to 63
64 /// terminate immediately. 64 /// Errors thrown by the task queues.
65 ScheduleError get error => _error; 65 ///
66 ScheduleError _error; 66 /// 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]
68 /// 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
70 /// also be added to this list, although no scheduled tasks will be run
71 /// afterwards.
72 ///
73 /// Any out-of-band callbacks that throw errors will also have those errors
74 /// added to this list.
75 final List<ScheduleError> errors = <ScheduleError>[];
Bob Nystrom 2013/02/11 22:28:38 You can probably drop the type annotation here: f
nweiz 2013/02/11 22:52:20 Done.
67 76
68 /// The task queue that's currently being run, or `null` if there is no such 77 /// The task queue that's currently being run, or `null` if there is no such
69 /// queue. One of [tasks], [onException], or [onComplete]. This will be `null` 78 /// queue. One of [tasks], [onException], or [onComplete]. This will be `null`
70 /// before the schedule starts running. 79 /// before the schedule starts running.
71 TaskQueue get currentQueue => _done ? null : _currentQueue; 80 TaskQueue get currentQueue => _done ? null : _currentQueue;
72 TaskQueue _currentQueue; 81 TaskQueue _currentQueue;
73 82
74 /// The number of out-of-band callbacks that have been registered with 83 /// The number of out-of-band callbacks that have been registered with
75 /// [wrapAsync] but have yet to be called. 84 /// [wrapAsync] but have yet to be called.
76 int _pendingCallbacks = 0; 85 int _pendingCallbacks = 0;
(...skipping 15 matching lines...) Expand all
92 Future run(void setUp()) { 101 Future run(void setUp()) {
93 return new Future.immediate(null).then((_) { 102 return new Future.immediate(null).then((_) {
94 try { 103 try {
95 setUp(); 104 setUp();
96 } catch (e, stackTrace) { 105 } catch (e, stackTrace) {
97 throw new ScheduleError.from(this, e, stackTrace: stackTrace); 106 throw new ScheduleError.from(this, e, stackTrace: stackTrace);
98 } 107 }
99 108
100 return tasks._run(); 109 return tasks._run();
101 }).catchError((e) { 110 }).catchError((e) {
102 _error = e; 111 errors.add(e);
103 return onException._run().then((_) { 112 return onException._run().catchError((e) {
113 errors.add(e);
114 throw e;
115 }).then((_) {
104 throw e; 116 throw e;
Bob Nystrom 2013/02/11 22:28:38 Is the intent here that the Future returned from r
nweiz 2013/02/11 22:52:20 Done.
105 }); 117 });
106 }).whenComplete(() => onComplete._run()).whenComplete(() { 118 }).whenComplete(() {
119 return onComplete._run().catchError((e) {
120 errors.add(e);
121 throw e;
122 });
123 }).whenComplete(() {
107 _done = true; 124 _done = true;
108 }); 125 });
109 } 126 }
110 127
111 /// Signals that an out-of-band error has occurred. Using [wrapAsync] along 128 /// Signals that an out-of-band error has occurred. Using [wrapAsync] along
112 /// with `throw` is usually preferable to calling this directly. 129 /// with `throw` is usually preferable to calling this directly.
113 /// 130 ///
114 /// The metadata in [AsyncError]s and [ScheduleError]s will be preserved. 131 /// The metadata in [AsyncError]s and [ScheduleError]s will be preserved.
115 void signalError(error, [stackTrace]) { 132 void signalError(error, [stackTrace]) {
116 var scheduleError = new ScheduleError.from(this, error, 133 var scheduleError = new ScheduleError.from(this, error,
117 stackTrace: stackTrace, task: currentTask); 134 stackTrace: stackTrace, task: currentTask);
118 if (_done) { 135 if (_done) {
136 errors.add(scheduleError);
119 throw new StateError( 137 throw new StateError(
120 "An out-of-band error was signaled outside of wrapAsync after the " 138 "An out-of-band error was signaled outside of wrapAsync after the "
121 "schedule finished running:" 139 "schedule finished running.\n"
122 "${prefixLines(scheduleError.toString())}"); 140 "${errorString()}");
123 } else if (currentQueue == null) { 141 } else if (currentQueue == null) {
124 // If we're not done but there's no current queue, that means we haven't 142 // If we're not done but there's no current queue, that means we haven't
125 // started yet and thus we're in setUp or the synchronous body of the 143 // started yet and thus we're in setUp or the synchronous body of the
126 // function. Throwing the error will thus pipe it into the main 144 // function. Throwing the error will thus pipe it into the main
127 // error-handling code. 145 // error-handling code.
128 throw scheduleError; 146 throw scheduleError;
129 } else { 147 } else {
130 _currentQueue._signalError(scheduleError); 148 _currentQueue._signalError(scheduleError);
131 } 149 }
132 } 150 }
(...skipping 20 matching lines...) Expand all
153 } finally { 171 } finally {
154 _pendingCallbacks--; 172 _pendingCallbacks--;
155 if (_pendingCallbacks == 0 && _noPendingCallbacks != null) { 173 if (_pendingCallbacks == 0 && _noPendingCallbacks != null) {
156 _noPendingCallbacks.complete(); 174 _noPendingCallbacks.complete();
157 _noPendingCallbacks = null; 175 _noPendingCallbacks = null;
158 } 176 }
159 } 177 }
160 }; 178 };
161 } 179 }
162 180
181 /// Returns a string representation of all errors registered on this schedule.
182 String errorString() {
183 if (errors.isEmpty) return "The schedule had no errors.";
184 if (errors.length == 1) return errors.first.toString();
185 var errorStrings = errors.map((e) => e.toString()).join("\n================"
186 "================================================================\n");
187 return "The schedule had ${errors.length} errors:\n$errorStrings";
188 }
189
163 /// Returns a [Future] that will complete once there are no pending 190 /// Returns a [Future] that will complete once there are no pending
164 /// out-of-band callbacks. 191 /// out-of-band callbacks.
165 Future _awaitNoPendingCallbacks() { 192 Future _awaitNoPendingCallbacks() {
166 if (_pendingCallbacks == 0) return new Future.immediate(null); 193 if (_pendingCallbacks == 0) return new Future.immediate(null);
167 if (_noPendingCallbacks == null) _noPendingCallbacks = new Completer(); 194 if (_noPendingCallbacks == null) _noPendingCallbacks = new Completer();
168 return _noPendingCallbacks.future; 195 return _noPendingCallbacks.future;
169 } 196 }
170 } 197 }
171 198
172 /// A queue of asynchronous tasks to execute in order. 199 /// A queue of asynchronous tasks to execute in order.
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 return task.result; 232 return task.result;
206 } 233 }
207 234
208 /// Runs all the tasks in this queue in order. 235 /// Runs all the tasks in this queue in order.
209 Future _run() { 236 Future _run() {
210 _schedule._currentQueue = this; 237 _schedule._currentQueue = this;
211 return Future.forEach(_contents, (task) { 238 return Future.forEach(_contents, (task) {
212 _schedule._currentTask = task; 239 _schedule._currentTask = task;
213 if (_error != null) throw _error; 240 if (_error != null) throw _error;
214 return task.fn().catchError((e) { 241 return task.fn().catchError((e) {
242 if (_error != null) _schedule.errors.add(_error);
215 throw new ScheduleError.from(_schedule, e, task: task); 243 throw new ScheduleError.from(_schedule, e, task: task);
216 }); 244 });
217 }).whenComplete(() { 245 }).whenComplete(() {
218 _schedule._currentTask = null; 246 _schedule._currentTask = null;
219 return _schedule._awaitNoPendingCallbacks(); 247 return _schedule._awaitNoPendingCallbacks();
220 }).then((_) { 248 }).then((_) {
221 if (_error != null) throw _error; 249 if (_error != null) throw _error;
222 }); 250 });
223 } 251 }
224 252
225 /// Signals that an out-of-band error has been detected and the queue should 253 /// Signals that an out-of-band error has been detected and the queue should
226 /// stop running as soon as possible. 254 /// stop running as soon as possible.
227 void _signalError(ScheduleError error) { 255 void _signalError(ScheduleError error) {
256 // If multiple errors are detected while a task is running, make sure the
257 // earlier ones are recorded in the schedule.
258 if (_error != null) _schedule.errors.add(_error);
228 _error = error; 259 _error = error;
229 } 260 }
230 261
231 String toString() => name; 262 String toString() => name;
232 263
233 /// Returns a detailed representation of the queue as a tree of tasks. If 264 /// Returns a detailed representation of the queue as a tree of tasks. If
234 /// [highlight] is passed, that task is specially highlighted. 265 /// [highlight] is passed, that task is specially highlighted.
235 /// 266 ///
236 /// [highlight] must be a task in this queue. 267 /// [highlight] must be a task in this queue.
237 String generateTree([Task highlight]) { 268 String generateTree([Task highlight]) {
238 assert(highlight == null || highlight.queue == this); 269 assert(highlight == null || highlight.queue == this);
239 return _contents.map((task) { 270 return _contents.map((task) {
240 var lines = task.toString().split("\n"); 271 var lines = task.toString().split("\n");
241 var firstLine = task == highlight ? 272 var firstLine = task == highlight ?
242 "> ${lines.first}" : "* ${lines.first}"; 273 "> ${lines.first}" : "* ${lines.first}";
243 lines = new List.from(lines.skip(1).map((line) => "| $line")); 274 lines = new List.from(lines.skip(1).map((line) => "| $line"));
244 lines.insertRange(0, 1, firstLine); 275 lines.insertRange(0, 1, firstLine);
245 return lines.join("\n"); 276 return lines.join("\n");
246 }).join("\n"); 277 }).join("\n");
247 } 278 }
248 } 279 }
OLDNEW
« no previous file with comments | « pkg/scheduled_test/lib/scheduled_test.dart ('k') | pkg/scheduled_test/test/scheduled_test_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698