| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // @dart = 2.9 |
| |
| // Test Stream.fromIterable. |
| |
| import 'dart:async'; |
| |
| import 'package:async_helper/async_helper.dart'; |
| import 'package:expect/expect.dart'; |
| import 'package:async_helper/async_minitest.dart'; |
| |
| import 'event_helper.dart'; |
| |
| // Tests that various typed iterables that do not throw creates |
| // suitable streams. |
| void iterableTest<T>(Iterable<T> iterable) { |
| asyncStart(); |
| List<T> expected = iterable.toList(); |
| Stream<T> stream = new Stream<T>.fromIterable(iterable); |
| var events = <T>[]; |
| stream.listen(events.add, onDone: () { |
| Expect.listEquals(expected, events, "fromIterable $iterable"); |
| asyncEnd(); |
| }); |
| } |
| |
| main() { |
| asyncStart(); |
| |
| iterableTest<Object>(<Object>[]); |
| iterableTest<Null>(<Null>[]); |
| iterableTest<Object>(<int>[1]); |
| iterableTest<Object>(<Object>[1, "two", true, null]); |
| iterableTest<int>(<int>[1, 2, 3, 4]); |
| iterableTest<String>(<String>["one", "two", "three", "four"]); |
| iterableTest<int>(new Iterable<int>.generate(1000, (i) => i)); |
| iterableTest<String>( |
| new Iterable<int>.generate(1000, (i) => i).map((i) => "$i")); |
| |
| Iterable<int> iter = new Iterable.generate(25, (i) => i * 2); |
| |
| { |
| // Test that the stream's .toList works. |
| asyncStart(); |
| new Stream.fromIterable(iter).toList().then((actual) { |
| List expected = iter.toList(); |
| Expect.equals(25, expected.length); |
| Expect.listEquals(expected, actual); |
| asyncEnd(); |
| }); |
| } |
| |
| { |
| // Test that the stream's .map works. |
| asyncStart(); |
| new Stream.fromIterable(iter).map((i) => i * 3).toList().then((actual) { |
| List expected = iter.map((i) => i * 3).toList(); |
| Expect.listEquals(expected, actual); |
| asyncEnd(); |
| }); |
| } |
| |
| { |
| // Test that pause works. |
| asyncStart(); |
| int ctr = 0; |
| |
| var stream = new Stream<int>.fromIterable(iter.map((x) { |
| ctr++; |
| return x; |
| })); |
| |
| StreamSubscription subscription; |
| var actual = []; |
| subscription = stream.listen((int value) { |
| actual.add(value); |
| // Do a 10 ms pause during the playback of the iterable. |
| Duration duration = const Duration(milliseconds: 10); |
| if (value == 20) { |
| asyncStart(); |
| int beforeCtr = ctr; |
| subscription.pause(new Future.delayed(duration, () {}).whenComplete(() { |
| Expect.equals(beforeCtr, ctr); |
| asyncEnd(); |
| })); |
| } |
| }, onDone: () { |
| Expect.listEquals(iter.toList(), actual); |
| asyncEnd(); |
| }); |
| } |
| |
| { |
| // Test that you can't listen twice.. |
| Stream stream = new Stream.fromIterable(iter); |
| stream.listen((x) {}).cancel(); |
| Expect.throws<StateError>(() => stream.listen((x) {})); |
| } |
| |
| { |
| // Regression test for http://dartbug.com/14332. |
| // This should succeed. |
| var from = new Stream.fromIterable([1, 2, 3, 4, 5]); |
| |
| var c = new StreamController(); |
| var sink = c.sink; |
| |
| asyncStart(2); |
| |
| // if this goes first, test failed (hanged). Swapping addStream and toList |
| // made failure go away. |
| sink.addStream(from).then((_) { |
| c.close(); |
| asyncEnd(); |
| }); |
| |
| c.stream.toList().then((x) { |
| Expect.listEquals([1, 2, 3, 4, 5], x); |
| asyncEnd(); |
| }); |
| } |
| |
| { |
| // Regression test for issue 14334 (#2) |
| var from = new Stream.fromIterable([1, 2, 3, 4, 5]); |
| |
| // odd numbers as data events, even numbers as error events |
| from = from.map((x) => x.isOdd ? x : throw x); |
| |
| var c = new StreamController(); |
| |
| asyncStart(); |
| |
| var data = [], errors = []; |
| c.stream.listen(data.add, onError: errors.add, onDone: () { |
| Expect.listEquals([1, 3, 5], data); |
| Expect.listEquals([2, 4], errors); |
| asyncEnd(); |
| }); |
| c.addStream(from).then((_) { |
| c.close(); |
| }); |
| } |
| |
| { |
| // Example from issue http://dartbug.com/33431. |
| asyncStart(); |
| var asyncStarStream = () async* { |
| yield 1; |
| yield 2; |
| throw "bad"; |
| }(); |
| collectEvents(asyncStarStream).then((events2) { |
| Expect.listEquals(["value", 1, "value", 2, "error", "bad"], events2); |
| asyncEnd(); |
| }); |
| |
| Iterable<int> throwingIterable() sync* { |
| yield 1; |
| yield 2; |
| throw "bad"; |
| } |
| |
| // Sanity check behavior. |
| var it = throwingIterable().iterator; |
| Expect.isTrue(it.moveNext()); |
| Expect.equals(1, it.current); |
| Expect.isTrue(it.moveNext()); |
| Expect.equals(2, it.current); |
| Expect.throws(it.moveNext, (e) => e == "bad"); |
| |
| asyncStart(); |
| var syncStarStream = new Stream<int>.fromIterable(throwingIterable()); |
| collectEvents(syncStarStream).then((events1) { |
| Expect.listEquals(["value", 1, "value", 2, "error", "bad"], events1); |
| asyncEnd(); |
| }); |
| } |
| |
| { |
| // Test error behavior. Changed when fixing issue 33431. |
| // Iterable where "current" throws for third value, moveNext on fifth call. |
| var m = new MockIterable<int>((n) { |
| return n != 5 || (throw "moveNext"); |
| }, (n) { |
| return n != 3 ? n : throw "current"; |
| }); |
| asyncStart(); |
| collectEvents(new Stream<int>.fromIterable(m)).then((events) { |
| // Error on "current" does not stop iteration. |
| // Error on "moveNext" does. |
| Expect.listEquals([ |
| "value", |
| 1, |
| "value", |
| 2, |
| "error", |
| "current", |
| "value", |
| 4, |
| "error", |
| "moveNext" |
| ], events); |
| asyncEnd(); |
| }); |
| } |
| |
| asyncEnd(); |
| } |
| |
| // Collects value and error events in a list. |
| // Value events preceeded by "value", error events by "error". |
| // Completes on done event. |
| Future<List<Object>> collectEvents(Stream<Object> stream) { |
| var c = new Completer<List<Object>>(); |
| var events = <Object>[]; |
| stream.listen((value) { |
| events..add("value")..add(value); |
| }, onError: (error) { |
| events..add("error")..add(error); |
| }, onDone: () { |
| c.complete(events); |
| }); |
| return c.future; |
| } |
| |
| // Mock iterable. |
| // A `MockIterable<T>(f1, f2)` calls `f1` on `moveNext` calls with incrementing |
| // values starting from 1. Calls `f2` on `current` access, with the same integer |
| // as the most recent `f1` call. |
| class MockIterable<T> extends Iterable<T> { |
| final bool Function(int) _onMoveNext; |
| final T Function(int) _onCurrent; |
| |
| MockIterable(this._onMoveNext, this._onCurrent); |
| |
| Iterator<T> get iterator => MockIterator(_onMoveNext, _onCurrent); |
| } |
| |
| class MockIterator<T> implements Iterator<T> { |
| final bool Function(int) _onMoveNext; |
| final T Function(int) _onCurrent; |
| |
| int _counter = 0; |
| |
| MockIterator(this._onMoveNext, this._onCurrent); |
| |
| bool moveNext() { |
| _counter += 1; |
| return _onMoveNext(_counter); |
| } |
| |
| T get current { |
| return _onCurrent(_counter); |
| } |
| } |