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 '../../../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 } | |
OLD | NEW |