aeca565ae9
Review URL: https://codereview.chromium.org//12088056 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@17812 260f80e4-7a28-3924-810f-c04153c831b5
454 lines
16 KiB
Dart
454 lines
16 KiB
Dart
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
library error_group_test;
|
|
|
|
import 'dart:async';
|
|
|
|
import '../../../pkg/unittest/lib/unittest.dart';
|
|
import '../../pub/error_group.dart';
|
|
import '../../pub/utils.dart';
|
|
|
|
ErrorGroup errorGroup;
|
|
|
|
// TODO(nweiz): once there's a global error handler, we should test that it does
|
|
// and does not get called at appropriate times. See issue 5958.
|
|
//
|
|
// One particular thing we should test that has no tests now is that a stream
|
|
// that has a subscription added and subsequently canceled counts as having no
|
|
// listeners.
|
|
|
|
main() {
|
|
group('with no futures or streams', () {
|
|
setUp(() {
|
|
errorGroup = new ErrorGroup();
|
|
});
|
|
|
|
test('should pass signaled errors to .done', () {
|
|
expect(errorGroup.done, throwsFormatException);
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
});
|
|
|
|
test("shouldn't allow additional futures or streams once an error has been "
|
|
"signaled", () {
|
|
expect(errorGroup.done, throwsFormatException);
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
|
|
expect(() => errorGroup.registerFuture(new Future.immediate(null)),
|
|
throwsStateError);
|
|
expect(() => errorGroup.registerStream(new StreamController().stream),
|
|
throwsStateError);
|
|
});
|
|
});
|
|
|
|
group('with a single future', () {
|
|
Completer completer;
|
|
Future future;
|
|
|
|
setUp(() {
|
|
errorGroup = new ErrorGroup();
|
|
completer = new Completer();
|
|
future = errorGroup.registerFuture(completer.future);
|
|
});
|
|
|
|
test('should pass through a value from the future', () {
|
|
expect(future, completion(equals('value')));
|
|
expect(errorGroup.done, completes);
|
|
completer.complete('value');
|
|
});
|
|
|
|
test("shouldn't allow additional futures or streams once .done has "
|
|
"been called", () {
|
|
completer.complete('value');
|
|
|
|
expect(() => errorGroup.registerFuture(new Future.immediate(null)),
|
|
throwsStateError);
|
|
expect(() => errorGroup.registerStream(new StreamController().stream),
|
|
throwsStateError);
|
|
});
|
|
|
|
test('should pass through an exception from the future if it has a '
|
|
'listener', () {
|
|
expect(future, throwsFormatException);
|
|
// errorGroup shouldn't top-level the exception
|
|
completer.completeError(new FormatException());
|
|
});
|
|
|
|
test('should notify the error group of an exception from the future even '
|
|
'if it has a listener', () {
|
|
expect(future, throwsFormatException);
|
|
expect(errorGroup.done, throwsFormatException);
|
|
completer.completeError(new FormatException());
|
|
});
|
|
|
|
test('should pass a signaled exception to the future if it has a listener '
|
|
'and should ignore a subsequent value from that future', () {
|
|
expect(future, throwsFormatException);
|
|
// errorGroup shouldn't top-level the exception
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
completer.complete('value');
|
|
});
|
|
|
|
test('should pass a signaled exception to the future if it has a listener '
|
|
'and should ignore a subsequent exception from that future', () {
|
|
expect(future, throwsFormatException);
|
|
// errorGroup shouldn't top-level the exception
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
completer.completeError(new ArgumentError());
|
|
});
|
|
|
|
test('should notify the error group of a signaled exception even if the '
|
|
'future has a listener', () {
|
|
expect(future, throwsFormatException);
|
|
expect(errorGroup.done, throwsFormatException);
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
});
|
|
|
|
test("should complete .done if the future receives a value even if the "
|
|
"future doesn't have a listener", () {
|
|
expect(errorGroup.done, completes);
|
|
completer.complete('value');
|
|
|
|
// A listener added afterwards should receive the value
|
|
expect(errorGroup.done.then((_) => future),
|
|
completion(equals('value')));
|
|
});
|
|
|
|
test("should pipe an exception from the future to .done if the future "
|
|
"doesn't have a listener", () {
|
|
expect(errorGroup.done, throwsFormatException);
|
|
completer.completeError(new FormatException());
|
|
|
|
// A listener added afterwards should receive the exception
|
|
expect(errorGroup.done.catchError((_) {
|
|
expect(future, throwsFormatException);
|
|
}), completes);
|
|
});
|
|
|
|
test("should pass a signaled exception to .done if the future doesn't have "
|
|
"a listener",
|
|
() {
|
|
expect(errorGroup.done, throwsFormatException);
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
|
|
// A listener added afterwards should receive the exception
|
|
expect(errorGroup.done.catchError((_) {
|
|
completer.complete('value'); // should be ignored
|
|
expect(future, throwsFormatException);
|
|
}), completes);
|
|
});
|
|
});
|
|
|
|
group('with multiple futures', () {
|
|
Completer completer1;
|
|
Completer completer2;
|
|
Future future1;
|
|
Future future2;
|
|
|
|
setUp(() {
|
|
errorGroup = new ErrorGroup();
|
|
completer1 = new Completer();
|
|
completer2 = new Completer();
|
|
future1 = errorGroup.registerFuture(completer1.future);
|
|
future2 = errorGroup.registerFuture(completer2.future);
|
|
});
|
|
|
|
test("should pipe exceptions from one future to the other and to "
|
|
".complete", () {
|
|
expect(future1, throwsFormatException);
|
|
expect(future2, throwsFormatException);
|
|
expect(errorGroup.done, throwsFormatException);
|
|
|
|
completer1.completeError(new FormatException());
|
|
});
|
|
|
|
test("each future should be able to complete with a value "
|
|
"independently", () {
|
|
expect(future1, completion(equals('value1')));
|
|
expect(future2, completion(equals('value2')));
|
|
expect(errorGroup.done, completes);
|
|
|
|
completer1.complete('value1');
|
|
completer2.complete('value2');
|
|
});
|
|
|
|
test("shouldn't throw a top-level exception if a future receives an error "
|
|
"after the other listened future completes", () {
|
|
expect(future1, completion(equals('value')));
|
|
completer1.complete('value');
|
|
|
|
expect(future1.then((_) {
|
|
// shouldn't cause a top-level exception
|
|
completer2.completeError(new FormatException());
|
|
}), completes);
|
|
});
|
|
|
|
test("shouldn't throw a top-level exception if an error is signaled after "
|
|
"one listened future completes", () {
|
|
expect(future1, completion(equals('value')));
|
|
completer1.complete('value');
|
|
|
|
expect(future1.then((_) {
|
|
// shouldn't cause a top-level exception
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
}), completes);
|
|
});
|
|
});
|
|
|
|
group('with a single stream', () {
|
|
StreamController controller;
|
|
Stream stream;
|
|
|
|
setUp(() {
|
|
errorGroup = new ErrorGroup();
|
|
controller = new StreamController.broadcast();
|
|
stream = errorGroup.registerStream(controller.stream);
|
|
});
|
|
|
|
test('should pass through values from the stream', () {
|
|
expect(stream.elementAt(0), completion(equals(1)));
|
|
expect(stream.elementAt(1), completion(equals(2)));
|
|
expect(errorGroup.done, completes);
|
|
|
|
controller..add(1)..add(2)..close();
|
|
});
|
|
|
|
test('should pass through an error from the stream if it has a '
|
|
'listener', () {
|
|
expect(stream.first, throwsFormatException);
|
|
// errorGroup shouldn't top-level the exception
|
|
controller.signalError(new AsyncError(new FormatException()));
|
|
});
|
|
|
|
test('should notify the error group of an exception from the stream even '
|
|
'if it has a listener', () {
|
|
expect(stream.first, throwsFormatException);
|
|
expect(errorGroup.done, throwsFormatException);
|
|
controller.signalError(new AsyncError(new FormatException()));
|
|
});
|
|
|
|
test('should pass a signaled exception to the stream if it has a listener '
|
|
'and should unsubscribe that stream', () {
|
|
expect(stream.first, throwsFormatException);
|
|
// errorGroup shouldn't top-level the exception
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
|
|
expect(stream.first.catchError((_) {
|
|
controller.add('value');
|
|
return stream.isEmpty;
|
|
}), completion(isTrue));
|
|
});
|
|
|
|
test('should notify the error group of a signaled exception even if the '
|
|
'stream has a listener', () {
|
|
expect(stream.first, throwsFormatException);
|
|
expect(errorGroup.done, throwsFormatException);
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
});
|
|
|
|
test("should complete .done when the stream is done even if the stream "
|
|
"doesn't have a listener", () {
|
|
expect(errorGroup.done, completes);
|
|
controller.add('value');
|
|
controller.close();
|
|
|
|
// A listener added afterwards should see an empty stream, since it's not
|
|
// single-subscription
|
|
expect(errorGroup.done.then((_) => stream.toList()),
|
|
completion(isEmpty));
|
|
});
|
|
|
|
test("should pipe an exception from the stream to .done if the stream "
|
|
"doesn't have a listener", () {
|
|
expect(errorGroup.done, throwsFormatException);
|
|
controller.signalError(new AsyncError(new FormatException()));
|
|
|
|
// A listener added afterwards should see an empty stream, since it's not
|
|
// single-subscription
|
|
expect(errorGroup.done.catchError((_) {
|
|
controller.add('value'); // should be ignored
|
|
return stream.toList();
|
|
}), completion(isEmpty));
|
|
});
|
|
|
|
test("should pass a signaled exception to .done if the stream doesn't "
|
|
"have a listener",
|
|
() {
|
|
expect(errorGroup.done, throwsFormatException);
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
|
|
// A listener added afterwards should receive the exception
|
|
expect(errorGroup.done.catchError((_) {
|
|
controller.add('value'); // should be ignored
|
|
return stream.toList();
|
|
}), completion(isEmpty));
|
|
});
|
|
});
|
|
|
|
group('with a single single-subscription stream', () {
|
|
StreamController controller;
|
|
Stream stream;
|
|
|
|
setUp(() {
|
|
errorGroup = new ErrorGroup();
|
|
controller = new StreamController();
|
|
stream = errorGroup.registerStream(controller.stream);
|
|
});
|
|
|
|
test("should complete .done when the stream is done even if the stream "
|
|
"doesn't have a listener", () {
|
|
expect(errorGroup.done, completes);
|
|
controller.add('value');
|
|
controller.close();
|
|
|
|
// A listener added afterwards should receive the value
|
|
expect(errorGroup.done.then((_) => stream.toList()),
|
|
completion(equals(['value'])));
|
|
});
|
|
|
|
test("should pipe an exception from the stream to .done if the stream "
|
|
"doesn't have a listener", () {
|
|
expect(errorGroup.done, throwsFormatException);
|
|
controller.signalError(new AsyncError(new FormatException()));
|
|
|
|
// A listener added afterwards should receive the exception
|
|
expect(errorGroup.done.catchError((_) {
|
|
controller.add('value'); // should be ignored
|
|
expect(stream.first, throwsFormatException);
|
|
}), completes);
|
|
});
|
|
|
|
test("should pass a signaled exception to .done if the stream doesn't "
|
|
"have a listener",
|
|
() {
|
|
expect(errorGroup.done, throwsFormatException);
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
|
|
// A listener added afterwards should receive the exception
|
|
expect(errorGroup.done.catchError((_) {
|
|
controller.add('value'); // should be ignored
|
|
expect(stream.first, throwsFormatException);
|
|
}), completes);
|
|
});
|
|
});
|
|
|
|
group('with multiple streams', () {
|
|
StreamController controller1;
|
|
StreamController controller2;
|
|
Stream stream1;
|
|
Stream stream2;
|
|
|
|
setUp(() {
|
|
errorGroup = new ErrorGroup();
|
|
controller1 = new StreamController.broadcast();
|
|
controller2 = new StreamController.broadcast();
|
|
stream1 = errorGroup.registerStream(controller1.stream);
|
|
stream2 = errorGroup.registerStream(controller2.stream);
|
|
});
|
|
|
|
test("should pipe exceptions from one stream to the other and to .done",
|
|
() {
|
|
expect(stream1.first, throwsFormatException);
|
|
expect(stream2.first, throwsFormatException);
|
|
expect(errorGroup.done, throwsFormatException);
|
|
|
|
controller1.signalError(new AsyncError(new FormatException()));
|
|
});
|
|
|
|
test("each future should be able to emit values independently", () {
|
|
expect(stream1.toList(), completion(equals(['value1.1', 'value1.2'])));
|
|
expect(stream2.toList(), completion(equals(['value2.1', 'value2.2'])));
|
|
expect(errorGroup.done, completes);
|
|
|
|
controller1..add('value1.1')..add('value1.2')..close();
|
|
controller2..add('value2.1')..add('value2.2')..close();
|
|
});
|
|
|
|
test("shouldn't throw a top-level exception if a stream receives an error "
|
|
"after the other listened stream completes", () {
|
|
expect(stream1.toList(), completion(equals(['value1', 'value2'])));
|
|
controller1..add('value1')..add('value2')..close();
|
|
|
|
expect(stream1.toList().then((_) {
|
|
// shouldn't cause a top-level exception
|
|
controller2.signalError(new AsyncError(new FormatException()));
|
|
}), completes);
|
|
});
|
|
|
|
test("shouldn't throw a top-level exception if an error is signaled after "
|
|
"one listened stream completes", () {
|
|
expect(stream1.toList(), completion(equals(['value1', 'value2'])));
|
|
controller1..add('value1')..add('value2')..close();
|
|
|
|
expect(stream1.toList().then((_) {
|
|
// shouldn't cause a top-level exception
|
|
errorGroup.signalError(new AsyncError(new FormatException()));
|
|
}), completes);
|
|
});
|
|
});
|
|
|
|
group('with a stream and a future', () {
|
|
StreamController controller;
|
|
Stream stream;
|
|
Completer completer;
|
|
Future future;
|
|
|
|
setUp(() {
|
|
errorGroup = new ErrorGroup();
|
|
controller = new StreamController.broadcast();
|
|
stream = errorGroup.registerStream(controller.stream);
|
|
completer = new Completer();
|
|
future = errorGroup.registerFuture(completer.future);
|
|
});
|
|
|
|
test("should pipe exceptions from the stream to the future", () {
|
|
expect(stream.first, throwsFormatException);
|
|
expect(future, throwsFormatException);
|
|
expect(errorGroup.done, throwsFormatException);
|
|
|
|
controller.signalError(new AsyncError(new FormatException()));
|
|
});
|
|
|
|
test("should pipe exceptions from the future to the stream", () {
|
|
expect(stream.first, throwsFormatException);
|
|
expect(future, throwsFormatException);
|
|
expect(errorGroup.done, throwsFormatException);
|
|
|
|
completer.completeError(new FormatException());
|
|
});
|
|
|
|
test("the stream and the future should be able to complete/emit values "
|
|
"independently", () {
|
|
expect(stream.toList(), completion(equals(['value1.1', 'value1.2'])));
|
|
expect(future, completion(equals('value2.0')));
|
|
expect(errorGroup.done, completes);
|
|
|
|
controller..add('value1.1')..add('value1.2')..close();
|
|
completer.complete('value2.0');
|
|
});
|
|
|
|
test("shouldn't throw a top-level exception if the stream receives an error "
|
|
"after the listened future completes", () {
|
|
expect(future, completion(equals('value')));
|
|
completer.complete('value');
|
|
|
|
expect(future.then((_) {
|
|
// shouldn't cause a top-level exception
|
|
controller.signalError(new AsyncError(new FormatException()));
|
|
}), completes);
|
|
});
|
|
|
|
test("shouldn't throw a top-level exception if the future receives an "
|
|
"error after the listened stream completes", () {
|
|
expect(stream.toList(), completion(equals(['value1', 'value2'])));
|
|
controller..add('value1')..add('value2')..close();
|
|
|
|
expect(stream.toList().then((_) {
|
|
// shouldn't cause a top-level exception
|
|
completer.completeError(new FormatException());
|
|
}), completes);
|
|
});
|
|
});
|
|
}
|