268 lines
7.4 KiB
Dart
268 lines
7.4 KiB
Dart
|
import 'dart:async';
|
||
|
|
||
|
import 'package:angel3_reactivex/angel3_reactivex.dart';
|
||
|
import 'package:test/test.dart';
|
||
|
|
||
|
import '../utils.dart';
|
||
|
|
||
|
void main() {
|
||
|
test('Rx.flatMap', () async {
|
||
|
const expectedOutput = [3, 2, 1];
|
||
|
var count = 0;
|
||
|
|
||
|
_getStream().flatMap(_getOtherStream).listen(expectAsync1((result) {
|
||
|
expect(result, expectedOutput[count++]);
|
||
|
}, count: expectedOutput.length));
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap.reusable', () async {
|
||
|
final transformer = FlatMapStreamTransformer<int, int>(_getOtherStream);
|
||
|
const expectedOutput = [3, 2, 1];
|
||
|
var countA = 0, countB = 0;
|
||
|
|
||
|
_getStream().transform(transformer).listen(expectAsync1((result) {
|
||
|
expect(result, expectedOutput[countA++]);
|
||
|
}, count: expectedOutput.length));
|
||
|
|
||
|
_getStream().transform(transformer).listen(expectAsync1((result) {
|
||
|
expect(result, expectedOutput[countB++]);
|
||
|
}, count: expectedOutput.length));
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap.asBroadcastStream', () async {
|
||
|
final stream = _getStream().asBroadcastStream().flatMap(_getOtherStream);
|
||
|
|
||
|
// listen twice on same stream
|
||
|
stream.listen(null);
|
||
|
stream.listen(null);
|
||
|
// code should reach here
|
||
|
await expectLater(true, true);
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap.error.shouldThrowA', () async {
|
||
|
final streamWithError =
|
||
|
Stream<int>.error(Exception()).flatMap(_getOtherStream);
|
||
|
|
||
|
streamWithError.listen(null,
|
||
|
onError: expectAsync2((Exception e, StackTrace s) {
|
||
|
expect(e, isException);
|
||
|
}));
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap.error.shouldThrowB', () async {
|
||
|
final streamWithError = Stream.value(1)
|
||
|
.flatMap((_) => Stream<void>.error(Exception('Catch me if you can!')));
|
||
|
|
||
|
streamWithError.listen(null,
|
||
|
onError: expectAsync2((Exception e, StackTrace s) {
|
||
|
expect(e, isException);
|
||
|
}));
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap.error.shouldThrowC', () async {
|
||
|
final streamWithError =
|
||
|
Stream.value(1).flatMap<void>((_) => throw Exception('oh noes!'));
|
||
|
|
||
|
streamWithError.listen(null,
|
||
|
onError: expectAsync2((Exception e, StackTrace s) {
|
||
|
expect(e, isException);
|
||
|
}));
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap.pause.resume', () async {
|
||
|
late StreamSubscription<int> subscription;
|
||
|
final stream = Stream.value(0).flatMap((_) => Stream.value(1));
|
||
|
|
||
|
subscription = stream.listen(expectAsync1((value) {
|
||
|
expect(value, 1);
|
||
|
|
||
|
subscription.cancel();
|
||
|
}, count: 1));
|
||
|
|
||
|
subscription.pause();
|
||
|
subscription.resume();
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap.chains', () {
|
||
|
expect(
|
||
|
Stream.value(1)
|
||
|
.flatMap((_) => Stream.value(2))
|
||
|
.flatMap((_) => Stream.value(3)),
|
||
|
emitsInOrder(<dynamic>[3, emitsDone]),
|
||
|
);
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap accidental broadcast', () async {
|
||
|
final controller = StreamController<int>();
|
||
|
|
||
|
final stream = controller.stream.flatMap((_) => Stream<int>.empty());
|
||
|
|
||
|
stream.listen(null);
|
||
|
expect(() => stream.listen(null), throwsStateError);
|
||
|
|
||
|
controller.add(1);
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap(maxConcurrent: 1)', () {
|
||
|
{
|
||
|
// asyncExpand / concatMap
|
||
|
final stream = Stream.fromIterable([1, 2, 3, 4]).flatMap(
|
||
|
(value) => Rx.timer(
|
||
|
value,
|
||
|
Duration(milliseconds: (5 - value) * 100),
|
||
|
),
|
||
|
maxConcurrent: 1,
|
||
|
);
|
||
|
expect(stream, emitsInOrder(<Object>[1, 2, 3, 4, emitsDone]));
|
||
|
}
|
||
|
|
||
|
{
|
||
|
// emits error
|
||
|
final stream = Stream.fromIterable([1, 2, 3, 4]).flatMap(
|
||
|
(value) => value == 1
|
||
|
? throw Exception()
|
||
|
: Rx.timer(
|
||
|
value,
|
||
|
Duration(milliseconds: (5 - value) * 100),
|
||
|
),
|
||
|
maxConcurrent: 1,
|
||
|
);
|
||
|
expect(stream,
|
||
|
emitsInOrder(<Object>[emitsError(isException), 2, 3, 4, emitsDone]));
|
||
|
}
|
||
|
|
||
|
{
|
||
|
// emits error
|
||
|
final stream = Stream.fromIterable([1, 2, 3, 4]).flatMap(
|
||
|
(value) => value == 1
|
||
|
? Stream<int>.error(Exception())
|
||
|
: Rx.timer(
|
||
|
value,
|
||
|
Duration(milliseconds: (5 - value) * 100),
|
||
|
),
|
||
|
maxConcurrent: 1,
|
||
|
);
|
||
|
expect(stream,
|
||
|
emitsInOrder(<Object>[emitsError(isException), 2, 3, 4, emitsDone]));
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap(maxConcurrent: 2)', () async {
|
||
|
const maxConcurrent = 2;
|
||
|
var activeCount = 0;
|
||
|
|
||
|
// 1 -> 500
|
||
|
// 2 -> 400
|
||
|
// 3 -> 500
|
||
|
// 4 -> 200
|
||
|
// -----1--4
|
||
|
// ----2-----3
|
||
|
// ----21--4-3
|
||
|
final stream = Stream.fromIterable([1, 2, 3, 4]).flatMap(
|
||
|
(value) {
|
||
|
return Rx.defer(() {
|
||
|
expect(++activeCount, lessThanOrEqualTo(maxConcurrent));
|
||
|
|
||
|
final ms = (value.isOdd ? 5 : 6 - value) * 100;
|
||
|
return Rx.timer(value, Duration(milliseconds: ms));
|
||
|
}).doOnDone(() => --activeCount);
|
||
|
},
|
||
|
maxConcurrent: maxConcurrent,
|
||
|
);
|
||
|
|
||
|
await expectLater(stream, emitsInOrder(<Object>[2, 1, 4, 3, emitsDone]));
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap(maxConcurrent: 3)', () async {
|
||
|
const maxConcurrent = 3;
|
||
|
var activeCount = 0;
|
||
|
|
||
|
// 1 -> 400
|
||
|
// 2 -> 300
|
||
|
// 3 -> 200
|
||
|
// 4 -> 200
|
||
|
// 5 -> 300
|
||
|
// 6 -> 400
|
||
|
// ----1----6
|
||
|
// ---2---5
|
||
|
// --3--4
|
||
|
// --3214-5-6
|
||
|
final stream = Stream.fromIterable([1, 2, 3, 4, 5, 6]).flatMap(
|
||
|
(value) {
|
||
|
return Rx.defer(() {
|
||
|
expect(++activeCount, lessThanOrEqualTo(maxConcurrent));
|
||
|
|
||
|
final ms = (value <= 3 ? 5 - value : value - 2) * 100;
|
||
|
return Rx.timer(value, Duration(milliseconds: ms));
|
||
|
}).doOnDone(() => --activeCount);
|
||
|
},
|
||
|
maxConcurrent: maxConcurrent,
|
||
|
);
|
||
|
|
||
|
await expectLater(
|
||
|
stream, emitsInOrder(<Object>[3, 2, 1, 4, 5, 6, emitsDone]));
|
||
|
});
|
||
|
|
||
|
test('Rx.flatMap.cancel', () {
|
||
|
_getStream()
|
||
|
.flatMap(_getOtherStream)
|
||
|
.listen(expectAsync1((data) {}, count: 0))
|
||
|
.cancel();
|
||
|
}, timeout: const Timeout(Duration(milliseconds: 200)));
|
||
|
|
||
|
test('Rx.flatMap(maxConcurrent: 1).cancel', () {
|
||
|
_getStream()
|
||
|
.flatMap(_getOtherStream, maxConcurrent: 1)
|
||
|
.listen(expectAsync1((data) {}, count: 0))
|
||
|
.cancel();
|
||
|
}, timeout: const Timeout(Duration(milliseconds: 200)));
|
||
|
|
||
|
test('Rx.flatMap.take.cancel', () {
|
||
|
_getStream()
|
||
|
.flatMap(_getOtherStream)
|
||
|
.take(1)
|
||
|
.listen(expectAsync1((data) => expect(data, 3), count: 1));
|
||
|
}, timeout: const Timeout(Duration(milliseconds: 200)));
|
||
|
|
||
|
test('Rx.flatMap(maxConcurrent: 1).take.cancel', () {
|
||
|
_getStream()
|
||
|
.flatMap(_getOtherStream, maxConcurrent: 1)
|
||
|
.take(1)
|
||
|
.listen(expectAsync1((data) => expect(data, 1), count: 1));
|
||
|
}, timeout: const Timeout(Duration(milliseconds: 200)));
|
||
|
|
||
|
test('Rx.flatMap(maxConcurrent: 2).take.cancel', () {
|
||
|
_getStream()
|
||
|
.flatMap(_getOtherStream, maxConcurrent: 2)
|
||
|
.take(1)
|
||
|
.listen(expectAsync1((data) => expect(data, 2), count: 1));
|
||
|
}, timeout: const Timeout(Duration(milliseconds: 200)));
|
||
|
|
||
|
test('Rx.flatMap.nullable', () {
|
||
|
nullableTest<String?>(
|
||
|
(s) => s.flatMap((v) => Stream.value(v)),
|
||
|
);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
Stream<int> _getStream() => Stream.fromIterable(const [1, 2, 3]);
|
||
|
|
||
|
Stream<int> _getOtherStream(int value) {
|
||
|
final controller = StreamController<int>();
|
||
|
|
||
|
Timer(
|
||
|
// Reverses the order of 1, 2, 3 to 3, 2, 1 by delaying 1, and 2 longer
|
||
|
// than they delay 3
|
||
|
Duration(
|
||
|
milliseconds: value == 1
|
||
|
? 15
|
||
|
: value == 2
|
||
|
? 10
|
||
|
: 5), () {
|
||
|
controller.add(value);
|
||
|
controller.close();
|
||
|
});
|
||
|
|
||
|
return controller.stream;
|
||
|
}
|