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(_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.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.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((_) => throw Exception('oh noes!')); streamWithError.listen(null, onError: expectAsync2((Exception e, StackTrace s) { expect(e, isException); })); }); test('Rx.flatMap.pause.resume', () async { late StreamSubscription 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([3, emitsDone]), ); }); test('Rx.flatMap accidental broadcast', () async { final controller = StreamController(); final stream = controller.stream.flatMap((_) => Stream.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([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([emitsError(isException), 2, 3, 4, emitsDone])); } { // emits error final stream = Stream.fromIterable([1, 2, 3, 4]).flatMap( (value) => value == 1 ? Stream.error(Exception()) : Rx.timer( value, Duration(milliseconds: (5 - value) * 100), ), maxConcurrent: 1, ); expect(stream, emitsInOrder([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([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([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( (s) => s.flatMap((v) => Stream.value(v)), ); }); } Stream _getStream() => Stream.fromIterable(const [1, 2, 3]); Stream _getOtherStream(int value) { final controller = StreamController(); 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; }