OLD | NEW |
| (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 'package:unittest/unittest.dart'; | |
10 | |
11 import '../lib/src/error_group.dart'; | |
12 import '../lib/src/utils.dart'; | |
13 | |
14 ErrorGroup errorGroup; | |
15 | |
16 // TODO(nweiz): once there's a global error handler, we should test that it does | |
17 // and does not get called at appropriate times. See issue 5958. | |
18 // | |
19 // One particular thing we should test that has no tests now is that a stream | |
20 // that has a subscription added and subsequently canceled counts as having no | |
21 // listeners. | |
22 | |
23 main() { | |
24 group('with no futures or streams', () { | |
25 setUp(() { | |
26 errorGroup = new ErrorGroup(); | |
27 }); | |
28 | |
29 test('should pass signaled errors to .done', () { | |
30 expect(errorGroup.done, throwsFormatException); | |
31 errorGroup.signalError(new FormatException()); | |
32 }); | |
33 | |
34 test( | |
35 "shouldn't allow additional futures or streams once an error has been " | |
36 "signaled", | |
37 () { | |
38 expect(errorGroup.done, throwsFormatException); | |
39 errorGroup.signalError(new FormatException()); | |
40 | |
41 expect( | |
42 () => errorGroup.registerFuture(new Future.value()), | |
43 throwsStateError); | |
44 expect( | |
45 () => errorGroup.registerStream(new StreamController(sync: true).strea
m), | |
46 throwsStateError); | |
47 }); | |
48 }); | |
49 | |
50 group('with a single future', () { | |
51 Completer completer; | |
52 Future future; | |
53 | |
54 setUp(() { | |
55 errorGroup = new ErrorGroup(); | |
56 completer = new Completer(); | |
57 future = errorGroup.registerFuture(completer.future); | |
58 }); | |
59 | |
60 test('should pass through a value from the future', () { | |
61 expect(future, completion(equals('value'))); | |
62 expect(errorGroup.done, completes); | |
63 completer.complete('value'); | |
64 }); | |
65 | |
66 test( | |
67 "shouldn't allow additional futures or streams once .done has " "been ca
lled", | |
68 () { | |
69 completer.complete('value'); | |
70 | |
71 expect( | |
72 completer.future.then((_) => errorGroup.registerFuture(new Future.valu
e())), | |
73 throwsStateError); | |
74 expect( | |
75 completer.future.then( | |
76 (_) => errorGroup.registerStream(new StreamController(sync: true).
stream)), | |
77 throwsStateError); | |
78 }); | |
79 | |
80 test( | |
81 'should pass through an exception from the future if it has a ' 'listene
r', | |
82 () { | |
83 expect(future, throwsFormatException); | |
84 // errorGroup shouldn't top-level the exception | |
85 completer.completeError(new FormatException()); | |
86 }); | |
87 | |
88 test( | |
89 'should notify the error group of an exception from the future even ' | |
90 'if it has a listener', | |
91 () { | |
92 expect(future, throwsFormatException); | |
93 expect(errorGroup.done, throwsFormatException); | |
94 completer.completeError(new FormatException()); | |
95 }); | |
96 | |
97 test( | |
98 'should pass a signaled exception to the future if it has a listener ' | |
99 'and should ignore a subsequent value from that future', | |
100 () { | |
101 expect(future, throwsFormatException); | |
102 // errorGroup shouldn't top-level the exception | |
103 errorGroup.signalError(new FormatException()); | |
104 completer.complete('value'); | |
105 }); | |
106 | |
107 test( | |
108 'should pass a signaled exception to the future if it has a listener ' | |
109 'and should ignore a subsequent exception from that future', | |
110 () { | |
111 expect(future, throwsFormatException); | |
112 // errorGroup shouldn't top-level the exception | |
113 errorGroup.signalError(new FormatException()); | |
114 completer.completeError(new ArgumentError()); | |
115 }); | |
116 | |
117 test( | |
118 'should notify the error group of a signaled exception even if the ' | |
119 'future has a listener', | |
120 () { | |
121 expect(future, throwsFormatException); | |
122 expect(errorGroup.done, throwsFormatException); | |
123 errorGroup.signalError(new FormatException()); | |
124 }); | |
125 | |
126 test( | |
127 "should complete .done if the future receives a value even if the " | |
128 "future doesn't have a listener", | |
129 () { | |
130 expect(errorGroup.done, completes); | |
131 completer.complete('value'); | |
132 | |
133 // A listener added afterwards should receive the value | |
134 expect(errorGroup.done.then((_) => future), completion(equals('value'))); | |
135 }); | |
136 | |
137 test( | |
138 "should pipe an exception from the future to .done if the future " | |
139 "doesn't have a listener", | |
140 () { | |
141 expect(errorGroup.done, throwsFormatException); | |
142 completer.completeError(new FormatException()); | |
143 | |
144 // A listener added afterwards should receive the exception | |
145 expect(errorGroup.done.catchError((_) { | |
146 expect(future, throwsFormatException); | |
147 }), completes); | |
148 }); | |
149 | |
150 test( | |
151 "should pass a signaled exception to .done if the future doesn't have " | |
152 "a listener", | |
153 () { | |
154 expect(errorGroup.done, throwsFormatException); | |
155 errorGroup.signalError(new FormatException()); | |
156 | |
157 // A listener added afterwards should receive the exception | |
158 expect(errorGroup.done.catchError((_) { | |
159 completer.complete('value'); // should be ignored | |
160 expect(future, throwsFormatException); | |
161 }), completes); | |
162 }); | |
163 }); | |
164 | |
165 group('with multiple futures', () { | |
166 Completer completer1; | |
167 Completer completer2; | |
168 Future future1; | |
169 Future future2; | |
170 | |
171 setUp(() { | |
172 errorGroup = new ErrorGroup(); | |
173 completer1 = new Completer(); | |
174 completer2 = new Completer(); | |
175 future1 = errorGroup.registerFuture(completer1.future); | |
176 future2 = errorGroup.registerFuture(completer2.future); | |
177 }); | |
178 | |
179 test( | |
180 "should pipe exceptions from one future to the other and to " ".complete
", | |
181 () { | |
182 expect(future1, throwsFormatException); | |
183 expect(future2, throwsFormatException); | |
184 expect(errorGroup.done, throwsFormatException); | |
185 | |
186 completer1.completeError(new FormatException()); | |
187 }); | |
188 | |
189 test( | |
190 "each future should be able to complete with a value " "independently", | |
191 () { | |
192 expect(future1, completion(equals('value1'))); | |
193 expect(future2, completion(equals('value2'))); | |
194 expect(errorGroup.done, completes); | |
195 | |
196 completer1.complete('value1'); | |
197 completer2.complete('value2'); | |
198 }); | |
199 | |
200 test( | |
201 "shouldn't throw a top-level exception if a future receives an error " | |
202 "after the other listened future completes", | |
203 () { | |
204 expect(future1, completion(equals('value'))); | |
205 completer1.complete('value'); | |
206 | |
207 expect(future1.then((_) { | |
208 // shouldn't cause a top-level exception | |
209 completer2.completeError(new FormatException()); | |
210 }), completes); | |
211 }); | |
212 | |
213 test( | |
214 "shouldn't throw a top-level exception if an error is signaled after " | |
215 "one listened future completes", | |
216 () { | |
217 expect(future1, completion(equals('value'))); | |
218 completer1.complete('value'); | |
219 | |
220 expect(future1.then((_) { | |
221 // shouldn't cause a top-level exception | |
222 errorGroup.signalError(new FormatException()); | |
223 }), completes); | |
224 }); | |
225 }); | |
226 | |
227 group('with a single stream', () { | |
228 StreamController controller; | |
229 Stream stream; | |
230 | |
231 setUp(() { | |
232 errorGroup = new ErrorGroup(); | |
233 controller = new StreamController.broadcast(sync: true); | |
234 stream = errorGroup.registerStream(controller.stream); | |
235 }); | |
236 | |
237 test('should pass through values from the stream', () { | |
238 StreamIterator iter = new StreamIterator(stream); | |
239 iter.moveNext().then((hasNext) { | |
240 expect(hasNext, isTrue); | |
241 expect(iter.current, equals(1)); | |
242 iter.moveNext().then((hasNext) { | |
243 expect(hasNext, isTrue); | |
244 expect(iter.current, equals(2)); | |
245 expect(iter.moveNext(), completion(isFalse)); | |
246 }); | |
247 }); | |
248 expect(errorGroup.done, completes); | |
249 | |
250 controller | |
251 ..add(1) | |
252 ..add(2) | |
253 ..close(); | |
254 }); | |
255 | |
256 test( | |
257 'should pass through an error from the stream if it has a ' 'listener', | |
258 () { | |
259 expect(stream.first, throwsFormatException); | |
260 // errorGroup shouldn't top-level the exception | |
261 controller.addError(new FormatException()); | |
262 }); | |
263 | |
264 test( | |
265 'should notify the error group of an exception from the stream even ' | |
266 'if it has a listener', | |
267 () { | |
268 expect(stream.first, throwsFormatException); | |
269 expect(errorGroup.done, throwsFormatException); | |
270 controller.addError(new FormatException()); | |
271 }); | |
272 | |
273 test( | |
274 'should pass a signaled exception to the stream if it has a listener ' | |
275 'and should unsubscribe that stream', | |
276 () { | |
277 // errorGroup shouldn't top-level the exception | |
278 expect(stream.first, throwsFormatException); | |
279 errorGroup.signalError(new FormatException()); | |
280 | |
281 expect(newFuture(() { | |
282 controller.add('value'); | |
283 }), completes); | |
284 }); | |
285 | |
286 test( | |
287 'should notify the error group of a signaled exception even if the ' | |
288 'stream has a listener', | |
289 () { | |
290 expect(stream.first, throwsFormatException); | |
291 expect(errorGroup.done, throwsFormatException); | |
292 errorGroup.signalError(new FormatException()); | |
293 }); | |
294 | |
295 test( | |
296 "should see one value and complete .done when the stream is done even " | |
297 "if the stream doesn't have a listener", | |
298 () { | |
299 expect(errorGroup.done, completes); | |
300 controller.add('value'); | |
301 controller.close(); | |
302 | |
303 // Now that broadcast controllers have been removed a listener should | |
304 // see the value that has been put into the controller. | |
305 expect( | |
306 errorGroup.done.then((_) => stream.toList()), | |
307 completion(equals(['value']))); | |
308 }); | |
309 | |
310 }); | |
311 | |
312 group('with a single single-subscription stream', () { | |
313 StreamController controller; | |
314 Stream stream; | |
315 | |
316 setUp(() { | |
317 errorGroup = new ErrorGroup(); | |
318 controller = new StreamController(sync: true); | |
319 stream = errorGroup.registerStream(controller.stream); | |
320 }); | |
321 | |
322 test( | |
323 "should complete .done when the stream is done even if the stream " | |
324 "doesn't have a listener", | |
325 () { | |
326 expect(errorGroup.done, completes); | |
327 controller.add('value'); | |
328 controller.close(); | |
329 | |
330 // A listener added afterwards should receive the value | |
331 expect( | |
332 errorGroup.done.then((_) => stream.toList()), | |
333 completion(equals(['value']))); | |
334 }); | |
335 | |
336 test( | |
337 "should pipe an exception from the stream to .done if the stream " | |
338 "doesn't have a listener", | |
339 () { | |
340 expect(errorGroup.done, throwsFormatException); | |
341 controller.addError(new FormatException()); | |
342 | |
343 // A listener added afterwards should receive the exception | |
344 expect(errorGroup.done.catchError((_) { | |
345 controller.add('value'); // should be ignored | |
346 expect(stream.first, throwsFormatException); | |
347 }), completes); | |
348 }); | |
349 | |
350 test( | |
351 "should pass a signaled exception to .done if the stream doesn't " | |
352 "have a listener", | |
353 () { | |
354 expect(errorGroup.done, throwsFormatException); | |
355 errorGroup.signalError(new FormatException()); | |
356 | |
357 // A listener added afterwards should receive the exception | |
358 expect(errorGroup.done.catchError((_) { | |
359 controller.add('value'); // should be ignored | |
360 expect(stream.first, throwsFormatException); | |
361 }), completes); | |
362 }); | |
363 }); | |
364 | |
365 group('with multiple streams', () { | |
366 StreamController controller1; | |
367 StreamController controller2; | |
368 Stream stream1; | |
369 Stream stream2; | |
370 | |
371 setUp(() { | |
372 errorGroup = new ErrorGroup(); | |
373 controller1 = new StreamController.broadcast(sync: true); | |
374 controller2 = new StreamController.broadcast(sync: true); | |
375 stream1 = errorGroup.registerStream(controller1.stream); | |
376 stream2 = errorGroup.registerStream(controller2.stream); | |
377 }); | |
378 | |
379 test( | |
380 "should pipe exceptions from one stream to the other and to .done", | |
381 () { | |
382 expect(stream1.first, throwsFormatException); | |
383 expect(stream2.first, throwsFormatException); | |
384 expect(errorGroup.done, throwsFormatException); | |
385 | |
386 controller1.addError(new FormatException()); | |
387 }); | |
388 | |
389 test("each future should be able to emit values independently", () { | |
390 expect(stream1.toList(), completion(equals(['value1.1', 'value1.2']))); | |
391 expect(stream2.toList(), completion(equals(['value2.1', 'value2.2']))); | |
392 expect(errorGroup.done, completes); | |
393 | |
394 controller1 | |
395 ..add('value1.1') | |
396 ..add('value1.2') | |
397 ..close(); | |
398 controller2 | |
399 ..add('value2.1') | |
400 ..add('value2.2') | |
401 ..close(); | |
402 }); | |
403 | |
404 test( | |
405 "shouldn't throw a top-level exception if a stream receives an error " | |
406 "after the other listened stream completes", | |
407 () { | |
408 var signal = new Completer(); | |
409 expect( | |
410 stream1.toList().whenComplete(signal.complete), | |
411 completion(equals(['value1', 'value2']))); | |
412 controller1 | |
413 ..add('value1') | |
414 ..add('value2') | |
415 ..close(); | |
416 | |
417 expect(signal.future.then((_) { | |
418 // shouldn't cause a top-level exception | |
419 controller2.addError(new FormatException()); | |
420 }), completes); | |
421 }); | |
422 | |
423 test( | |
424 "shouldn't throw a top-level exception if an error is signaled after " | |
425 "one listened stream completes", | |
426 () { | |
427 var signal = new Completer(); | |
428 expect( | |
429 stream1.toList().whenComplete(signal.complete), | |
430 completion(equals(['value1', 'value2']))); | |
431 controller1 | |
432 ..add('value1') | |
433 ..add('value2') | |
434 ..close(); | |
435 | |
436 expect(signal.future.then((_) { | |
437 // shouldn't cause a top-level exception | |
438 errorGroup.signalError(new FormatException()); | |
439 }), completes); | |
440 }); | |
441 }); | |
442 | |
443 group('with a stream and a future', () { | |
444 StreamController controller; | |
445 Stream stream; | |
446 Completer completer; | |
447 Future future; | |
448 | |
449 setUp(() { | |
450 errorGroup = new ErrorGroup(); | |
451 controller = new StreamController.broadcast(sync: true); | |
452 stream = errorGroup.registerStream(controller.stream); | |
453 completer = new Completer(); | |
454 future = errorGroup.registerFuture(completer.future); | |
455 }); | |
456 | |
457 test("should pipe exceptions from the stream to the future", () { | |
458 expect(stream.first, throwsFormatException); | |
459 expect(future, throwsFormatException); | |
460 expect(errorGroup.done, throwsFormatException); | |
461 | |
462 controller.addError(new FormatException()); | |
463 }); | |
464 | |
465 test("should pipe exceptions from the future to the stream", () { | |
466 expect(stream.first, throwsFormatException); | |
467 expect(future, throwsFormatException); | |
468 expect(errorGroup.done, throwsFormatException); | |
469 | |
470 completer.completeError(new FormatException()); | |
471 }); | |
472 | |
473 test( | |
474 "the stream and the future should be able to complete/emit values " | |
475 "independently", | |
476 () { | |
477 expect(stream.toList(), completion(equals(['value1.1', 'value1.2']))); | |
478 expect(future, completion(equals('value2.0'))); | |
479 expect(errorGroup.done, completes); | |
480 | |
481 controller | |
482 ..add('value1.1') | |
483 ..add('value1.2') | |
484 ..close(); | |
485 completer.complete('value2.0'); | |
486 }); | |
487 | |
488 test( | |
489 "shouldn't throw a top-level exception if the stream receives an error " | |
490 "after the listened future completes", | |
491 () { | |
492 expect(future, completion(equals('value'))); | |
493 completer.complete('value'); | |
494 | |
495 expect(future.then((_) { | |
496 // shouldn't cause a top-level exception | |
497 controller.addError(new FormatException()); | |
498 }), completes); | |
499 }); | |
500 | |
501 test( | |
502 "shouldn't throw a top-level exception if the future receives an " | |
503 "error after the listened stream completes", | |
504 () { | |
505 var signal = new Completer(); | |
506 expect( | |
507 stream.toList().whenComplete(signal.complete), | |
508 completion(equals(['value1', 'value2']))); | |
509 controller | |
510 ..add('value1') | |
511 ..add('value2') | |
512 ..close(); | |
513 | |
514 expect(signal.future.then((_) { | |
515 // shouldn't cause a top-level exception | |
516 completer.completeError(new FormatException()); | |
517 }), completes); | |
518 }); | |
519 }); | |
520 } | |
OLD | NEW |