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

Side by Side Diff: utils/tests/pub/error_group_test.dart

Issue 11941003: Add an ErrorGroup class to Pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 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 | Annotate | Revision Log
« utils/pub/error_group.dart ('K') | « utils/pub/error_group.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library error_group_test;
6
7 import 'dart:async';
8
9 import '../../../pkg/unittest/lib/unittest.dart';
10 import '../../pub/error_group.dart';
11 import '../../pub/utils.dart';
12
13 ErrorGroup errorGroup;
14
15 // TODO(nweiz): once there's a global error handler, we should test that it does
16 // and does not get called at appropriate times. See issue 5958.
17 //
18 // One particular thing we should test that has no tests now is that a stream
19 // that has a subscription added and subsequently canceled counts as having no
20 // listeners.
21
22 main() {
23 group('with no futures or streams', () {
24 setUp(() {
25 errorGroup = new ErrorGroup();
26 });
27
28 test('should pass signaled errors to .completer', () {
29 expect(errorGroup.complete, throwsFormatException);
30 errorGroup.signalError(new AsyncError(new FormatException()));
31 });
32
33 test("shouldn't allow additional futures or streams once an error has been "
34 "signaled", () {
35 expect(errorGroup.complete, throwsFormatException);
36 errorGroup.signalError(new AsyncError(new FormatException()));
37
38 expect(() => errorGroup.registerFuture(new Future.immediate(null)),
39 throwsStateError);
40 expect(() => errorGroup.registerStream(new StreamController()),
41 throwsStateError);
42 });
43 });
44
45 group('with a single future', () {
46 Completer completer;
47 Future future;
48
49 setUp(() {
50 errorGroup = new ErrorGroup();
51 completer = new Completer();
52 future = errorGroup.registerFuture(completer.future);
53 });
54
55 test('should pass through a value from the future', () {
56 expect(future, completion(equals('value')));
57 expect(errorGroup.complete, completes);
58 completer.complete('value');
59 });
60
61 test("shouldn't allow additional futures or streams once .complete has "
62 "been called", () {
63 completer.complete('value');
64
65 expect(() => errorGroup.registerFuture(new Future.immediate(null)),
66 throwsStateError);
67 expect(() => errorGroup.registerStream(new StreamController()),
68 throwsStateError);
69 });
70
71 test('should pass through an exception from the future if it has a '
72 'listener', () {
73 expect(future, throwsFormatException);
74 // errorGroup shouldn't top-level the exception
75 completer.completeError(new FormatException());
76 });
77
78 test('should notify the error group of an exception from the future even '
79 'if it has a listener', () {
80 expect(future, throwsFormatException);
81 expect(errorGroup.complete, throwsFormatException);
82 completer.completeError(new FormatException());
83 });
84
85 test('should pass a signaled exception to the future if it has a listener '
86 'and should ignore a subsequent value from that future', () {
87 expect(future, throwsFormatException);
88 // errorGroup shouldn't top-level the exception
89 errorGroup.signalError(new AsyncError(new FormatException()));
90 completer.complete('value');
91 });
92
93 test('should pass a signaled exception to the future if it has a listener '
94 'and should ignore a subsequent exception from that future', () {
95 expect(future, throwsFormatException);
96 // errorGroup shouldn't top-level the exception
97 errorGroup.signalError(new AsyncError(new FormatException()));
98 completer.completeError(new ArgumentError());
99 });
100
101 test('should notify the error group of a signaled exception even if the '
102 'future has a listener', () {
103 expect(future, throwsFormatException);
104 expect(errorGroup.complete, throwsFormatException);
105 errorGroup.signalError(new AsyncError(new FormatException()));
106 });
107
108 test("should complete .completer if the future receives a value even if "
109 "the future doesn't have a listener", () {
110 expect(errorGroup.complete, completes);
111 completer.complete('value');
112
113 // A listener added afterwards should receive the value
114 expect(errorGroup.complete.then((_) => future),
115 completion(equals('value')));
116 });
117
118 test("should pipe an exception from the future to .complete if the future "
119 "doesn't have a listener", () {
120 expect(errorGroup.complete, throwsFormatException);
121 completer.completeError(new FormatException());
122
123 // A listener added afterwards should receive the exception
124 expect(errorGroup.complete.catchError((_) {
125 expect(future, throwsFormatException);
126 }), completes);
127 });
128
129 test("should pass a signaled exception to .complete if the future doesn't "
130 "have a listener",
131 () {
132 expect(errorGroup.complete, throwsFormatException);
133 errorGroup.signalError(new AsyncError(new FormatException()));
134
135 // A listener added afterwards should receive the exception
136 expect(errorGroup.complete.catchError((_) {
137 completer.complete('value'); // should be ignored
138 expect(future, throwsFormatException);
139 }), completes);
140 });
141 });
142
143 group('with multiple futures', () {
144 Completer completer1;
145 Completer completer2;
146 Future future1;
147 Future future2;
148
149 setUp(() {
150 errorGroup = new ErrorGroup();
151 completer1 = new Completer();
152 completer2 = new Completer();
153 future1 = errorGroup.registerFuture(completer1.future);
154 future2 = errorGroup.registerFuture(completer2.future);
155 });
156
157 test("should pipe exceptions from one future to the other and to "
158 ".complete", () {
159 expect(future1, throwsFormatException);
160 expect(future2, throwsFormatException);
161 expect(errorGroup.complete, throwsFormatException);
162
163 completer1.completeError(new FormatException());
164 });
165
166 test("each future should be able to complete with a value "
167 "independently", () {
168 expect(future1, completion(equals('value1')));
169 expect(future2, completion(equals('value2')));
170 expect(errorGroup.complete, completes);
171
172 completer1.complete('value1');
173 completer2.complete('value2');
174 });
175
176 test("shouldn't throw a top-level exception if a future receives an error "
177 "after the other listened future completes", () {
178 expect(future1, completion(equals('value')));
179 completer1.complete('value');
180
181 expect(future1.then((_) {
182 // shouldn't cause a top-level exception
183 completer2.completeError(new FormatException());
184 }), completes);
185 });
186
187 test("shouldn't throw a top-level exception if an error is signaled after "
188 "one listened future completes", () {
189 expect(future1, completion(equals('value')));
190 completer1.complete('value');
191
192 expect(future1.then((_) {
193 // shouldn't cause a top-level exception
194 errorGroup.signalError(new AsyncError(new FormatException()));
195 }), completes);
196 });
197 });
198
199 group('with a single stream', () {
200 StreamController controller;
201 Stream stream;
202
203 setUp(() {
204 errorGroup = new ErrorGroup();
205 controller = new StreamController.multiSubscription();
206 stream = errorGroup.registerStream(controller);
207 });
208
209 test('should pass through values from the stream', () {
210 expect(stream.elementAt(0), completion(equals(1)));
211 expect(stream.elementAt(1), completion(equals(2)));
212 expect(errorGroup.complete, completes);
213
214 controller..add(1)..add(2)..close();
215 });
216
217 test('should pass through an error from the stream if it has a '
218 'listener', () {
219 expect(stream.first, throwsFormatException);
220 // errorGroup shouldn't top-level the exception
221 controller.signalError(new AsyncError(new FormatException()));
222 });
223
224 test('should notify the error group of an exception from the stream even '
225 'if it has a listener', () {
226 expect(stream.first, throwsFormatException);
227 expect(errorGroup.complete, throwsFormatException);
228 controller.signalError(new AsyncError(new FormatException()));
229 });
230
231 test('should pass a signaled exception to the stream if it has a listener '
232 'and should unsubscribe that stream', () {
233 expect(stream.first, throwsFormatException);
234 // errorGroup shouldn't top-level the exception
235 errorGroup.signalError(new AsyncError(new FormatException()));
236
237 expect(stream.first.catchError((_) {
238 controller.add('value');
239 return stream.isEmpty;
240 }), completion(isTrue));
241 });
242
243 test('should notify the error group of a signaled exception even if the '
244 'stream has a listener', () {
245 expect(stream.first, throwsFormatException);
246 expect(errorGroup.complete, throwsFormatException);
247 errorGroup.signalError(new AsyncError(new FormatException()));
248 });
249
250 test("should complete .completer when the stream is done even if the "
251 "stream doesn't have a listener", () {
252 expect(errorGroup.complete, completes);
253 controller.add('value');
254 controller.close();
255
256 // A listener added afterwards should see an empty stream, since it's not
257 // single-subscription
258 expect(errorGroup.complete.then((_) => stream.toList()),
259 completion(isEmpty));
260 });
261
262 test("should pipe an exception from the stream to .complete if the stream "
263 "doesn't have a listener", () {
264 expect(errorGroup.complete, throwsFormatException);
265 controller.signalError(new AsyncError(new FormatException()));
266
267 // A listener added afterwards should see an empty stream, since it's not
268 // single-subscription
269 expect(errorGroup.complete.catchError((_) {
270 controller.add('value'); // should be ignored
271 return stream.toList();
272 }), completion(isEmpty));
273 });
274
275 test("should pass a signaled exception to .complete if the stream doesn't "
276 "have a listener",
277 () {
278 expect(errorGroup.complete, throwsFormatException);
279 errorGroup.signalError(new AsyncError(new FormatException()));
280
281 // A listener added afterwards should receive the exception
282 expect(errorGroup.complete.catchError((_) {
283 controller.add('value'); // should be ignored
284 return stream.toList();
285 }), completion(isEmpty));
286 });
287 });
288
289 group('with a single single-subscription stream', () {
290 StreamController controller;
291 Stream stream;
292
293 setUp(() {
294 errorGroup = new ErrorGroup();
295 controller = new StreamController();
296 stream = errorGroup.registerStream(controller);
297 });
298
299 test("should complete .completer when the stream is done even if the "
300 "stream doesn't have a listener", () {
301 expect(errorGroup.complete, completes);
302 controller.add('value');
303 controller.close();
304
305 // A listener added afterwards should receive the value
306 expect(errorGroup.complete.then((_) => stream.toList()),
307 completion(equals(['value'])));
308 });
309
310 test("should pipe an exception from the stream to .complete if the stream "
311 "doesn't have a listener", () {
312 expect(errorGroup.complete, throwsFormatException);
313 controller.signalError(new AsyncError(new FormatException()));
314
315 // A listener added afterwards should receive the exception
316 expect(errorGroup.complete.catchError((_) {
317 controller.add('value'); // should be ignored
318 expect(stream.first, throwsFormatException);
319 }), completes);
320 });
321
322 test("should pass a signaled exception to .complete if the stream doesn't "
323 "have a listener",
324 () {
325 expect(errorGroup.complete, throwsFormatException);
326 errorGroup.signalError(new AsyncError(new FormatException()));
327
328 // A listener added afterwards should receive the exception
329 expect(errorGroup.complete.catchError((_) {
330 controller.add('value'); // should be ignored
331 expect(stream.first, throwsFormatException);
332 }), completes);
333 });
334 });
335
336 group('with multiple streams', () {
337 StreamController controller1;
338 StreamController controller2;
339 Stream stream1;
340 Stream stream2;
341
342 setUp(() {
343 errorGroup = new ErrorGroup();
344 controller1 = new StreamController.multiSubscription();
345 controller2 = new StreamController.multiSubscription();
346 stream1 = errorGroup.registerStream(controller1);
347 stream2 = errorGroup.registerStream(controller2);
348 });
349
350 test("should pipe exceptions from one stream to the other and to "
351 ".complete", () {
352 expect(stream1.first, throwsFormatException);
353 expect(stream2.first, throwsFormatException);
354 expect(errorGroup.complete, throwsFormatException);
355
356 controller1.signalError(new AsyncError(new FormatException()));
357 });
358
359 test("each future should be able to emit values independently", () {
360 expect(stream1.toList(), completion(equals(['value1.1', 'value1.2'])));
361 expect(stream2.toList(), completion(equals(['value2.1', 'value2.2'])));
362 expect(errorGroup.complete, completes);
363
364 controller1..add('value1.1')..add('value1.2')..close();
365 controller2..add('value2.1')..add('value2.2')..close();
366 });
367
368 test("shouldn't throw a top-level exception if a stream receives an error "
369 "after the other listened stream completes", () {
370 expect(stream1.toList(), completion(equals(['value1', 'value2'])));
371 controller1..add('value1')..add('value2')..close();
372
373 expect(stream1.toList().then((_) {
374 // shouldn't cause a top-level exception
375 controller2.signalError(new AsyncError(new FormatException()));
376 }), completes);
377 });
378
379 test("shouldn't throw a top-level exception if an error is signaled after "
380 "one listened stream completes", () {
381 expect(stream1.toList(), completion(equals(['value1', 'value2'])));
382 controller1..add('value1')..add('value2')..close();
383
384 expect(stream1.toList().then((_) {
385 // shouldn't cause a top-level exception
386 errorGroup.signalError(new AsyncError(new FormatException()));
387 }), completes);
388 });
389 });
390
391 group('with a stream and a future', () {
392 StreamController controller;
393 Stream stream;
394 Completer completer;
395 Future future;
396
397 setUp(() {
398 errorGroup = new ErrorGroup();
399 controller = new StreamController.multiSubscription();
400 stream = errorGroup.registerStream(controller);
401 completer = new Completer();
402 future = errorGroup.registerFuture(completer.future);
403 });
404
405 test("should pipe exceptions from the stream to the future", () {
406 expect(stream.first, throwsFormatException);
407 expect(future, throwsFormatException);
408 expect(errorGroup.complete, throwsFormatException);
409
410 controller.signalError(new AsyncError(new FormatException()));
411 });
412
413 test("should pipe exceptions from the future to the stream", () {
414 expect(stream.first, throwsFormatException);
415 expect(future, throwsFormatException);
416 expect(errorGroup.complete, throwsFormatException);
417
418 completer.completeError(new FormatException());
419 });
420
421 test("the stream and the future should be able to complete/emit values "
422 "independently", () {
423 expect(stream.toList(), completion(equals(['value1.1', 'value1.2'])));
424 expect(future, completion(equals('value2.0')));
425 expect(errorGroup.complete, completes);
426
427 controller..add('value1.1')..add('value1.2')..close();
428 completer.complete('value2.0');
429 });
430
431 test("shouldn't throw a top-level exception if the stream receives an error "
432 "after the listened future completes", () {
433 expect(future, completion(equals('value')));
434 completer.complete('value');
435
436 expect(future.then((_) {
437 // shouldn't cause a top-level exception
438 controller.signalError(new AsyncError(new FormatException()));
439 }), completes);
440 });
441
442 test("shouldn't throw a top-level exception if the future receives an "
443 "error after the listened stream completes", () {
444 expect(stream.toList(), completion(equals(['value1', 'value2'])));
445 controller..add('value1')..add('value2')..close();
446
447 expect(stream.toList().then((_) {
448 // shouldn't cause a top-level exception
449 completer.completeError(new FormatException());
450 }), completes);
451 });
452 });
453 }
454
455 // TODO(nweiz): remove this once it's built in to unittest
Bob Nystrom 2013/01/15 22:49:47 File a bug?
nweiz 2013/01/16 00:12:10 Done.
456 /// A matcher for StateErrors.
457 const isStateError = const _StateError();
458
459 /// A matcher for functions that throw StateError.
460 const Matcher throwsStateError =
461 const Throws(isStateError);
462
463 class _StateError extends TypeMatcher {
464 const _StateError() : super("StateError");
465 bool matches(item, MatchState matchState) => item is StateError;
466 }
OLDNEW
« utils/pub/error_group.dart ('K') | « utils/pub/error_group.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698