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

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: Code review changes 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 errors = <ScheduleError>[];
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((innerError) {
113 // If an error occurs in a task in the onException queue, make sure it's
114 // registered in the error list and re-throw it. We could also re-throw
115 // `e`; ultimately, all the errors will be shown to the user if any
116 // ScheduleError is thrown.
117 errors.add(innerError);
118 throw innerError;
119 }).then((_) {
120 // If there are no errors in the onException queue, re-throw the
121 // original error that caused it to run.
104 throw e; 122 throw e;
105 }); 123 });
106 }).whenComplete(() => onComplete._run()).whenComplete(() { 124 }).whenComplete(() {
125 return onComplete._run().catchError((e) {
126 // If an error occurs in a task in the onComplete queue, make sure it's
127 // registered in the error list and re-throw it.
128 errors.add(e);
129 throw e;
130 });
131 }).whenComplete(() {
107 _done = true; 132 _done = true;
108 }); 133 });
109 } 134 }
110 135
111 /// Signals that an out-of-band error has occurred. Using [wrapAsync] along 136 /// Signals that an out-of-band error has occurred. Using [wrapAsync] along
112 /// with `throw` is usually preferable to calling this directly. 137 /// with `throw` is usually preferable to calling this directly.
113 /// 138 ///
114 /// The metadata in [AsyncError]s and [ScheduleError]s will be preserved. 139 /// The metadata in [AsyncError]s and [ScheduleError]s will be preserved.
115 void signalError(error, [stackTrace]) { 140 void signalError(error, [stackTrace]) {
116 var scheduleError = new ScheduleError.from(this, error, 141 var scheduleError = new ScheduleError.from(this, error,
117 stackTrace: stackTrace, task: currentTask); 142 stackTrace: stackTrace, task: currentTask);
118 if (_done) { 143 if (_done) {
144 errors.add(scheduleError);
119 throw new StateError( 145 throw new StateError(
120 "An out-of-band error was signaled outside of wrapAsync after the " 146 "An out-of-band error was signaled outside of wrapAsync after the "
121 "schedule finished running:" 147 "schedule finished running.\n"
122 "${prefixLines(scheduleError.toString())}"); 148 "${errorString()}");
123 } else if (currentQueue == null) { 149 } else if (currentQueue == null) {
124 // If we're not done but there's no current queue, that means we haven't 150 // 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 151 // 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 152 // function. Throwing the error will thus pipe it into the main
127 // error-handling code. 153 // error-handling code.
128 throw scheduleError; 154 throw scheduleError;
129 } else { 155 } else {
130 _currentQueue._signalError(scheduleError); 156 _currentQueue._signalError(scheduleError);
131 } 157 }
132 } 158 }
(...skipping 20 matching lines...) Expand all
153 } finally { 179 } finally {
154 _pendingCallbacks--; 180 _pendingCallbacks--;
155 if (_pendingCallbacks == 0 && _noPendingCallbacks != null) { 181 if (_pendingCallbacks == 0 && _noPendingCallbacks != null) {
156 _noPendingCallbacks.complete(); 182 _noPendingCallbacks.complete();
157 _noPendingCallbacks = null; 183 _noPendingCallbacks = null;
158 } 184 }
159 } 185 }
160 }; 186 };
161 } 187 }
162 188
189 /// Returns a string representation of all errors registered on this schedule.
190 String errorString() {
191 if (errors.isEmpty) return "The schedule had no errors.";
192 if (errors.length == 1) return errors.first.toString();
193 var errorStrings = errors.map((e) => e.toString()).join("\n================"
194 "================================================================\n");
195 return "The schedule had ${errors.length} errors:\n$errorStrings";
196 }
197
163 /// Returns a [Future] that will complete once there are no pending 198 /// Returns a [Future] that will complete once there are no pending
164 /// out-of-band callbacks. 199 /// out-of-band callbacks.
165 Future _awaitNoPendingCallbacks() { 200 Future _awaitNoPendingCallbacks() {
166 if (_pendingCallbacks == 0) return new Future.immediate(null); 201 if (_pendingCallbacks == 0) return new Future.immediate(null);
167 if (_noPendingCallbacks == null) _noPendingCallbacks = new Completer(); 202 if (_noPendingCallbacks == null) _noPendingCallbacks = new Completer();
168 return _noPendingCallbacks.future; 203 return _noPendingCallbacks.future;
169 } 204 }
170 } 205 }
171 206
172 /// A queue of asynchronous tasks to execute in order. 207 /// 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; 240 return task.result;
206 } 241 }
207 242
208 /// Runs all the tasks in this queue in order. 243 /// Runs all the tasks in this queue in order.
209 Future _run() { 244 Future _run() {
210 _schedule._currentQueue = this; 245 _schedule._currentQueue = this;
211 return Future.forEach(_contents, (task) { 246 return Future.forEach(_contents, (task) {
212 _schedule._currentTask = task; 247 _schedule._currentTask = task;
213 if (_error != null) throw _error; 248 if (_error != null) throw _error;
214 return task.fn().catchError((e) { 249 return task.fn().catchError((e) {
250 if (_error != null) _schedule.errors.add(_error);
215 throw new ScheduleError.from(_schedule, e, task: task); 251 throw new ScheduleError.from(_schedule, e, task: task);
216 }); 252 });
217 }).whenComplete(() { 253 }).whenComplete(() {
218 _schedule._currentTask = null; 254 _schedule._currentTask = null;
219 return _schedule._awaitNoPendingCallbacks(); 255 return _schedule._awaitNoPendingCallbacks();
220 }).then((_) { 256 }).then((_) {
221 if (_error != null) throw _error; 257 if (_error != null) throw _error;
222 }); 258 });
223 } 259 }
224 260
225 /// Signals that an out-of-band error has been detected and the queue should 261 /// Signals that an out-of-band error has been detected and the queue should
226 /// stop running as soon as possible. 262 /// stop running as soon as possible.
227 void _signalError(ScheduleError error) { 263 void _signalError(ScheduleError error) {
264 // If multiple errors are detected while a task is running, make sure the
265 // earlier ones are recorded in the schedule.
266 if (_error != null) _schedule.errors.add(_error);
228 _error = error; 267 _error = error;
229 } 268 }
230 269
231 String toString() => name; 270 String toString() => name;
232 271
233 /// Returns a detailed representation of the queue as a tree of tasks. If 272 /// Returns a detailed representation of the queue as a tree of tasks. If
234 /// [highlight] is passed, that task is specially highlighted. 273 /// [highlight] is passed, that task is specially highlighted.
235 /// 274 ///
236 /// [highlight] must be a task in this queue. 275 /// [highlight] must be a task in this queue.
237 String generateTree([Task highlight]) { 276 String generateTree([Task highlight]) {
238 assert(highlight == null || highlight.queue == this); 277 assert(highlight == null || highlight.queue == this);
239 return _contents.map((task) { 278 return _contents.map((task) {
240 var lines = task.toString().split("\n"); 279 var lines = task.toString().split("\n");
241 var firstLine = task == highlight ? 280 var firstLine = task == highlight ?
242 "> ${lines.first}" : "* ${lines.first}"; 281 "> ${lines.first}" : "* ${lines.first}";
243 lines = new List.from(lines.skip(1).map((line) => "| $line")); 282 lines = new List.from(lines.skip(1).map((line) => "| $line"));
244 lines.insertRange(0, 1, firstLine); 283 lines.insertRange(0, 1, firstLine);
245 return lines.join("\n"); 284 return lines.join("\n");
246 }).join("\n"); 285 }).join("\n");
247 } 286 }
248 } 287 }
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