| 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 |