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("shouldn't allow additional futures or streams once an error has been " | |
35 "signaled", () { | |
36 expect(errorGroup.done, throwsFormatException); | |
37 errorGroup.signalError(new FormatException()); | |
38 | |
39 expect(() => errorGroup.registerFuture(new Future.value()), | |
40 throwsStateError); | |
41 expect(() => errorGroup.registerStream( | |
42 new StreamController(sync: true).stream), | |
43 throwsStateError); | |
44 }); | |
45 }); | |
46 | |
47 group('with a single future', () { | |
48 Completer completer; | |
49 Future future; | |
50 | |
51 setUp(() { | |
52 errorGroup = new ErrorGroup(); | |
53 completer = new Completer(); | |
54 future = errorGroup.registerFuture(completer.future); | |
55 }); | |
56 | |
57 test('should pass through a value from the future', () { | |
58 expect(future, completion(equals('value'))); | |
59 expect(errorGroup.done, completes); | |
60 completer.complete('value'); | |
61 }); | |
62 | |
63 test("shouldn't allow additional futures or streams once .done has " | |
64 "been called", () { | |
65 completer.complete('value'); | |
66 | |
67 expect(completer.future | |
68 .then((_) => errorGroup.registerFuture(new Future.value())), | |
69 throwsStateError); | |
70 expect(completer.future | |
71 .then((_) => errorGroup.registerStream( | |
72 new StreamController(sync: true).stream)), | |
73 throwsStateError); | |
74 }); | |
75 | |
76 test('should pass through an exception from the future if it has a ' | |
77 'listener', () { | |
78 expect(future, throwsFormatException); | |
79 // errorGroup shouldn't top-level the exception | |
80 completer.completeError(new FormatException()); | |
81 }); | |
82 | |
83 test('should notify the error group of an exception from the future even ' | |
84 'if it has a listener', () { | |
85 expect(future, throwsFormatException); | |
86 expect(errorGroup.done, throwsFormatException); | |
87 completer.completeError(new FormatException()); | |
88 }); | |
89 | |
90 test('should pass a signaled exception to the future if it has a listener ' | |
91 'and should ignore a subsequent value from that future', () { | |
92 expect(future, throwsFormatException); | |
93 // errorGroup shouldn't top-level the exception | |
94 errorGroup.signalError(new FormatException()); | |
95 completer.complete('value'); | |
96 }); | |
97 | |
98 test('should pass a signaled exception to the future if it has a listener ' | |
99 'and should ignore a subsequent exception from that future', () { | |
100 expect(future, throwsFormatException); | |
101 // errorGroup shouldn't top-level the exception | |
102 errorGroup.signalError(new FormatException()); | |
103 completer.completeError(new ArgumentError()); | |
104 }); | |
105 | |
106 test('should notify the error group of a signaled exception even if the ' | |
107 'future has a listener', () { | |
108 expect(future, throwsFormatException); | |
109 expect(errorGroup.done, throwsFormatException); | |
110 errorGroup.signalError(new FormatException()); | |
111 }); | |
112 | |
113 test("should complete .done if the future receives a value even if the " | |
114 "future doesn't have a listener", () { | |
115 expect(errorGroup.done, completes); | |
116 completer.complete('value'); | |
117 | |
118 // A listener added afterwards should receive the value | |
119 expect(errorGroup.done.then((_) => future), | |
120 completion(equals('value'))); | |
121 }); | |
122 | |
123 test("should pipe an exception from the future to .done if the future " | |
124 "doesn't have a listener", () { | |
125 expect(errorGroup.done, throwsFormatException); | |
126 completer.completeError(new FormatException()); | |
127 | |
128 // A listener added afterwards should receive the exception | |
129 expect(errorGroup.done.catchError((_) { | |
130 expect(future, throwsFormatException); | |
131 }), completes); | |
132 }); | |
133 | |
134 test("should pass a signaled exception to .done if the future doesn't have " | |
135 "a listener", | |
136 () { | |
137 expect(errorGroup.done, throwsFormatException); | |
138 errorGroup.signalError(new FormatException()); | |
139 | |
140 // A listener added afterwards should receive the exception | |
141 expect(errorGroup.done.catchError((_) { | |
142 completer.complete('value'); // should be ignored | |
143 expect(future, throwsFormatException); | |
144 }), completes); | |
145 }); | |
146 }); | |
147 | |
148 group('with multiple futures', () { | |
149 Completer completer1; | |
150 Completer completer2; | |
151 Future future1; | |
152 Future future2; | |
153 | |
154 setUp(() { | |
155 errorGroup = new ErrorGroup(); | |
156 completer1 = new Completer(); | |
157 completer2 = new Completer(); | |
158 future1 = errorGroup.registerFuture(completer1.future); | |
159 future2 = errorGroup.registerFuture(completer2.future); | |
160 }); | |
161 | |
162 test("should pipe exceptions from one future to the other and to " | |
163 ".complete", () { | |
164 expect(future1, throwsFormatException); | |
165 expect(future2, throwsFormatException); | |
166 expect(errorGroup.done, throwsFormatException); | |
167 | |
168 completer1.completeError(new FormatException()); | |
169 }); | |
170 | |
171 test("each future should be able to complete with a value " | |
172 "independently", () { | |
173 expect(future1, completion(equals('value1'))); | |
174 expect(future2, completion(equals('value2'))); | |
175 expect(errorGroup.done, completes); | |
176 | |
177 completer1.complete('value1'); | |
178 completer2.complete('value2'); | |
179 }); | |
180 | |
181 test("shouldn't throw a top-level exception if a future receives an error " | |
182 "after the other listened future completes", () { | |
183 expect(future1, completion(equals('value'))); | |
184 completer1.complete('value'); | |
185 | |
186 expect(future1.then((_) { | |
187 // shouldn't cause a top-level exception | |
188 completer2.completeError(new FormatException()); | |
189 }), completes); | |
190 }); | |
191 | |
192 test("shouldn't throw a top-level exception if an error is signaled after " | |
193 "one listened future completes", () { | |
194 expect(future1, completion(equals('value'))); | |
195 completer1.complete('value'); | |
196 | |
197 expect(future1.then((_) { | |
198 // shouldn't cause a top-level exception | |
199 errorGroup.signalError(new FormatException()); | |
200 }), completes); | |
201 }); | |
202 }); | |
203 | |
204 group('with a single stream', () { | |
205 StreamController controller; | |
206 Stream stream; | |
207 | |
208 setUp(() { | |
209 errorGroup = new ErrorGroup(); | |
210 controller = new StreamController.broadcast(sync: true); | |
211 stream = errorGroup.registerStream(controller.stream); | |
212 }); | |
213 | |
214 test('should pass through values from the stream', () { | |
215 StreamIterator iter = new StreamIterator(stream); | |
216 iter.moveNext().then((hasNext) { | |
217 expect(hasNext, isTrue); | |
218 expect(iter.current, equals(1)); | |
219 iter.moveNext().then((hasNext) { | |
220 expect(hasNext, isTrue); | |
221 expect(iter.current, equals(2)); | |
222 expect(iter.moveNext(), completion(isFalse)); | |
223 }); | |
224 }); | |
225 expect(errorGroup.done, completes); | |
226 | |
227 controller..add(1)..add(2)..close(); | |
228 }); | |
229 | |
230 test('should pass through an error from the stream if it has a ' | |
231 'listener', () { | |
232 expect(stream.first, throwsFormatException); | |
233 // errorGroup shouldn't top-level the exception | |
234 controller.addError(new FormatException()); | |
235 }); | |
236 | |
237 test('should notify the error group of an exception from the stream even ' | |
238 'if it has a listener', () { | |
239 expect(stream.first, throwsFormatException); | |
240 expect(errorGroup.done, throwsFormatException); | |
241 controller.addError(new FormatException()); | |
242 }); | |
243 | |
244 test('should pass a signaled exception to the stream if it has a listener ' | |
245 'and should unsubscribe that stream', () { | |
246 // errorGroup shouldn't top-level the exception | |
247 expect(stream.first, throwsFormatException); | |
248 errorGroup.signalError(new FormatException()); | |
249 | |
250 expect(newFuture(() { | |
251 controller.add('value'); | |
252 }), completes); | |
253 }); | |
254 | |
255 test('should notify the error group of a signaled exception even if the ' | |
256 'stream has a listener', () { | |
257 expect(stream.first, throwsFormatException); | |
258 expect(errorGroup.done, throwsFormatException); | |
259 errorGroup.signalError(new FormatException()); | |
260 }); | |
261 | |
262 test("should see one value and complete .done when the stream is done even " | |
263 "if the stream doesn't have a listener", () { | |
264 expect(errorGroup.done, completes); | |
265 controller.add('value'); | |
266 controller.close(); | |
267 | |
268 // Now that broadcast controllers have been removed a listener should | |
269 // see the value that has been put into the controller. | |
270 expect(errorGroup.done.then((_) => stream.toList()), | |
271 completion(equals(['value']))); | |
272 }); | |
273 | |
274 }); | |
275 | |
276 group('with a single single-subscription stream', () { | |
277 StreamController controller; | |
278 Stream stream; | |
279 | |
280 setUp(() { | |
281 errorGroup = new ErrorGroup(); | |
282 controller = new StreamController(sync: true); | |
283 stream = errorGroup.registerStream(controller.stream); | |
284 }); | |
285 | |
286 test("should complete .done when the stream is done even if the stream " | |
287 "doesn't have a listener", () { | |
288 expect(errorGroup.done, completes); | |
289 controller.add('value'); | |
290 controller.close(); | |
291 | |
292 // A listener added afterwards should receive the value | |
293 expect(errorGroup.done.then((_) => stream.toList()), | |
294 completion(equals(['value']))); | |
295 }); | |
296 | |
297 test("should pipe an exception from the stream to .done if the stream " | |
298 "doesn't have a listener", () { | |
299 expect(errorGroup.done, throwsFormatException); | |
300 controller.addError(new FormatException()); | |
301 | |
302 // A listener added afterwards should receive the exception | |
303 expect(errorGroup.done.catchError((_) { | |
304 controller.add('value'); // should be ignored | |
305 expect(stream.first, throwsFormatException); | |
306 }), completes); | |
307 }); | |
308 | |
309 test("should pass a signaled exception to .done if the stream doesn't " | |
310 "have a listener", | |
311 () { | |
312 expect(errorGroup.done, throwsFormatException); | |
313 errorGroup.signalError(new FormatException()); | |
314 | |
315 // A listener added afterwards should receive the exception | |
316 expect(errorGroup.done.catchError((_) { | |
317 controller.add('value'); // should be ignored | |
318 expect(stream.first, throwsFormatException); | |
319 }), completes); | |
320 }); | |
321 }); | |
322 | |
323 group('with multiple streams', () { | |
324 StreamController controller1; | |
325 StreamController controller2; | |
326 Stream stream1; | |
327 Stream stream2; | |
328 | |
329 setUp(() { | |
330 errorGroup = new ErrorGroup(); | |
331 controller1 = new StreamController.broadcast(sync: true); | |
332 controller2 = new StreamController.broadcast(sync: true); | |
333 stream1 = errorGroup.registerStream(controller1.stream); | |
334 stream2 = errorGroup.registerStream(controller2.stream); | |
335 }); | |
336 | |
337 test("should pipe exceptions from one stream to the other and to .done", | |
338 () { | |
339 expect(stream1.first, throwsFormatException); | |
340 expect(stream2.first, throwsFormatException); | |
341 expect(errorGroup.done, throwsFormatException); | |
342 | |
343 controller1.addError(new FormatException()); | |
344 }); | |
345 | |
346 test("each future should be able to emit values independently", () { | |
347 expect(stream1.toList(), completion(equals(['value1.1', 'value1.2']))); | |
348 expect(stream2.toList(), completion(equals(['value2.1', 'value2.2']))); | |
349 expect(errorGroup.done, completes); | |
350 | |
351 controller1..add('value1.1')..add('value1.2')..close(); | |
352 controller2..add('value2.1')..add('value2.2')..close(); | |
353 }); | |
354 | |
355 test("shouldn't throw a top-level exception if a stream receives an error " | |
356 "after the other listened stream completes", () { | |
357 var signal = new Completer(); | |
358 expect(stream1.toList().whenComplete(signal.complete), | |
359 completion(equals(['value1', 'value2']))); | |
360 controller1..add('value1')..add('value2')..close(); | |
361 | |
362 expect(signal.future.then((_) { | |
363 // shouldn't cause a top-level exception | |
364 controller2.addError(new FormatException()); | |
365 }), completes); | |
366 }); | |
367 | |
368 test("shouldn't throw a top-level exception if an error is signaled after " | |
369 "one listened stream completes", () { | |
370 var signal = new Completer(); | |
371 expect(stream1.toList().whenComplete(signal.complete), | |
372 completion(equals(['value1', 'value2']))); | |
373 controller1..add('value1')..add('value2')..close(); | |
374 | |
375 expect(signal.future.then((_) { | |
376 // shouldn't cause a top-level exception | |
377 errorGroup.signalError(new FormatException()); | |
378 }), completes); | |
379 }); | |
380 }); | |
381 | |
382 group('with a stream and a future', () { | |
383 StreamController controller; | |
384 Stream stream; | |
385 Completer completer; | |
386 Future future; | |
387 | |
388 setUp(() { | |
389 errorGroup = new ErrorGroup(); | |
390 controller = new StreamController.broadcast(sync: true); | |
391 stream = errorGroup.registerStream(controller.stream); | |
392 completer = new Completer(); | |
393 future = errorGroup.registerFuture(completer.future); | |
394 }); | |
395 | |
396 test("should pipe exceptions from the stream to the future", () { | |
397 expect(stream.first, throwsFormatException); | |
398 expect(future, throwsFormatException); | |
399 expect(errorGroup.done, throwsFormatException); | |
400 | |
401 controller.addError(new FormatException()); | |
402 }); | |
403 | |
404 test("should pipe exceptions from the future to the stream", () { | |
405 expect(stream.first, throwsFormatException); | |
406 expect(future, throwsFormatException); | |
407 expect(errorGroup.done, throwsFormatException); | |
408 | |
409 completer.completeError(new FormatException()); | |
410 }); | |
411 | |
412 test("the stream and the future should be able to complete/emit values " | |
413 "independently", () { | |
414 expect(stream.toList(), completion(equals(['value1.1', 'value1.2']))); | |
415 expect(future, completion(equals('value2.0'))); | |
416 expect(errorGroup.done, completes); | |
417 | |
418 controller..add('value1.1')..add('value1.2')..close(); | |
419 completer.complete('value2.0'); | |
420 }); | |
421 | |
422 test("shouldn't throw a top-level exception if the stream receives an error
" | |
423 "after the listened future completes", () { | |
424 expect(future, completion(equals('value'))); | |
425 completer.complete('value'); | |
426 | |
427 expect(future.then((_) { | |
428 // shouldn't cause a top-level exception | |
429 controller.addError(new FormatException()); | |
430 }), completes); | |
431 }); | |
432 | |
433 test("shouldn't throw a top-level exception if the future receives an " | |
434 "error after the listened stream completes", () { | |
435 var signal = new Completer(); | |
436 expect(stream.toList().whenComplete(signal.complete), | |
437 completion(equals(['value1', 'value2']))); | |
438 controller..add('value1')..add('value2')..close(); | |
439 | |
440 expect(signal.future.then((_) { | |
441 // shouldn't cause a top-level exception | |
442 completer.completeError(new FormatException()); | |
443 }), completes); | |
444 }); | |
445 }); | |
446 } | |
OLD | NEW |