platform/sandbox/reactivex/test/subject/behavior_subject_test.dart

1476 lines
43 KiB
Dart
Raw Normal View History

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),
);
});
});
});
}