1476 lines
43 KiB
Dart
1476 lines
43 KiB
Dart
|
import 'dart:async';
|
||
|
|
||
|
import 'package:angel3_reactivex/angel3_reactivex.dart';
|
||
|
import 'package:test/test.dart';
|
||
|
|
||
|
import '../utils.dart';
|
||
|
|
||
|
void main() {
|
||
|
final throwsValueStreamError = throwsA(isA<ValueStreamError>());
|
||
|
|
||
|
group('BehaviorSubject', () {
|
||
|
test('emits the most recently emitted item to every subscriber', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
unseeded.add(1);
|
||
|
unseeded.add(2);
|
||
|
unseeded.add(3);
|
||
|
|
||
|
seeded.add(1);
|
||
|
seeded.add(2);
|
||
|
seeded.add(3);
|
||
|
|
||
|
await expectLater(unseeded.stream, emits(3));
|
||
|
await expectLater(unseeded.stream, emits(3));
|
||
|
await expectLater(unseeded.stream, emits(3));
|
||
|
|
||
|
await expectLater(seeded.stream, emits(3));
|
||
|
await expectLater(seeded.stream, emits(3));
|
||
|
await expectLater(seeded.stream, emits(3));
|
||
|
});
|
||
|
|
||
|
test('emits the most recently emitted null item to every subscriber',
|
||
|
() async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int?>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int?>.seeded(0);
|
||
|
|
||
|
unseeded.add(1);
|
||
|
unseeded.add(2);
|
||
|
unseeded.add(null);
|
||
|
|
||
|
seeded.add(1);
|
||
|
seeded.add(2);
|
||
|
seeded.add(null);
|
||
|
|
||
|
await expectLater(unseeded.stream, emits(isNull));
|
||
|
await expectLater(unseeded.stream, emits(isNull));
|
||
|
await expectLater(unseeded.stream, emits(isNull));
|
||
|
|
||
|
await expectLater(seeded.stream, emits(isNull));
|
||
|
await expectLater(seeded.stream, emits(isNull));
|
||
|
await expectLater(seeded.stream, emits(isNull));
|
||
|
});
|
||
|
|
||
|
test(
|
||
|
'emits the most recently emitted item to every subscriber that subscribe to the subject directly',
|
||
|
() async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
unseeded.add(1);
|
||
|
unseeded.add(2);
|
||
|
unseeded.add(3);
|
||
|
|
||
|
seeded.add(1);
|
||
|
seeded.add(2);
|
||
|
seeded.add(3);
|
||
|
|
||
|
await expectLater(unseeded, emits(3));
|
||
|
await expectLater(unseeded, emits(3));
|
||
|
await expectLater(unseeded, emits(3));
|
||
|
|
||
|
await expectLater(seeded, emits(3));
|
||
|
await expectLater(seeded, emits(3));
|
||
|
await expectLater(seeded, emits(3));
|
||
|
});
|
||
|
|
||
|
test('emits errors to every subscriber', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
unseeded.add(1);
|
||
|
unseeded.add(2);
|
||
|
unseeded.add(3);
|
||
|
unseeded.addError(Exception('oh noes!'));
|
||
|
|
||
|
seeded.add(1);
|
||
|
seeded.add(2);
|
||
|
seeded.add(3);
|
||
|
seeded.addError(Exception('oh noes!'));
|
||
|
|
||
|
await expectLater(unseeded.stream, emitsError(isException));
|
||
|
await expectLater(unseeded.stream, emitsError(isException));
|
||
|
await expectLater(unseeded.stream, emitsError(isException));
|
||
|
|
||
|
await expectLater(seeded.stream, emitsError(isException));
|
||
|
await expectLater(seeded.stream, emitsError(isException));
|
||
|
await expectLater(seeded.stream, emitsError(isException));
|
||
|
});
|
||
|
|
||
|
test('emits event after error to every subscriber', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
unseeded.add(1);
|
||
|
unseeded.add(2);
|
||
|
unseeded.addError(Exception('oh noes!'));
|
||
|
unseeded.add(3);
|
||
|
|
||
|
seeded.add(1);
|
||
|
seeded.add(2);
|
||
|
seeded.addError(Exception('oh noes!'));
|
||
|
seeded.add(3);
|
||
|
|
||
|
await expectLater(unseeded.stream, emits(3));
|
||
|
await expectLater(unseeded.stream, emits(3));
|
||
|
await expectLater(unseeded.stream, emits(3));
|
||
|
|
||
|
await expectLater(seeded.stream, emits(3));
|
||
|
await expectLater(seeded.stream, emits(3));
|
||
|
await expectLater(seeded.stream, emits(3));
|
||
|
});
|
||
|
|
||
|
test('emits errors to every subscriber', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int?>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int?>.seeded(0);
|
||
|
final exception = Exception('oh noes!');
|
||
|
|
||
|
unseeded.add(1);
|
||
|
unseeded.add(2);
|
||
|
unseeded.add(3);
|
||
|
unseeded.addError(exception);
|
||
|
|
||
|
seeded.add(1);
|
||
|
seeded.add(2);
|
||
|
seeded.add(3);
|
||
|
seeded.addError(exception);
|
||
|
|
||
|
expect(unseeded.value, 3);
|
||
|
expect(unseeded.valueOrNull, 3);
|
||
|
expect(unseeded.hasValue, true);
|
||
|
|
||
|
expect(unseeded.error, exception);
|
||
|
expect(unseeded.errorOrNull, exception);
|
||
|
expect(unseeded.hasError, true);
|
||
|
|
||
|
await expectLater(unseeded, emitsError(exception));
|
||
|
await expectLater(unseeded, emitsError(exception));
|
||
|
await expectLater(unseeded, emitsError(exception));
|
||
|
|
||
|
expect(seeded.value, 3);
|
||
|
expect(seeded.valueOrNull, 3);
|
||
|
expect(seeded.hasValue, true);
|
||
|
|
||
|
expect(seeded.error, exception);
|
||
|
expect(seeded.errorOrNull, exception);
|
||
|
expect(seeded.hasError, true);
|
||
|
|
||
|
await expectLater(seeded, emitsError(exception));
|
||
|
await expectLater(seeded, emitsError(exception));
|
||
|
await expectLater(seeded, emitsError(exception));
|
||
|
});
|
||
|
|
||
|
test('can synchronously get the latest value', () {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
unseeded.add(1);
|
||
|
unseeded.add(2);
|
||
|
unseeded.add(3);
|
||
|
|
||
|
seeded.add(1);
|
||
|
seeded.add(2);
|
||
|
seeded.add(3);
|
||
|
|
||
|
expect(unseeded.value, 3);
|
||
|
expect(unseeded.valueOrNull, 3);
|
||
|
expect(unseeded.hasValue, true);
|
||
|
|
||
|
expect(seeded.value, 3);
|
||
|
expect(seeded.valueOrNull, 3);
|
||
|
expect(seeded.hasValue, true);
|
||
|
});
|
||
|
|
||
|
test('can synchronously get the latest null value', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int?>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int?>.seeded(0);
|
||
|
|
||
|
unseeded.add(1);
|
||
|
unseeded.add(2);
|
||
|
unseeded.add(null);
|
||
|
|
||
|
seeded.add(1);
|
||
|
seeded.add(2);
|
||
|
seeded.add(null);
|
||
|
|
||
|
expect(unseeded.value, isNull);
|
||
|
expect(unseeded.valueOrNull, isNull);
|
||
|
expect(unseeded.hasValue, true);
|
||
|
|
||
|
expect(seeded.value, isNull);
|
||
|
expect(seeded.valueOrNull, isNull);
|
||
|
expect(seeded.hasValue, true);
|
||
|
});
|
||
|
|
||
|
test('emits the seed item if no new items have been emitted', () async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>.seeded(1);
|
||
|
|
||
|
await expectLater(subject.stream, emits(1));
|
||
|
await expectLater(subject.stream, emits(1));
|
||
|
await expectLater(subject.stream, emits(1));
|
||
|
});
|
||
|
|
||
|
test('emits the null seed item if no new items have been emitted',
|
||
|
() async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int?>.seeded(null);
|
||
|
|
||
|
await expectLater(subject.stream, emits(isNull));
|
||
|
await expectLater(subject.stream, emits(isNull));
|
||
|
await expectLater(subject.stream, emits(isNull));
|
||
|
});
|
||
|
|
||
|
test('can synchronously get the initial value', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>.seeded(1);
|
||
|
|
||
|
expect(subject.value, 1);
|
||
|
expect(subject.valueOrNull, 1);
|
||
|
expect(subject.hasValue, true);
|
||
|
});
|
||
|
|
||
|
test('can synchronously get the initial null value', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int?>.seeded(null);
|
||
|
|
||
|
expect(subject.value, null);
|
||
|
expect(subject.valueOrNull, null);
|
||
|
expect(subject.hasValue, true);
|
||
|
});
|
||
|
|
||
|
test('initial value is null when no value has been emitted', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
expect(() => subject.value, throwsValueStreamError);
|
||
|
expect(subject.valueOrNull, null);
|
||
|
expect(subject.hasValue, false);
|
||
|
});
|
||
|
|
||
|
test('emits done event to listeners when the subject is closed', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
await expectLater(unseeded.isClosed, isFalse);
|
||
|
await expectLater(seeded.isClosed, isFalse);
|
||
|
|
||
|
unseeded.add(1);
|
||
|
scheduleMicrotask(() => unseeded.close());
|
||
|
|
||
|
seeded.add(1);
|
||
|
scheduleMicrotask(() => seeded.close());
|
||
|
|
||
|
await expectLater(unseeded.stream, emitsInOrder(<dynamic>[1, emitsDone]));
|
||
|
await expectLater(unseeded.isClosed, isTrue);
|
||
|
|
||
|
await expectLater(seeded.stream, emitsInOrder(<dynamic>[1, emitsDone]));
|
||
|
await expectLater(seeded.isClosed, isTrue);
|
||
|
});
|
||
|
|
||
|
test('emits error events to subscribers', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
scheduleMicrotask(() => unseeded.addError(Exception()));
|
||
|
scheduleMicrotask(() => seeded.addError(Exception()));
|
||
|
|
||
|
await expectLater(unseeded.stream, emitsError(isException));
|
||
|
await expectLater(seeded.stream, emitsError(isException));
|
||
|
});
|
||
|
|
||
|
test('replays the previously emitted items from addStream', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
await unseeded.addStream(Stream.fromIterable(const [1, 2, 3]));
|
||
|
await seeded.addStream(Stream.fromIterable(const [1, 2, 3]));
|
||
|
|
||
|
await expectLater(unseeded.stream, emits(3));
|
||
|
await expectLater(unseeded.stream, emits(3));
|
||
|
await expectLater(unseeded.stream, emits(3));
|
||
|
|
||
|
await expectLater(seeded.stream, emits(3));
|
||
|
await expectLater(seeded.stream, emits(3));
|
||
|
await expectLater(seeded.stream, emits(3));
|
||
|
});
|
||
|
|
||
|
test('replays the previously emitted errors from addStream', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
await unseeded.addStream(Stream<int>.error('error'),
|
||
|
cancelOnError: false);
|
||
|
await seeded.addStream(Stream<int>.error('error'), cancelOnError: false);
|
||
|
|
||
|
await expectLater(unseeded.stream, emitsError('error'));
|
||
|
await expectLater(unseeded.stream, emitsError('error'));
|
||
|
});
|
||
|
|
||
|
test('allows items to be added once addStream is complete', () async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
await subject.addStream(Stream.fromIterable(const [1, 2]));
|
||
|
subject.add(3);
|
||
|
|
||
|
await expectLater(subject.stream, emits(3));
|
||
|
});
|
||
|
|
||
|
test('allows items to be added once addStream completes with an error',
|
||
|
() async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
unawaited(subject
|
||
|
.addStream(Stream<int>.error(Exception()), cancelOnError: true)
|
||
|
.whenComplete(() => subject.add(1)));
|
||
|
|
||
|
await expectLater(subject.stream,
|
||
|
emitsInOrder(<StreamMatcher>[emitsError(isException), emits(1)]));
|
||
|
});
|
||
|
|
||
|
test('does not allow events to be added when addStream is active',
|
||
|
() async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
// Purposely don't wait for the future to complete, then try to add items
|
||
|
// ignore: unawaited_futures
|
||
|
subject.addStream(Stream.fromIterable(const [1, 2, 3]));
|
||
|
|
||
|
await expectLater(() => subject.add(1), throwsStateError);
|
||
|
});
|
||
|
|
||
|
test('does not allow errors to be added when addStream is active',
|
||
|
() async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
// Purposely don't wait for the future to complete, then try to add items
|
||
|
// ignore: unawaited_futures
|
||
|
subject.addStream(Stream.fromIterable(const [1, 2, 3]));
|
||
|
|
||
|
await expectLater(() => subject.addError(Error()), throwsStateError);
|
||
|
});
|
||
|
|
||
|
test('does not allow subject to be closed when addStream is active',
|
||
|
() async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
// Purposely don't wait for the future to complete, then try to add items
|
||
|
// ignore: unawaited_futures
|
||
|
subject.addStream(Stream.fromIterable(const [1, 2, 3]));
|
||
|
|
||
|
await expectLater(() => subject.close(), throwsStateError);
|
||
|
});
|
||
|
|
||
|
test(
|
||
|
'does not allow addStream to add items when previous addStream is active',
|
||
|
() async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
// Purposely don't wait for the future to complete, then try to add items
|
||
|
// ignore: unawaited_futures
|
||
|
subject.addStream(Stream.fromIterable(const [1, 2, 3]));
|
||
|
|
||
|
await expectLater(() => subject.addStream(Stream.fromIterable(const [1])),
|
||
|
throwsStateError);
|
||
|
});
|
||
|
|
||
|
test('returns onListen callback set in constructor', () async {
|
||
|
void testOnListen() {}
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<void>(onListen: testOnListen);
|
||
|
|
||
|
await expectLater(subject.onListen, testOnListen);
|
||
|
});
|
||
|
|
||
|
test('sets onListen callback', () async {
|
||
|
void testOnListen() {}
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<void>();
|
||
|
|
||
|
await expectLater(subject.onListen, isNull);
|
||
|
|
||
|
subject.onListen = testOnListen;
|
||
|
|
||
|
await expectLater(subject.onListen, testOnListen);
|
||
|
});
|
||
|
|
||
|
test('returns onCancel callback set in constructor', () async {
|
||
|
Future<void> onCancel() => Future<void>.value(null);
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<void>(onCancel: onCancel);
|
||
|
|
||
|
await expectLater(subject.onCancel, onCancel);
|
||
|
});
|
||
|
|
||
|
test('sets onCancel callback', () async {
|
||
|
void testOnCancel() {}
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<void>();
|
||
|
|
||
|
await expectLater(subject.onCancel, isNull);
|
||
|
|
||
|
subject.onCancel = testOnCancel;
|
||
|
|
||
|
await expectLater(subject.onCancel, testOnCancel);
|
||
|
});
|
||
|
|
||
|
test('reports if a listener is present', () async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
await expectLater(subject.hasListener, isFalse);
|
||
|
|
||
|
subject.stream.listen(null);
|
||
|
|
||
|
await expectLater(subject.hasListener, isTrue);
|
||
|
});
|
||
|
|
||
|
test('onPause unsupported', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
expect(subject.isPaused, isFalse);
|
||
|
expect(() => subject.onPause, throwsUnsupportedError);
|
||
|
expect(() => subject.onPause = () {}, throwsUnsupportedError);
|
||
|
});
|
||
|
|
||
|
test('onResume unsupported', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
expect(() => subject.onResume, throwsUnsupportedError);
|
||
|
expect(() => subject.onResume = () {}, throwsUnsupportedError);
|
||
|
});
|
||
|
|
||
|
test('returns controller sink', () async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
await expectLater(subject.sink, TypeMatcher<EventSink<int>>());
|
||
|
});
|
||
|
|
||
|
test('correctly closes done Future', () async {
|
||
|
final subject = BehaviorSubject<void>();
|
||
|
|
||
|
scheduleMicrotask(() => subject.close());
|
||
|
|
||
|
await expectLater(subject.done, completes);
|
||
|
});
|
||
|
|
||
|
test('can be listened to multiple times', () async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject.seeded(1);
|
||
|
final stream = subject.stream;
|
||
|
|
||
|
await expectLater(stream, emits(1));
|
||
|
await expectLater(stream, emits(1));
|
||
|
});
|
||
|
|
||
|
test('always returns the same stream', () async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
await expectLater(subject.stream, equals(subject.stream));
|
||
|
});
|
||
|
|
||
|
test('adding to sink has same behavior as adding to Subject itself',
|
||
|
() async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
subject.sink.add(1);
|
||
|
|
||
|
expect(subject.value, 1);
|
||
|
|
||
|
subject.sink.add(2);
|
||
|
subject.sink.add(3);
|
||
|
|
||
|
await expectLater(subject.stream, emits(3));
|
||
|
await expectLater(subject.stream, emits(3));
|
||
|
await expectLater(subject.stream, emits(3));
|
||
|
});
|
||
|
|
||
|
test('setter `value=` has same behavior as adding to Subject', () async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
subject.value = 1;
|
||
|
|
||
|
expect(subject.value, 1);
|
||
|
|
||
|
subject.value = 2;
|
||
|
subject.value = 3;
|
||
|
|
||
|
await expectLater(subject.stream, emits(3));
|
||
|
await expectLater(subject.stream, emits(3));
|
||
|
await expectLater(subject.stream, emits(3));
|
||
|
});
|
||
|
|
||
|
test('is always treated as a broadcast Stream', () async {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
final stream = subject.asyncMap((event) => Future.value(event));
|
||
|
|
||
|
expect(subject.isBroadcast, isTrue);
|
||
|
expect(stream.isBroadcast, isTrue);
|
||
|
});
|
||
|
|
||
|
test('hasValue returns false for an empty subject', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
expect(subject.hasValue, isFalse);
|
||
|
});
|
||
|
|
||
|
test('hasValue returns true for a seeded subject with non-null seed', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>.seeded(1);
|
||
|
|
||
|
expect(subject.hasValue, isTrue);
|
||
|
});
|
||
|
|
||
|
test('hasValue returns true for a seeded subject with null seed', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int?>.seeded(null);
|
||
|
|
||
|
expect(subject.hasValue, isTrue);
|
||
|
});
|
||
|
|
||
|
test('hasValue returns true for an unseeded subject after an emission', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
subject.add(1);
|
||
|
|
||
|
expect(subject.hasValue, isTrue);
|
||
|
});
|
||
|
|
||
|
test('hasError returns false for an empty subject', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
expect(subject.hasError, isFalse);
|
||
|
});
|
||
|
|
||
|
test('hasError returns false for a seeded subject with non-null seed', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>.seeded(1);
|
||
|
|
||
|
expect(subject.hasError, isFalse);
|
||
|
});
|
||
|
|
||
|
test('hasError returns false for a seeded subject with null seed', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int?>.seeded(null);
|
||
|
|
||
|
expect(subject.hasError, isFalse);
|
||
|
});
|
||
|
|
||
|
test('hasError returns false for an unseeded subject after an emission',
|
||
|
() {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
subject.add(1);
|
||
|
|
||
|
expect(subject.hasError, isFalse);
|
||
|
});
|
||
|
|
||
|
test('hasError returns true for an unseeded subject after addError', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
subject.add(1);
|
||
|
subject.addError('error');
|
||
|
|
||
|
expect(subject.hasError, isTrue);
|
||
|
});
|
||
|
|
||
|
test('hasError returns true for a seeded subject after addError', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>.seeded(1);
|
||
|
|
||
|
subject.addError('error');
|
||
|
|
||
|
expect(subject.hasError, isTrue);
|
||
|
});
|
||
|
|
||
|
test('error returns null for an empty subject', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>();
|
||
|
|
||
|
expect(subject.hasError, isFalse);
|
||
|
expect(subject.errorOrNull, isNull);
|
||
|
expect(() => subject.error, throwsValueStreamError);
|
||
|
});
|
||
|
|
||
|
test('error returns null for a seeded subject with non-null seed', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int>.seeded(1);
|
||
|
|
||
|
expect(subject.hasError, isFalse);
|
||
|
expect(subject.errorOrNull, isNull);
|
||
|
expect(() => subject.error, throwsValueStreamError);
|
||
|
});
|
||
|
|
||
|
test('error returns null for a seeded subject with null seed', () {
|
||
|
// ignore: close_sinks
|
||
|
final subject = BehaviorSubject<int?>.seeded(null);
|
||
|
|
||
|
expect(subject.hasError, isFalse);
|
||
|
expect(subject.errorOrNull, isNull);
|
||
|
expect(() => subject.error, throwsValueStreamError);
|
||
|
});
|
||
|
|
||
|
test('can synchronously get the latest error', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
unseeded.add(1);
|
||
|
unseeded.add(2);
|
||
|
unseeded.add(3);
|
||
|
expect(unseeded.hasError, isFalse);
|
||
|
expect(unseeded.errorOrNull, isNull);
|
||
|
expect(() => unseeded.error, throwsValueStreamError);
|
||
|
|
||
|
unseeded.addError(Exception('oh noes!'));
|
||
|
expect(unseeded.hasError, isTrue);
|
||
|
expect(unseeded.errorOrNull, isException);
|
||
|
expect(unseeded.error, isException);
|
||
|
|
||
|
seeded.add(1);
|
||
|
seeded.add(2);
|
||
|
seeded.add(3);
|
||
|
expect(seeded.hasError, isFalse);
|
||
|
expect(seeded.errorOrNull, isNull);
|
||
|
expect(() => seeded.error, throwsValueStreamError);
|
||
|
|
||
|
seeded.addError(Exception('oh noes!'));
|
||
|
expect(seeded.hasError, isTrue);
|
||
|
expect(seeded.errorOrNull, isException);
|
||
|
expect(seeded.error, isException);
|
||
|
});
|
||
|
|
||
|
test('emits event after error to every subscriber', () async {
|
||
|
// ignore: close_sinks
|
||
|
final unseeded = BehaviorSubject<int>(),
|
||
|
// ignore: close_sinks
|
||
|
seeded = BehaviorSubject<int>.seeded(0);
|
||
|
|
||
|
unseeded.add(1);
|
||
|
unseeded.add(2);
|
||
|
unseeded.addError(Exception('oh noes!'));
|
||
|
expect(unseeded.hasError, isTrue);
|
||
|
expect(unseeded.errorOrNull, isException);
|
||
|
expect(unseeded.error, isException);
|
||
|
unseeded.add(3);
|
||
|
expect(unseeded.hasError, isTrue);
|
||
|
expect(unseeded.errorOrNull, isException);
|
||
|
expect(unseeded.error, isException);
|
||
|
|
||
|
seeded.add(1);
|
||
|
seeded.add(2);
|
||
|
seeded.addError(Exception('oh noes!'));
|
||
|
expect(seeded.hasError, isTrue);
|
||
|
expect(seeded.errorOrNull, isException);
|
||
|
expect(seeded.error, isException);
|
||
|
seeded.add(3);
|
||
|
expect(seeded.hasError, isTrue);
|
||
|
expect(seeded.errorOrNull, isException);
|
||
|
expect(seeded.error, isException);
|
||
|
});
|
||
|
|
||
|
test(
|
||
|
'issue/350: emits duplicate values when listening multiple times and starting with an Error',
|
||
|
() async {
|
||
|
final subject = BehaviorSubject<dynamic>();
|
||
|
|
||
|
subject.addError('error');
|
||
|
|
||
|
await subject.close();
|
||
|
|
||
|
await expectLater(subject,
|
||
|
emitsInOrder(<StreamMatcher>[emitsError('error'), emitsDone]));
|
||
|
await expectLater(subject,
|
||
|
emitsInOrder(<StreamMatcher>[emitsError('error'), emitsDone]));
|
||
|
await expectLater(subject,
|
||
|
emitsInOrder(<StreamMatcher>[emitsError('error'), emitsDone]));
|
||
|
});
|
||
|
|
||
|
test('issue/419: sync behavior', () async {
|
||
|
final subject = BehaviorSubject.seeded(1, sync: true);
|
||
|
final mappedStream = subject.map((event) => event).shareValue();
|
||
|
|
||
|
mappedStream.listen(null);
|
||
|
|
||
|
expect(mappedStream.value, equals(1));
|
||
|
|
||
|
await subject.close();
|
||
|
}, skip: true);
|
||
|
|
||
|
test('issue/419: sync throughput', () async {
|
||
|
final subject = BehaviorSubject.seeded(1, sync: true);
|
||
|
final mappedStream = subject.map((event) => event).shareValue();
|
||
|
|
||
|
mappedStream.listen(null);
|
||
|
|
||
|
subject.add(2);
|
||
|
|
||
|
expect(mappedStream.value, equals(2));
|
||
|
|
||
|
await subject.close();
|
||
|
}, skip: true);
|
||
|
|
||
|
test('issue/419: async behavior', () async {
|
||
|
final subject = BehaviorSubject.seeded(1);
|
||
|
final mappedStream = subject.map((event) => event).shareValue();
|
||
|
|
||
|
mappedStream.listen(null,
|
||
|
onDone: () => expect(mappedStream.value, equals(1)));
|
||
|
|
||
|
expect(() => mappedStream.value, throwsValueStreamError);
|
||
|
expect(mappedStream.valueOrNull, isNull);
|
||
|
expect(mappedStream.hasValue, false);
|
||
|
|
||
|
await subject.close();
|
||
|
});
|
||
|
|
||
|
test('issue/419: async throughput', () async {
|
||
|
final subject = BehaviorSubject.seeded(1);
|
||
|
final mappedStream = subject.map((event) => event).shareValue();
|
||
|
|
||
|
mappedStream.listen(null,
|
||
|
onDone: () => expect(mappedStream.value, equals(2)));
|
||
|
|
||
|
subject.add(2);
|
||
|
|
||
|
expect(() => mappedStream.value, throwsValueStreamError);
|
||
|
expect(mappedStream.valueOrNull, isNull);
|
||
|
expect(mappedStream.hasValue, false);
|
||
|
|
||
|
await subject.close();
|
||
|
});
|
||
|
|
||
|
test('issue/477: get first after cancelled', () async {
|
||
|
final a = BehaviorSubject.seeded('a');
|
||
|
final bug = a.switchMap((v) => BehaviorSubject.seeded('b'));
|
||
|
await bug.listen(null).cancel();
|
||
|
expect(await bug.first, 'b');
|
||
|
});
|
||
|
|
||
|
test('issue/477: get first multiple times', () async {
|
||
|
final a = BehaviorSubject.seeded('a');
|
||
|
final bug = a.switchMap((_) => BehaviorSubject.seeded('b'));
|
||
|
bug.listen(null);
|
||
|
expect(await bug.first, 'b');
|
||
|
expect(await bug.first, 'b');
|
||
|
});
|
||
|
|
||
|
test('issue/478: get first multiple times', () async {
|
||
|
final a = BehaviorSubject.seeded('a');
|
||
|
final b = BehaviorSubject.seeded('b');
|
||
|
final bug =
|
||
|
Rx.combineLatest2(a, b, (String _a, String _b) => 'ab').shareValue();
|
||
|
expect(await bug.first, 'ab');
|
||
|
expect(await bug.first, 'ab');
|
||
|
});
|
||
|
|
||
|
test('angel3_reactivex #477/#500 - a', () async {
|
||
|
final a = BehaviorSubject.seeded('a')
|
||
|
.switchMap((_) => BehaviorSubject.seeded('a'))
|
||
|
..listen(print);
|
||
|
await pumpEventQueue();
|
||
|
expect(await a.first, 'a');
|
||
|
});
|
||
|
|
||
|
test('angel3_reactivex #477/#500 - b', () async {
|
||
|
final b = BehaviorSubject.seeded('b')
|
||
|
.map((_) => 'b')
|
||
|
.switchMap((_) => BehaviorSubject.seeded('b'))
|
||
|
..listen(print);
|
||
|
await pumpEventQueue();
|
||
|
expect(await b.first, 'b');
|
||
|
});
|
||
|
|
||
|
test('issue/587', () async {
|
||
|
final source = BehaviorSubject.seeded('source');
|
||
|
final switched =
|
||
|
source.switchMap((value) => BehaviorSubject.seeded('switched'));
|
||
|
var i = 0;
|
||
|
switched.listen((_) => i++);
|
||
|
expect(await switched.first, 'switched');
|
||
|
expect(i, 1);
|
||
|
expect(await switched.first, 'switched');
|
||
|
expect(i, 1);
|
||
|
});
|
||
|
|
||
|
test('do not update latest value after closed', () {
|
||
|
final seeded = BehaviorSubject.seeded(0);
|
||
|
final unseeded = BehaviorSubject<int>();
|
||
|
|
||
|
seeded.add(1);
|
||
|
unseeded.add(1);
|
||
|
|
||
|
expect(seeded.value, 1);
|
||
|
expect(unseeded.value, 1);
|
||
|
|
||
|
seeded.close();
|
||
|
unseeded.close();
|
||
|
|
||
|
expect(() => seeded.add(2), throwsStateError);
|
||
|
expect(() => unseeded.add(2), throwsStateError);
|
||
|
expect(() => seeded.addError(Exception()), throwsStateError);
|
||
|
expect(() => unseeded.addError(Exception()), throwsStateError);
|
||
|
|
||
|
expect(seeded.value, 1);
|
||
|
expect(unseeded.value, 1);
|
||
|
});
|
||
|
|
||
|
group('override built-in', () {
|
||
|
test('where', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject.where((event) => event.isOdd);
|
||
|
expect(stream, emitsInOrder(<int>[1, 3]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream = behaviorSubject.where((event) => event.isOdd);
|
||
|
expect(stream, emitsInOrder(<int>[1, 3]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('map', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var mapped = behaviorSubject.map((event) => event + 1);
|
||
|
expect(mapped, emitsInOrder(<int>[2, 3]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var mapped = behaviorSubject.map((event) => event + 1);
|
||
|
expect(mapped, emitsInOrder(<int>[2, 3]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('asyncMap', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var mapped =
|
||
|
behaviorSubject.asyncMap((event) => Future.value(event + 1));
|
||
|
expect(mapped, emitsInOrder(<int>[2, 3]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var mapped =
|
||
|
behaviorSubject.asyncMap((event) => Future.value(event + 1));
|
||
|
expect(mapped, emitsInOrder(<int>[2, 3]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('asyncExpand', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream =
|
||
|
behaviorSubject.asyncExpand((event) => Stream.value(event + 1));
|
||
|
expect(stream, emitsInOrder(<int>[2, 3]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream =
|
||
|
behaviorSubject.asyncExpand((event) => Stream.value(event + 1));
|
||
|
expect(stream, emitsInOrder(<int>[2, 3]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('handleError', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject.handleError(
|
||
|
expectAsync1<void, dynamic>(
|
||
|
(dynamic e) => expect(e, isException),
|
||
|
count: 1,
|
||
|
),
|
||
|
);
|
||
|
|
||
|
expect(
|
||
|
stream,
|
||
|
emitsInOrder(<int>[1, 2]),
|
||
|
);
|
||
|
|
||
|
behaviorSubject.addError(Exception());
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream = behaviorSubject.handleError(
|
||
|
expectAsync1<void, dynamic>(
|
||
|
(dynamic e) => expect(e, isException),
|
||
|
count: 1,
|
||
|
),
|
||
|
);
|
||
|
|
||
|
expect(
|
||
|
stream,
|
||
|
emitsInOrder(<int>[1, 2]),
|
||
|
);
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.addError(Exception());
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('expand', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject.expand((event) => [event + 1]);
|
||
|
expect(stream, emitsInOrder(<int>[2, 3]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream = behaviorSubject.expand((event) => [event + 1]);
|
||
|
expect(stream, emitsInOrder(<int>[2, 3]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('transform', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject.transform(
|
||
|
IntervalStreamTransformer(const Duration(milliseconds: 100)));
|
||
|
expect(stream, emitsInOrder(<int>[1, 2]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream = behaviorSubject.transform(
|
||
|
IntervalStreamTransformer(const Duration(milliseconds: 100)));
|
||
|
expect(stream, emitsInOrder(<int>[1, 2]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('cast', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<Object>.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject.cast<int>();
|
||
|
expect(stream, emitsInOrder(<int>[1, 2]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<Object>();
|
||
|
|
||
|
var stream = behaviorSubject.cast<int>();
|
||
|
expect(stream, emitsInOrder(<int>[1, 2]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('take', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject.take(2);
|
||
|
expect(stream, emitsInOrder(<int>[1, 2]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream = behaviorSubject.take(2);
|
||
|
expect(stream, emitsInOrder(<int>[1, 2]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('takeWhile', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject.takeWhile((element) => element <= 2);
|
||
|
expect(stream, emitsInOrder(<int>[1, 2]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream = behaviorSubject.takeWhile((element) => element <= 2);
|
||
|
expect(stream, emitsInOrder(<int>[1, 2]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('skip', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject.skip(2);
|
||
|
expect(stream, emitsInOrder(<int>[3, 4]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
behaviorSubject.add(4);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream = behaviorSubject.skip(2);
|
||
|
expect(stream, emitsInOrder(<int>[3, 4]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
behaviorSubject.add(4);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('skipWhile', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject.skipWhile((element) => element < 3);
|
||
|
expect(stream, emitsInOrder(<int>[3, 4]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
behaviorSubject.add(4);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream = behaviorSubject.skipWhile((element) => element < 3);
|
||
|
expect(stream, emitsInOrder(<int>[3, 4]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
behaviorSubject.add(4);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('distinct', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject.distinct();
|
||
|
expect(stream, emitsInOrder(<int>[1, 2]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream = behaviorSubject.distinct();
|
||
|
expect(stream, emitsInOrder(<int>[1, 2]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(2);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('timeout', () {
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject.seeded(1);
|
||
|
|
||
|
var stream = behaviorSubject
|
||
|
.interval(const Duration(milliseconds: 100))
|
||
|
.timeout(
|
||
|
const Duration(milliseconds: 70),
|
||
|
onTimeout: expectAsync1(
|
||
|
(EventSink<int> sink) {},
|
||
|
count: 4,
|
||
|
),
|
||
|
);
|
||
|
|
||
|
expect(stream, emitsInOrder(<int>[1, 2, 3, 4]));
|
||
|
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
behaviorSubject.add(4);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var behaviorSubject = BehaviorSubject<int>();
|
||
|
|
||
|
var stream = behaviorSubject
|
||
|
.interval(const Duration(milliseconds: 100))
|
||
|
.timeout(
|
||
|
const Duration(milliseconds: 70),
|
||
|
onTimeout: expectAsync1(
|
||
|
(EventSink<int> sink) {},
|
||
|
count: 4,
|
||
|
),
|
||
|
);
|
||
|
|
||
|
expect(stream, emitsInOrder(<int>[1, 2, 3, 4]));
|
||
|
|
||
|
behaviorSubject.add(1);
|
||
|
behaviorSubject.add(2);
|
||
|
behaviorSubject.add(3);
|
||
|
behaviorSubject.add(4);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
|
||
|
test('stream returns a read-only stream', () async {
|
||
|
final subject = BehaviorSubject<int>()..add(1);
|
||
|
|
||
|
// streams returned by BehaviorSubject are read-only stream,
|
||
|
// ie. they don't support adding events.
|
||
|
expect(subject.stream, isNot(isA<BehaviorSubject<int>>()));
|
||
|
expect(subject.stream, isNot(isA<Sink<int>>()));
|
||
|
|
||
|
expect(
|
||
|
subject.stream,
|
||
|
isA<ValueStream<int>>().having(
|
||
|
(v) => v.value,
|
||
|
'BehaviorSubject.stream.value',
|
||
|
1,
|
||
|
),
|
||
|
);
|
||
|
|
||
|
// BehaviorSubject.stream is a broadcast stream
|
||
|
{
|
||
|
final stream = subject.stream;
|
||
|
expect(stream.isBroadcast, isTrue);
|
||
|
await expectLater(stream, emitsInOrder(<Object>[1]));
|
||
|
await expectLater(stream, emitsInOrder(<Object>[1]));
|
||
|
}
|
||
|
|
||
|
// streams returned by the same subject are considered equal,
|
||
|
// but not identical
|
||
|
expect(identical(subject.stream, subject.stream), isFalse);
|
||
|
expect(subject.stream == subject.stream, isTrue);
|
||
|
});
|
||
|
|
||
|
group('lastEventOrNull', () {
|
||
|
test('empty subject', () {
|
||
|
final s = BehaviorSubject<int>();
|
||
|
expect(s.lastEventOrNull, isNull);
|
||
|
expect(s.isLastEventValue, isFalse);
|
||
|
expect(s.isLastEventError, isFalse);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(s.stream.lastEventOrNull, isNull);
|
||
|
expect(s.stream.isLastEventValue, isFalse);
|
||
|
expect(s.stream.isLastEventError, isFalse);
|
||
|
});
|
||
|
|
||
|
test('subject with value', () {
|
||
|
final s = BehaviorSubject<int>.seeded(42);
|
||
|
expect(
|
||
|
s.lastEventOrNull,
|
||
|
StreamNotification<int>.data(42),
|
||
|
);
|
||
|
expect(s.isLastEventValue, isTrue);
|
||
|
expect(s.isLastEventError, isFalse);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(
|
||
|
s.stream.lastEventOrNull,
|
||
|
StreamNotification<int>.data(42),
|
||
|
);
|
||
|
expect(s.stream.isLastEventValue, isTrue);
|
||
|
expect(s.stream.isLastEventError, isFalse);
|
||
|
});
|
||
|
|
||
|
test('subject with error', () {
|
||
|
final s = BehaviorSubject<int>();
|
||
|
final exception = Exception();
|
||
|
s.addError(exception, StackTrace.empty);
|
||
|
|
||
|
expect(
|
||
|
s.lastEventOrNull,
|
||
|
StreamNotification<int>.error(exception, StackTrace.empty),
|
||
|
);
|
||
|
expect(s.isLastEventValue, isFalse);
|
||
|
expect(s.isLastEventError, isTrue);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(
|
||
|
s.stream.lastEventOrNull,
|
||
|
StreamNotification<int>.error(exception, StackTrace.empty),
|
||
|
);
|
||
|
expect(s.stream.isLastEventValue, isFalse);
|
||
|
expect(s.stream.isLastEventError, isTrue);
|
||
|
});
|
||
|
|
||
|
test('add error and then value', () {
|
||
|
final s = BehaviorSubject<int>();
|
||
|
final exception = Exception();
|
||
|
s.addError(exception, StackTrace.empty);
|
||
|
s.add(42);
|
||
|
|
||
|
expect(
|
||
|
s.lastEventOrNull,
|
||
|
StreamNotification<int>.data(42),
|
||
|
);
|
||
|
expect(s.isLastEventValue, isTrue);
|
||
|
expect(s.isLastEventError, isFalse);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(
|
||
|
s.stream.lastEventOrNull,
|
||
|
StreamNotification<int>.data(42),
|
||
|
);
|
||
|
expect(s.stream.isLastEventValue, isTrue);
|
||
|
expect(s.stream.isLastEventError, isFalse);
|
||
|
});
|
||
|
|
||
|
test('add value and then error', () {
|
||
|
final s = BehaviorSubject<int>();
|
||
|
s.add(42);
|
||
|
final exception = Exception();
|
||
|
s.addError(exception, StackTrace.empty);
|
||
|
|
||
|
expect(
|
||
|
s.lastEventOrNull,
|
||
|
StreamNotification<int>.error(exception, StackTrace.empty),
|
||
|
);
|
||
|
expect(s.isLastEventValue, isFalse);
|
||
|
expect(s.isLastEventError, isTrue);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(
|
||
|
s.stream.lastEventOrNull,
|
||
|
StreamNotification<int>.error(exception, StackTrace.empty),
|
||
|
);
|
||
|
expect(s.stream.isLastEventValue, isFalse);
|
||
|
expect(s.stream.isLastEventError, isTrue);
|
||
|
});
|
||
|
|
||
|
test('add value and then close', () async {
|
||
|
final s = BehaviorSubject<int>();
|
||
|
s.add(42);
|
||
|
await s.close();
|
||
|
|
||
|
expect(
|
||
|
s.lastEventOrNull,
|
||
|
StreamNotification<int>.data(42),
|
||
|
);
|
||
|
expect(s.isLastEventValue, isTrue);
|
||
|
expect(s.isLastEventError, isFalse);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(
|
||
|
s.stream.lastEventOrNull,
|
||
|
StreamNotification<int>.data(42),
|
||
|
);
|
||
|
expect(s.stream.isLastEventValue, isTrue);
|
||
|
expect(s.stream.isLastEventError, isFalse);
|
||
|
});
|
||
|
|
||
|
test('add error and then close', () async {
|
||
|
final s = BehaviorSubject<int>();
|
||
|
final exception = Exception();
|
||
|
s.addError(exception, StackTrace.empty);
|
||
|
await s.close();
|
||
|
|
||
|
expect(
|
||
|
s.lastEventOrNull,
|
||
|
StreamNotification<int>.error(exception, StackTrace.empty),
|
||
|
);
|
||
|
expect(s.isLastEventValue, isFalse);
|
||
|
expect(s.isLastEventError, isTrue);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(
|
||
|
s.stream.lastEventOrNull,
|
||
|
StreamNotification<int>.error(exception, StackTrace.empty),
|
||
|
);
|
||
|
expect(s.stream.isLastEventValue, isFalse);
|
||
|
expect(s.stream.isLastEventError, isTrue);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
group('errorAndStackTraceOrNull', () {
|
||
|
test('empty subject', () {
|
||
|
final s = BehaviorSubject<int>();
|
||
|
expect(s.errorAndStackTraceOrNull, isNull);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(s.stream.errorAndStackTraceOrNull, isNull);
|
||
|
});
|
||
|
|
||
|
test('seeded subject', () {
|
||
|
final s = BehaviorSubject<int>.seeded(42);
|
||
|
expect(s.errorAndStackTraceOrNull, isNull);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(s.stream.errorAndStackTraceOrNull, isNull);
|
||
|
});
|
||
|
|
||
|
test('subject with error and stack trace', () {
|
||
|
final s = BehaviorSubject<int>();
|
||
|
final exception = Exception();
|
||
|
s.addError(exception, StackTrace.empty);
|
||
|
|
||
|
expect(
|
||
|
s.errorAndStackTraceOrNull,
|
||
|
ErrorAndStackTrace(exception, StackTrace.empty),
|
||
|
);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(
|
||
|
s.stream.errorAndStackTraceOrNull,
|
||
|
ErrorAndStackTrace(exception, StackTrace.empty),
|
||
|
);
|
||
|
});
|
||
|
|
||
|
test('subject with error', () {
|
||
|
final s = BehaviorSubject<int>();
|
||
|
final exception = Exception();
|
||
|
s.addError(exception);
|
||
|
|
||
|
expect(
|
||
|
s.errorAndStackTraceOrNull,
|
||
|
ErrorAndStackTrace(exception, null),
|
||
|
);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(
|
||
|
s.stream.errorAndStackTraceOrNull,
|
||
|
ErrorAndStackTrace(exception, null),
|
||
|
);
|
||
|
});
|
||
|
|
||
|
test('seeded subject and close', () {
|
||
|
final s = BehaviorSubject<int>.seeded(42)..close();
|
||
|
|
||
|
expect(s.errorAndStackTraceOrNull, isNull);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(s.stream.errorAndStackTraceOrNull, isNull);
|
||
|
});
|
||
|
|
||
|
test('error and close', () {
|
||
|
final s = BehaviorSubject<int>();
|
||
|
final exception = Exception();
|
||
|
s
|
||
|
..addError(exception)
|
||
|
..close();
|
||
|
|
||
|
expect(
|
||
|
s.errorAndStackTraceOrNull,
|
||
|
ErrorAndStackTrace(exception, null),
|
||
|
);
|
||
|
|
||
|
// the stream has the same value as the subject
|
||
|
expect(
|
||
|
s.stream.errorAndStackTraceOrNull,
|
||
|
ErrorAndStackTrace(exception, null),
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|