| // Copyright (c) 2011, 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. |
| |
| // Test the basic StreamController and StreamController.singleSubscription. |
| library stream_controller_test; |
| |
| import "package:expect/expect.dart"; |
| import "package:async_helper/async_helper.dart"; |
| import 'dart:async'; |
| import 'event_helper.dart'; |
| |
| const MS = const Duration(milliseconds: 1); |
| |
| fail(e) { |
| Expect.fail("Unexepected error: $e"); |
| } |
| |
| void testMultiController() { |
| // Test normal flow. |
| { |
| var c = new StreamController(sync: true); |
| Events expectedEvents = new Events() |
| ..add(42) |
| ..add("dibs") |
| ..error("error!") |
| ..error("error too!") |
| ..close(); |
| CaptureEvents actualEvents = |
| new Events.capture(c.stream.asBroadcastStream()) as CaptureEvents; |
| expectedEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test automatic unsubscription on error. |
| { |
| var c = new StreamController(sync: true); |
| var expectedEvents = new Events() |
| ..add(42) |
| ..error("error"); |
| var actualEvents = |
| new Events.capture(c.stream.asBroadcastStream(), cancelOnError: true); |
| Events sentEvents = new Events() |
| ..add(42) |
| ..error("error") |
| ..add("Are you there?"); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test manual unsubscription. |
| { |
| var c = new StreamController(sync: true); |
| var expectedEvents = new Events() |
| ..add(42) |
| ..error("error") |
| ..add(37); |
| dynamic actualEvents = |
| new Events.capture(c.stream.asBroadcastStream(), cancelOnError: false); |
| expectedEvents.replay(c); |
| actualEvents.subscription.cancel(); |
| c.add("Are you there"); // Not sent to actualEvents. |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test filter. |
| { |
| var c = new StreamController(sync: true); |
| var expectedEvents = new Events() |
| ..add("a string") |
| ..add("another string") |
| ..close(); |
| var sentEvents = new Events() |
| ..add("a string") |
| ..add(42) |
| ..add("another string") |
| ..close(); |
| var actualEvents = new Events.capture( |
| c.stream.asBroadcastStream().where((v) => v is String)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test map. |
| { |
| var c = new StreamController(sync: true); |
| var expectedEvents = new Events() |
| ..add("abab") |
| ..error("error") |
| ..close(); |
| var sentEvents = new Events() |
| ..add("ab") |
| ..error("error") |
| ..close(); |
| var actualEvents = |
| new Events.capture(c.stream.asBroadcastStream().map((v) => "$v$v")); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test handleError. |
| { |
| var c = new StreamController(sync: true); |
| var expectedEvents = new Events() |
| ..add("ab") |
| ..error("[foo]"); |
| var sentEvents = new Events() |
| ..add("ab") |
| ..error("foo") |
| ..add("ab") |
| ..close(); |
| var actualEvents = new Events.capture( |
| c.stream.asBroadcastStream().handleError((error) { |
| if (error is String) { |
| // TODO(floitsch): this test originally changed the stacktrace. |
| throw "[${error}]"; |
| } |
| }), |
| cancelOnError: true); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // reduce is tested asynchronously and therefore not in this file. |
| |
| // Test expand |
| { |
| var c = new StreamController(sync: true); |
| var sentEvents = new Events() |
| ..add(3) |
| ..add(2) |
| ..add(4) |
| ..close(); |
| var expectedEvents = new Events() |
| ..add(1) |
| ..add(2) |
| ..add(3) |
| ..add(1) |
| ..add(2) |
| ..add(1) |
| ..add(2) |
| ..add(3) |
| ..add(4) |
| ..close(); |
| var actualEvents = |
| new Events.capture(c.stream.asBroadcastStream().expand((v) { |
| var l = []; |
| for (int i = 0; i < v; i++) l.add(i + 1); |
| return l; |
| })); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test transform. |
| { |
| var c = new StreamController(sync: true); |
| var sentEvents = new Events() |
| ..add("a") |
| ..error(42) |
| ..add("b") |
| ..close(); |
| var expectedEvents = new Events() |
| ..error("a") |
| ..add(42) |
| ..error("b") |
| ..add("foo") |
| ..close(); |
| var actualEvents = new Events.capture(c.stream |
| .asBroadcastStream() |
| .transform(new StreamTransformer.fromHandlers(handleData: (v, s) { |
| s.addError(v); |
| }, handleError: (e, st, s) { |
| s.add(e); |
| }, handleDone: (s) { |
| s.add("foo"); |
| s.close(); |
| }))); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test multiple filters. |
| { |
| var c = new StreamController(sync: true); |
| var sentEvents = new Events() |
| ..add(42) |
| ..add("snugglefluffy") |
| ..add(7) |
| ..add("42") |
| ..error("not FormatException") // Unsubscribes. |
| ..close(); |
| var expectedEvents = new Events() |
| ..add(42) |
| ..error("not FormatException"); |
| var actualEvents = new Events.capture( |
| c.stream |
| .asBroadcastStream() |
| .where((v) => v is String) |
| .map((v) => int.parse(v)) |
| .handleError((error) { |
| if (error is! FormatException) throw error; |
| }).where((v) => v > 10), |
| cancelOnError: true); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test subscription changes while firing. |
| { |
| var c = new StreamController<int>(sync: true); |
| var sink = c.sink; |
| var stream = c.stream.asBroadcastStream(); |
| var counter = 0; |
| var subscription = stream.listen(null); |
| subscription.onData((data) { |
| counter += data; |
| subscription.cancel(); |
| stream.listen((data) { |
| counter += 10 * data; |
| }); |
| var subscription2 = stream.listen(null); |
| subscription2.onData((data) { |
| counter += 100 * data; |
| if (data == 4) subscription2.cancel(); |
| }); |
| }); |
| sink.add(1); // seen by stream 1 |
| sink.add(2); // seen by stream 10 and 100 |
| sink.add(3); // -"- |
| sink.add(4); // -"- |
| sink.add(5); // seen by stream 10 |
| Expect.equals(1 + 20 + 200 + 30 + 300 + 40 + 400 + 50, counter); |
| } |
| } |
| |
| testSingleController() { |
| // Test normal flow. |
| { |
| var c = new StreamController(sync: true); |
| Events expectedEvents = new Events() |
| ..add(42) |
| ..add("dibs") |
| ..error("error!") |
| ..error("error too!") |
| ..close(); |
| CaptureEvents actualEvents = new Events.capture(c.stream) as CaptureEvents; |
| expectedEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test automatic unsubscription on error. |
| { |
| var c = new StreamController(sync: true); |
| var expectedEvents = new Events() |
| ..add(42) |
| ..error("error"); |
| var actualEvents = new Events.capture(c.stream, cancelOnError: true); |
| Events sentEvents = new Events() |
| ..add(42) |
| ..error("error") |
| ..add("Are you there?"); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test manual unsubscription. |
| { |
| var c = new StreamController(sync: true); |
| var expectedEvents = new Events() |
| ..add(42) |
| ..error("error") |
| ..add(37); |
| dynamic actualEvents = new Events.capture(c.stream, cancelOnError: false); |
| expectedEvents.replay(c); |
| actualEvents.subscription.cancel(); |
| c.add("Are you there"); // Not sent to actualEvents. |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test filter. |
| { |
| var c = new StreamController(sync: true); |
| var expectedEvents = new Events() |
| ..add("a string") |
| ..add("another string") |
| ..close(); |
| var sentEvents = new Events() |
| ..add("a string") |
| ..add(42) |
| ..add("another string") |
| ..close(); |
| var actualEvents = new Events.capture(c.stream.where((v) => v is String)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test map. |
| { |
| var c = new StreamController(sync: true); |
| var expectedEvents = new Events() |
| ..add("abab") |
| ..error("error") |
| ..close(); |
| var sentEvents = new Events() |
| ..add("ab") |
| ..error("error") |
| ..close(); |
| var actualEvents = new Events.capture(c.stream.map((v) => "$v$v")); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test handleError. |
| { |
| var c = new StreamController(sync: true); |
| var expectedEvents = new Events() |
| ..add("ab") |
| ..error("[foo]"); |
| var sentEvents = new Events() |
| ..add("ab") |
| ..error("foo") |
| ..add("ab") |
| ..close(); |
| var actualEvents = new Events.capture(c.stream.handleError((error) { |
| if (error is String) { |
| // TODO(floitsch): this error originally changed the stack trace. |
| throw "[${error}]"; |
| } |
| }), cancelOnError: true); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // reduce is tested asynchronously and therefore not in this file. |
| |
| // Test expand |
| { |
| var c = new StreamController(sync: true); |
| var sentEvents = new Events() |
| ..add(3) |
| ..add(2) |
| ..add(4) |
| ..close(); |
| var expectedEvents = new Events() |
| ..add(1) |
| ..add(2) |
| ..add(3) |
| ..add(1) |
| ..add(2) |
| ..add(1) |
| ..add(2) |
| ..add(3) |
| ..add(4) |
| ..close(); |
| var actualEvents = new Events.capture(c.stream.expand((v) { |
| var l = []; |
| for (int i = 0; i < v; i++) l.add(i + 1); |
| return l; |
| })); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // test contains. |
| { |
| var c = new StreamController(sync: true); |
| // Error after match is not important. |
| var sentEvents = new Events() |
| ..add("a") |
| ..add("x") |
| ..error("FAIL") |
| ..close(); |
| Future<bool> contains = c.stream.contains("x"); |
| contains.then((var c) { |
| Expect.isTrue(c); |
| }); |
| sentEvents.replay(c); |
| } |
| |
| { |
| var c = new StreamController(sync: true); |
| // Not matching is ok. |
| var sentEvents = new Events() |
| ..add("a") |
| ..add("x") |
| ..add("b") |
| ..close(); |
| Future<bool> contains = c.stream.contains("y"); |
| contains.then((var c) { |
| Expect.isFalse(c); |
| }); |
| sentEvents.replay(c); |
| } |
| |
| { |
| var c = new StreamController(sync: true); |
| // Error before match makes future err. |
| var sentEvents = new Events() |
| ..add("a") |
| ..error("FAIL") |
| ..add("b") |
| ..close(); |
| Future<bool> contains = c.stream.contains("b"); |
| contains.then((var c) { |
| Expect.fail("no value expected"); |
| }).catchError((error) { |
| Expect.equals("FAIL", error); |
| }); |
| sentEvents.replay(c); |
| } |
| |
| // Test transform. |
| { |
| var c = new StreamController(sync: true); |
| var sentEvents = new Events() |
| ..add("a") |
| ..error(42) |
| ..add("b") |
| ..close(); |
| var expectedEvents = new Events() |
| ..error("a") |
| ..add(42) |
| ..error("b") |
| ..add("foo") |
| ..close(); |
| var actualEvents = new Events.capture(c.stream |
| .transform(new StreamTransformer.fromHandlers(handleData: (v, s) { |
| s.addError(v); |
| }, handleError: (e, st, s) { |
| s.add(e); |
| }, handleDone: (s) { |
| s.add("foo"); |
| s.close(); |
| }))); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test multiple filters. |
| { |
| var c = new StreamController(sync: true); |
| var sentEvents = new Events() |
| ..add(42) |
| ..add("snugglefluffy") |
| ..add(7) |
| ..add("42") |
| ..error("not FormatException") // Unsubscribes. |
| ..close(); |
| var expectedEvents = new Events() |
| ..add(42) |
| ..error("not FormatException"); |
| var actualEvents = new Events.capture( |
| c.stream |
| .where((v) => v is String) |
| .map((v) => int.parse(v)) |
| .handleError((error) { |
| if (error is! FormatException) throw error; |
| }).where((v) => v > 10), |
| cancelOnError: true); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| // Test that only one subscription is allowed. |
| { |
| var c = new StreamController(sync: true); |
| var sink = c.sink; |
| var stream = c.stream; |
| var counter = 0; |
| var subscription = stream.listen((data) { |
| counter += data as int; |
| }); |
| Expect.throwsStateError(() => stream.listen(null)); |
| sink.add(1); |
| Expect.equals(1, counter); |
| c.close(); |
| } |
| } |
| |
| testExtraMethods() { |
| Events sentEvents = new Events() |
| ..add(1) |
| ..add(2) |
| ..add(3) |
| ..close(); |
| |
| var c = new StreamController(sync: true); |
| Events expectedEvents = new Events() |
| ..add(3) |
| ..close(); |
| Events actualEvents = new Events.capture(c.stream.skip(2)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| |
| c = new StreamController(sync: true); |
| expectedEvents = new Events()..close(); |
| actualEvents = new Events.capture(c.stream.skip(3)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| |
| c = new StreamController(sync: true); |
| expectedEvents = new Events()..close(); |
| actualEvents = new Events.capture(c.stream.skip(7)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| |
| c = new StreamController(sync: true); |
| expectedEvents = sentEvents; |
| actualEvents = new Events.capture(c.stream.skip(0)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| |
| c = new StreamController(sync: true); |
| expectedEvents = new Events() |
| ..add(3) |
| ..close(); |
| actualEvents = new Events.capture(c.stream.skipWhile((x) => x <= 2)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| |
| c = new StreamController(sync: true); |
| expectedEvents = new Events() |
| ..add(2) |
| ..add(3) |
| ..close(); |
| actualEvents = new Events.capture(c.stream.skipWhile((x) => x <= 1)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| |
| c = new StreamController(sync: true); |
| expectedEvents = new Events() |
| ..add(1) |
| ..add(2) |
| ..add(3) |
| ..close(); |
| actualEvents = new Events.capture(c.stream.skipWhile((x) => false)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| |
| c = new StreamController(sync: true); |
| expectedEvents = new Events() |
| ..add(1) |
| ..add(2) |
| ..close(); |
| actualEvents = new Events.capture(c.stream.take(2)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| |
| c = new StreamController(sync: true); |
| expectedEvents = new Events() |
| ..add(1) |
| ..add(2) |
| ..close(); |
| actualEvents = new Events.capture(c.stream.takeWhile((x) => x <= 2)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| |
| c = new StreamController(sync: true); |
| sentEvents = new Events() |
| ..add(1) |
| ..add(1) |
| ..add(2) |
| ..add(1) |
| ..add(2) |
| ..add(2) |
| ..add(2) |
| ..close(); |
| expectedEvents = new Events() |
| ..add(1) |
| ..add(2) |
| ..add(1) |
| ..add(2) |
| ..close(); |
| actualEvents = new Events.capture(c.stream.distinct()); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| |
| c = new StreamController(sync: true); |
| sentEvents = new Events() |
| ..add(5) |
| ..add(6) |
| ..add(4) |
| ..add(6) |
| ..add(8) |
| ..add(3) |
| ..add(4) |
| ..add(1) |
| ..close(); |
| expectedEvents = new Events() |
| ..add(5) |
| ..add(4) |
| ..add(3) |
| ..add(1) |
| ..close(); |
| // Use 'distinct' as a filter with access to the previously emitted event. |
| actualEvents = new Events.capture(c.stream.distinct((a, b) => a < b)); |
| sentEvents.replay(c); |
| Expect.listEquals(expectedEvents.events, actualEvents.events); |
| } |
| |
| void testClosed() { |
| StreamController c = new StreamController(sync: true); |
| Expect.isFalse(c.isClosed); |
| c.add(42); |
| Expect.isFalse(c.isClosed); |
| c.addError("bad"); |
| Expect.isFalse(c.isClosed); |
| c.close(); |
| Expect.isTrue(c.isClosed); |
| } |
| |
| void testCloseFuture() { |
| asyncStart(); |
| asyncStart(); |
| var c = new StreamController(); |
| var f = c.close(); |
| Expect.isTrue(c.isClosed); |
| bool doneSeen = false; |
| f.then((_) { |
| Expect.isTrue(doneSeen); |
| asyncEnd(); |
| }); |
| // Only listen after a while. |
| new Timer(MS * 250, () { |
| c.stream.listen(null, onDone: () { |
| asyncEnd(); |
| doneSeen = true; |
| }); |
| }); |
| } |
| |
| void testCloseFuture2() { |
| asyncStart(); |
| asyncStart(); |
| var c = new StreamController.broadcast(); |
| var f = c.close(); |
| Expect.isTrue(c.isClosed); |
| bool doneSeen = false; |
| f.then((_) { |
| // Done future on broadcast stream can happen |
| // before a listener is added. |
| Expect.isFalse(doneSeen); |
| asyncEnd(); |
| }); |
| // Only listen after a while. |
| new Timer(MS * 250, () { |
| c.stream.listen(null, onDone: () { |
| doneSeen = true; |
| asyncEnd(); |
| }); |
| }); |
| } |
| |
| void testCloseFuture3() { |
| asyncStart(); |
| var c = new StreamController.broadcast(); |
| c..add(1)..add(2)..add(3)..add(4); |
| c.stream.listen(null).cancel(); |
| var f = c.close(); |
| Expect.isTrue(c.isClosed); |
| f.then((_) { |
| asyncEnd(); |
| }); |
| } |
| |
| void testStreamEquals() { |
| StreamController c; |
| c = new StreamController(sync: false); |
| Expect.equals(c.stream, c.stream); |
| c = new StreamController(sync: true); |
| Expect.equals(c.stream, c.stream); |
| c = new StreamController(sync: false, onListen: () {}); |
| Expect.equals(c.stream, c.stream); |
| c = new StreamController(sync: true, onListen: () {}); |
| Expect.equals(c.stream, c.stream); |
| c = new StreamController.broadcast(sync: false); |
| Expect.equals(c.stream, c.stream); |
| c = new StreamController.broadcast(sync: true); |
| Expect.equals(c.stream, c.stream); |
| c = new StreamController.broadcast(sync: false, onListen: () {}); |
| Expect.equals(c.stream, c.stream); |
| c = new StreamController.broadcast(sync: true, onListen: () {}); |
| Expect.equals(c.stream, c.stream); |
| } |
| |
| void testCancelThrow() { |
| asyncStart(); |
| asyncStart(); |
| asyncStart(); |
| StreamController c = new StreamController(onCancel: () { |
| asyncEnd(); |
| throw "ERROR"; |
| }); |
| c.add(1); |
| c.add(2); |
| c.add(3); |
| Future done = c.close(); |
| late StreamSubscription sub; |
| sub = c.stream.listen((v) { |
| Expect.equals(1, v); |
| Future f = sub.cancel(); |
| f.catchError((e) { |
| // Must complete with error from onCancel. |
| Expect.equals("ERROR", e); |
| asyncEnd(); |
| }); |
| }); |
| done.catchError(fail).whenComplete(asyncEnd); // Must complete without error. |
| } |
| |
| void testCancelThrow2() { |
| asyncStart(); |
| asyncStart(); |
| asyncStart(); |
| asyncStart(); |
| asyncStart(); |
| StreamController c2 = new StreamController(onCancel: () { |
| asyncEnd(); |
| throw "ERROR"; |
| }); |
| c2.add(1); |
| c2.add(2); |
| Future done2 = c2.close(); |
| done2.catchError(fail).whenComplete(asyncEnd); // Should not get error; |
| |
| StreamController c = new StreamController(); |
| var sub; |
| sub = c.stream.listen((v) { |
| Expect.equals(1, v); |
| Future f = sub.cancel(); |
| f.catchError((e) { |
| // Error from addStream stream's cancel must go only here. |
| asyncEnd(); |
| Expect.equals("ERROR", e); |
| }); |
| }); |
| var addDone = c.addStream(c2.stream); |
| addDone.catchError(fail).whenComplete(asyncEnd); // Should not get error. |
| var done = c.done; |
| done.catchError(fail).whenComplete(asyncEnd); // Should not get error. |
| } |
| |
| void testCancelThrow3() { |
| asyncStart(); |
| asyncStart(); |
| asyncStart(); |
| asyncStart(); |
| asyncStart(); |
| asyncStart(); |
| StreamController c2 = new StreamController(onCancel: () { |
| asyncEnd(); |
| throw "ERROR2"; |
| }); |
| c2.add(1); |
| c2.add(2); |
| var done2 = c2.close(); |
| done2.catchError(fail).whenComplete(asyncEnd); // Should not get error; |
| |
| StreamController c = new StreamController(onCancel: () { |
| asyncEnd(); |
| throw "ERROR1"; |
| }); |
| var sub; |
| sub = c.stream.listen((v) { |
| Expect.equals(1, v); |
| Future f = sub.cancel(); |
| f.catchError((e) { |
| // Only the last error ends up here. |
| Expect.equals("ERROR1", e); |
| asyncEnd(); |
| }); |
| }); |
| var addDone = c.addStream(c2.stream); |
| addDone.catchError(fail).whenComplete(asyncEnd); // Error must not go here. |
| c.done.catchError(fail).whenComplete(asyncEnd); // Error must not go here. |
| } |
| |
| void testBroadcastListenAfterClose() { |
| asyncStart(); |
| StreamController c = new StreamController.broadcast(); |
| var f = c.close(); |
| f.then((_) { |
| // Listening after close is allowed. The listener gets a done event. |
| c.stream.listen(null, onDone: asyncEnd); |
| }); |
| } |
| |
| void testBroadcastListenAfterClosePaused() { |
| asyncStart(); |
| StreamController c = new StreamController.broadcast(); |
| var f = c.close(); |
| f.then((_) { |
| // Listening after close is allowed. The listener gets a done event. |
| var sub = c.stream.listen(null, onDone: () { |
| Expect.fail("wrong done"); |
| }); |
| sub.pause(); |
| sub.pause(); |
| new Timer(MS * 100, () { |
| sub.asFuture().whenComplete(() { |
| Expect.fail("Bad complete"); |
| }); |
| sub.resume(); |
| new Timer(MS * 100, () { |
| sub.onDone(asyncEnd); |
| sub.resume(); |
| }); |
| }); |
| }); |
| } |
| |
| void testAsBroadcastListenAfterClose() { |
| asyncStart(); |
| asyncStart(); |
| StreamController c = new StreamController(); |
| Stream s = c.stream.asBroadcastStream(); |
| s.listen(null, onDone: asyncEnd); |
| var f = c.close(); |
| f.then((_) { |
| // Listening after close is allowed. The listener gets a done event. |
| s.listen(null, onDone: asyncEnd); |
| }); |
| } |
| |
| void testAsBroadcastListenAfterClosePaused() { |
| asyncStart(); |
| asyncStart(); |
| StreamController c = new StreamController(); |
| Stream s = c.stream.asBroadcastStream(); |
| s.listen(null, onDone: asyncEnd); |
| var f = c.close(); |
| f.then((_) { |
| // Listening after close is allowed. The listener gets a done event. |
| var sub = s.listen(null, onDone: () { |
| Expect.fail("wrong done"); |
| }); |
| sub.pause(); |
| sub.pause(); |
| new Timer(MS * 100, () { |
| sub.asFuture().whenComplete(() { |
| Expect.fail("Bad complete"); |
| }); |
| sub.resume(); |
| new Timer(MS * 100, () { |
| sub.onDone(asyncEnd); |
| sub.resume(); |
| }); |
| }); |
| }); |
| } |
| |
| void testEventInListen() { |
| asyncStart(); |
| // Regression test for http://dartbug.com/19722 |
| var c; |
| void send() { |
| c.add(1); |
| } |
| |
| int i = 1; |
| c = new StreamController.broadcast(onListen: send, sync: true); |
| c.stream.listen((v) { |
| Expect.equals(i++, v); |
| }, onDone: asyncEnd); |
| c.add(2); |
| c.close(); |
| } |
| |
| void testSyncControllerNotReentrant() { |
| Stream emptyStream = (new StreamController.broadcast()..close()).stream; |
| asyncStart(); |
| for (int listenerCount = 1; listenerCount <= 2; listenerCount++) { |
| StreamController c = new StreamController.broadcast(sync: true); |
| for (int i = 0; i < listenerCount; i++) { |
| asyncStart(); |
| asyncStart(); |
| c.stream.listen((v) { |
| Expect.equals(42, v); |
| Expect.throws(() { |
| c.add(37); |
| }); |
| Expect.throws(() { |
| c.addError(37); |
| }); |
| Expect.throws(() { |
| c.addStream(emptyStream); |
| }); |
| Expect.throws(() { |
| c.close(); |
| }); |
| asyncEnd(); |
| }, onError: (e, s) { |
| Expect.equals(87, e); |
| Expect.throws(() { |
| c.add(37); |
| }); |
| Expect.throws(() { |
| c.addError(37); |
| }); |
| Expect.throws(() { |
| c.addStream(emptyStream); |
| }); |
| Expect.throws(() { |
| c.close(); |
| }); |
| asyncEnd(); |
| }); |
| } |
| c.add(42); |
| c.addError(87); |
| } |
| asyncEnd(); |
| } |
| |
| void testSettingCallbacks() { |
| const int initial = 0; |
| const int running = 1; |
| const int paused = 2; |
| const int canceled = 3; |
| |
| var controller = new StreamController(); |
| var stream = controller.stream; |
| var state = initial; |
| |
| var onListen = () { |
| state = running; |
| }; |
| var onPause = () { |
| state = paused; |
| }; |
| var onResume = () { |
| state = running; |
| }; |
| var onCancel = () { |
| state = canceled; |
| }; |
| |
| Expect.isNull(controller.onListen); |
| Expect.isNull(controller.onPause); |
| Expect.isNull(controller.onResume); |
| Expect.isNull(controller.onCancel); |
| |
| controller |
| ..onListen = onListen |
| ..onPause = onPause |
| ..onResume = onResume |
| ..onCancel = onCancel; |
| |
| Expect.equals(onListen, controller.onListen); |
| Expect.equals(onPause, controller.onPause); |
| Expect.equals(onResume, controller.onResume); |
| Expect.equals(onCancel, controller.onCancel); |
| |
| Expect.equals(initial, state); |
| var sub = stream.listen(null); |
| Expect.equals(running, state); |
| sub.pause(); |
| Expect.equals(paused, state); |
| Expect.isTrue(controller.isPaused); |
| sub.resume(); |
| Expect.equals(running, state); |
| Expect.isFalse(controller.isPaused); |
| |
| var onListen2 = () { |
| state = -running; |
| }; |
| var onPause2 = () { |
| state = -paused; |
| }; |
| var onResume2 = () { |
| state = -running; |
| }; |
| var onCancel2 = () { |
| state = -canceled; |
| }; |
| // Changing them later does make a difference. |
| controller |
| ..onListen = onListen2 |
| ..onPause = onPause2 |
| ..onResume = onResume2 |
| ..onCancel = onCancel2; |
| |
| Expect.equals(onListen2, controller.onListen); |
| Expect.equals(onPause2, controller.onPause); |
| Expect.equals(onResume2, controller.onResume); |
| Expect.equals(onCancel2, controller.onCancel); |
| |
| Expect.equals(running, state); |
| sub.pause(); |
| Expect.equals(-paused, state); |
| Expect.isTrue(controller.isPaused); |
| sub.resume(); |
| Expect.equals(-running, state); |
| Expect.isFalse(controller.isPaused); |
| sub.cancel(); |
| Expect.equals(-canceled, state); |
| } |
| |
| void testSettingNullCallbacks() { |
| failCallback() => fail("Callback should not be called"); |
| var controller = new StreamController( |
| onListen: failCallback, |
| onPause: failCallback, |
| onResume: failCallback, |
| onCancel: failCallback); |
| |
| var stream = controller.stream; |
| |
| Expect.isFalse(controller.hasListener); |
| Expect.isTrue(controller.isPaused); |
| |
| Expect.isNotNull(controller.onListen); |
| controller.onListen = null; |
| Expect.isNull(controller.onListen); |
| |
| var sub = stream.listen(null); |
| |
| Expect.isTrue(controller.hasListener); |
| Expect.isFalse(controller.isPaused); |
| |
| Expect.isNotNull(controller.onPause); |
| controller.onPause = null; |
| Expect.isNull(controller.onPause); |
| |
| sub.pause(); |
| |
| Expect.isTrue(controller.hasListener); |
| Expect.isTrue(controller.isPaused); |
| |
| Expect.isNotNull(controller.onResume); |
| controller.onResume = null; |
| Expect.isNull(controller.onResume); |
| |
| sub.resume(); |
| |
| Expect.isTrue(controller.hasListener); |
| Expect.isFalse(controller.isPaused); |
| |
| Expect.isNotNull(controller.onCancel); |
| controller.onCancel = null; |
| Expect.isNull(controller.onCancel); |
| |
| sub.cancel(); |
| |
| Expect.isFalse(controller.hasListener); |
| Expect.isFalse(controller.isPaused); |
| } |
| |
| void testBroadcastSettingCallbacks() { |
| const int initial = 0; |
| const int running = 1; |
| const int canceled = 2; |
| |
| var controller = new StreamController.broadcast(); |
| var stream = controller.stream; |
| var state = initial; |
| |
| Expect.throwsUnsupportedError(() => controller.onPause = () {}); |
| Expect.throwsUnsupportedError(() => controller.onResume = () {}); |
| |
| controller |
| ..onListen = () { |
| state = running; |
| } |
| ..onCancel = () { |
| state = canceled; |
| }; |
| |
| Expect.equals(initial, state); |
| var sub = stream.listen(null); |
| Expect.equals(running, state); |
| sub.cancel(); |
| Expect.equals(canceled, state); |
| |
| // Changing them later does make a difference. |
| controller |
| ..onListen = () { |
| state = -running; |
| } |
| ..onCancel = () { |
| state = -canceled; |
| }; |
| |
| var sub2 = stream.listen(null); |
| Expect.equals(-running, state); |
| sub2.cancel(); |
| Expect.equals(-canceled, state); |
| } |
| |
| void testBroadcastSettingNullCallbacks() { |
| failCallback() => fail("Callback should not be called"); |
| var controller = new StreamController.broadcast( |
| onListen: failCallback, onCancel: failCallback); |
| |
| var stream = controller.stream; |
| |
| Expect.isFalse(controller.hasListener); |
| |
| controller.onListen = null; |
| |
| var sub = stream.listen(null); |
| |
| Expect.isTrue(controller.hasListener); |
| |
| controller.onCancel = null; |
| |
| sub.cancel(); |
| |
| Expect.isFalse(controller.hasListener); |
| } |
| |
| main() { |
| asyncStart(); |
| testMultiController(); |
| testSingleController(); |
| testExtraMethods(); |
| testClosed(); |
| testCloseFuture(); |
| testCloseFuture2(); |
| testCloseFuture3(); |
| testStreamEquals(); |
| testCancelThrow(); |
| testCancelThrow2(); |
| testCancelThrow3(); |
| testBroadcastListenAfterClose(); |
| testBroadcastListenAfterClosePaused(); |
| testAsBroadcastListenAfterClose(); |
| testAsBroadcastListenAfterClosePaused(); |
| testEventInListen(); |
| testSyncControllerNotReentrant(); |
| testSettingCallbacks(); |
| testSettingNullCallbacks(); |
| testBroadcastSettingCallbacks(); |
| testBroadcastSettingNullCallbacks(); |
| asyncEnd(); |
| } |