| Index: tests/lib_strong/async/stream_state_helper.dart
|
| diff --git a/tests/lib_strong/async/stream_state_helper.dart b/tests/lib_strong/async/stream_state_helper.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0ba61fc9e69aa060601c1c86c7ab0531cb773ad1
|
| --- /dev/null
|
| +++ b/tests/lib_strong/async/stream_state_helper.dart
|
| @@ -0,0 +1,589 @@
|
| +// 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.
|
| +
|
| +library stream_state_helper;
|
| +
|
| +import "package:unittest/unittest.dart";
|
| +import "dart:async";
|
| +import "dart:collection";
|
| +
|
| +class SubscriptionProtocolTest {
|
| + final StreamProtocolTest _streamTest;
|
| + final int id;
|
| + StreamSubscription _subscription;
|
| +
|
| + SubscriptionProtocolTest(this.id, this._subscription, this._streamTest);
|
| +
|
| + void pause([Future resumeSignal]) {
|
| + if (_subscription == null) throw new StateError("Not subscribed");
|
| + _subscription.pause(resumeSignal);
|
| + }
|
| +
|
| + void resume() {
|
| + if (_subscription == null) throw new StateError("Not subscribed");
|
| + _subscription.resume();
|
| + }
|
| +
|
| + void cancel() {
|
| + if (_subscription == null) throw new StateError("Not subscribed");
|
| + _subscription.cancel();
|
| + _subscription = null;
|
| + }
|
| +
|
| + void expectData(var data, [void action()]) {
|
| + _streamTest._expectData(this, data, action);
|
| + }
|
| +
|
| + void expectError(var error, [void action()]) {
|
| + _streamTest._expectError(this, error, action);
|
| + }
|
| +
|
| + void expectDone([void action()]) {
|
| + _streamTest._expectDone(this, action);
|
| + }
|
| +}
|
| +
|
| +class StreamProtocolTest {
|
| + bool trace = false;
|
| + final bool isBroadcast;
|
| + final bool isAsBroadcast;
|
| + StreamController _controller;
|
| + Stream _controllerStream;
|
| + // Most recent subscription created. Used as default for pause/resume.
|
| + SubscriptionProtocolTest _latestSubscription;
|
| + List<Event> _expectations = new List<Event>();
|
| + int _nextExpectationIndex = 0;
|
| + int _subscriptionIdCounter = 0;
|
| + Function _onComplete;
|
| +
|
| + StreamProtocolTest.broadcast({ bool sync: false })
|
| + : isBroadcast = true, isAsBroadcast = false {
|
| + _controller = new StreamController.broadcast(
|
| + sync: sync,
|
| + onListen: _onListen,
|
| + onCancel: _onCancel);
|
| + _controllerStream = _controller.stream;
|
| + _onComplete = expectAsync((){
|
| + _onComplete = null; // Being null marks the test as being complete.
|
| + });
|
| + }
|
| +
|
| + StreamProtocolTest({ bool sync: false })
|
| + : isBroadcast = false, isAsBroadcast = false {
|
| + _controller = new StreamController(
|
| + sync: sync,
|
| + onListen: _onListen,
|
| + onPause: _onPause,
|
| + onResume: _onResume,
|
| + onCancel: _onCancel);
|
| + _controllerStream = _controller.stream;
|
| + _onComplete = expectAsync((){
|
| + _onComplete = null; // Being null marks the test as being complete.
|
| + });
|
| + }
|
| +
|
| + StreamProtocolTest.asBroadcast({ bool sync: false })
|
| + : isBroadcast = false, isAsBroadcast = true {
|
| + _controller = new StreamController(
|
| + sync: sync,
|
| + onListen: _onListen,
|
| + onPause: _onPause,
|
| + onResume: _onResume,
|
| + onCancel: _onCancel);
|
| + _controllerStream = _controller.stream.asBroadcastStream(
|
| + onListen: _onBroadcastListen,
|
| + onCancel: _onBroadcastCancel);
|
| + _onComplete = expectAsync((){
|
| + _onComplete = null; // Being null marks the test as being complete.
|
| + });
|
| + }
|
| +
|
| + // Actions on the stream and controller.
|
| + void add(var data) { _controller.add(data); }
|
| + void error(var error) { _controller.addError(error); }
|
| + void close() { _controller.close(); }
|
| +
|
| + SubscriptionProtocolTest listen({bool cancelOnError : false}) {
|
| + int subscriptionId = _subscriptionIdCounter++;
|
| +
|
| + StreamSubscription subscription = _controllerStream.listen(
|
| + (var data) { _onData(subscriptionId, data); },
|
| + onError: (Object error) { _onError(subscriptionId, error); },
|
| + onDone: () { _onDone(subscriptionId); },
|
| + cancelOnError: cancelOnError);
|
| + _latestSubscription =
|
| + new SubscriptionProtocolTest(subscriptionId, subscription, this);
|
| + if (trace) {
|
| + print("[Listen #$subscriptionId(#${_latestSubscription.hashCode})]");
|
| + }
|
| + return _latestSubscription;
|
| + }
|
| +
|
| + // Actions on the most recently created subscription.
|
| + void pause([Future resumeSignal]) {
|
| + _latestSubscription.pause(resumeSignal);
|
| + }
|
| +
|
| + void resume() {
|
| + _latestSubscription.resume();
|
| + }
|
| +
|
| + void cancel() {
|
| + _latestSubscription.cancel();
|
| + _latestSubscription = null;
|
| + }
|
| +
|
| + // End the test now. There must be no open expectations, and no furter
|
| + // expectations will be allowed.
|
| + // Called automatically by an onCancel event on a non-broadcast stream.
|
| + void terminate() {
|
| + if (_nextExpectationIndex != _expectations.length) {
|
| + _withNextExpectation((Event expect) {
|
| + _fail("Expected: $expect\n"
|
| + "Found : Early termination.\n${expect._stackTrace}");
|
| + });
|
| + }
|
| + _onComplete();
|
| + }
|
| +
|
| + // Handling of stream events.
|
| + void _onData(int id, var data) {
|
| + if (trace) print("[Data#$id : $data]");
|
| + _withNextExpectation((Event expect) {
|
| + if (!expect.matchData(id, data)) {
|
| + _fail("Expected: $expect\n"
|
| + "Found : [Data#$id: $data]\n${expect._stackTrace}");
|
| + }
|
| + });
|
| + }
|
| +
|
| + void _onError(int id, Object error) {
|
| + if (trace) print("[Error#$id : $error]");
|
| + _withNextExpectation((Event expect) {
|
| + if (!expect.matchError(id, error)) {
|
| + _fail("Expected: $expect\n"
|
| + "Found : [Error#$id: ${error}]\n${expect._stackTrace}");
|
| + }
|
| + });
|
| + }
|
| +
|
| + void _onDone(int id) {
|
| + if (trace) print("[Done#$id]");
|
| + _withNextExpectation((Event expect) {
|
| + if (!expect.matchDone(id)) {
|
| + _fail("Expected: $expect\n"
|
| + "Found : [Done#$id]\n${expect._stackTrace}");
|
| + }
|
| + });
|
| + }
|
| +
|
| + void _onPause() {
|
| + if (trace) print("[Pause]");
|
| + _withNextExpectation((Event expect) {
|
| + if (!expect.matchPause()) {
|
| + _fail("Expected: $expect\n"
|
| + "Found : [Paused]\n${expect._stackTrace}");
|
| + }
|
| + });
|
| + }
|
| +
|
| + void _onResume() {
|
| + if (trace) print("[Resumed]");
|
| + _withNextExpectation((Event expect) {
|
| + if (!expect.matchResume()) {
|
| + _fail("Expected: $expect\n"
|
| + "Found : [Resumed]\n${expect._stackTrace}");
|
| + }
|
| + });
|
| + }
|
| +
|
| + void _onListen() {
|
| + if (trace) print("[Subscribed]");
|
| + _withNextExpectation((Event expect) {
|
| + if (!expect.matchSubscribe()) {
|
| + _fail("Expected: $expect\n"
|
| + "Found: [Subscribed]\n${expect._stackTrace}");
|
| + }
|
| + });
|
| + }
|
| +
|
| + void _onCancel() {
|
| + if (trace) print("[Cancelled]");
|
| + _withNextExpectation((Event expect) {
|
| + if (!expect.matchCancel()) {
|
| + _fail("Expected: $expect\n"
|
| + "Found: [Cancelled]\n${expect._stackTrace}");
|
| + }
|
| + });
|
| + }
|
| +
|
| + void _onBroadcastListen(StreamSubscription sub) {
|
| + if (trace) print("[BroadcastListen]");
|
| + _withNextExpectation((Event expect) {
|
| + if (!expect.matchBroadcastListen(sub)) {
|
| + _fail("Expected: $expect\n"
|
| + "Found: [BroadcastListen]\n${expect._stackTrace}");
|
| + }
|
| + });
|
| + }
|
| +
|
| + void _onBroadcastCancel(StreamSubscription sub) {
|
| + if (trace) print("[BroadcastCancel]");
|
| + _withNextExpectation((Event expect) {
|
| + if (!expect.matchBroadcastCancel(sub)) {
|
| + _fail("Expected: $expect\n"
|
| + "Found: [BroadcastCancel]\n${expect._stackTrace}");
|
| + }
|
| + });
|
| + }
|
| +
|
| + void _withNextExpectation(void action(Event expect)) {
|
| + if (_nextExpectationIndex == _expectations.length) {
|
| + _nextExpectationIndex++;
|
| + action(new MismatchEvent());
|
| + } else {
|
| + Event next = _expectations[_nextExpectationIndex++];
|
| + action(next);
|
| + }
|
| + }
|
| +
|
| + // Adds _expectations.
|
| + void expectAny([void action()]) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + _expectations.add(new LogAnyEvent(action));
|
| + }
|
| +
|
| + void expectData(var data, [void action()]) {
|
| + _expectData(null, data, action);
|
| + }
|
| +
|
| + void _expectData(SubscriptionProtocolTest sub, var data, void action()) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + _expectations.add(new DataEvent(sub, data, action));
|
| + }
|
| +
|
| + void expectError(var error, [void action()]) {
|
| + _expectError(null, error, action);
|
| + }
|
| +
|
| + void _expectError(SubscriptionProtocolTest sub, var error, void action()) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + _expectations.add(new ErrorEvent(sub, error, action));
|
| + }
|
| +
|
| + void expectDone([void action()]) {
|
| + _expectDone(null, action);
|
| + }
|
| +
|
| + void _expectDone(SubscriptionProtocolTest sub, [void action()]) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + _expectations.add(new DoneEvent(sub, action));
|
| + }
|
| +
|
| + void expectPause([void action()]) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + _expectations.add(new PauseCallbackEvent(action));
|
| + }
|
| +
|
| + void expectResume([void action()]) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + _expectations.add(new ResumeCallbackEvent(action));
|
| + }
|
| +
|
| + void expectListen([void action()]) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + _expectations.add(
|
| + new SubscriptionCallbackEvent(action));
|
| + }
|
| +
|
| + void expectCancel([void action()]) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + _expectations.add(
|
| + new CancelCallbackEvent(action));
|
| + }
|
| +
|
| + void expectBroadcastListen([void action(StreamSubscription sub)]) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + if (!isAsBroadcast) throw new StateError("Not an asBroadcast stream");
|
| + _expectations.add(new BroadcastListenCallbackEvent(action));
|
| + }
|
| +
|
| + void expectBroadcastCancel([void action(StreamSubscription sub)]) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + if (!isAsBroadcast) throw new StateError("Not an asBroadcast stream");
|
| + _expectations.add(new BroadcastCancelCallbackEvent(action));
|
| + }
|
| +
|
| + void expectBroadcastListenOpt([void action(StreamSubscription sub)]) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + if (!isAsBroadcast) return;
|
| + _expectations.add(new BroadcastListenCallbackEvent(action));
|
| + }
|
| +
|
| + void expectBroadcastCancelOpt([void action(StreamSubscription sub)]) {
|
| + if (_onComplete == null) {
|
| + _fail("Adding expectation after completing");
|
| + }
|
| + if (!isAsBroadcast) return;
|
| + _expectations.add(new BroadcastCancelCallbackEvent(action));
|
| + }
|
| +
|
| + void _fail(String message) {
|
| + if (_nextExpectationIndex == 0) {
|
| + throw "Unexpected event:\n$message\nNo earlier events matched.";
|
| + }
|
| + StringBuffer buf = new StringBuffer();
|
| + for (int i = 0; i < _expectations.length; i++) {
|
| + if (i == _nextExpectationIndex - 1) {
|
| + buf.write("->");
|
| + } else {
|
| + buf.write(" ");
|
| + }
|
| + buf.write(_expectations[i]);
|
| + buf.write("\n");
|
| + }
|
| + throw "Unexpected event:\n$message\nAll expectations:\n$buf";
|
| + }
|
| +}
|
| +
|
| +class Event {
|
| + Function _action;
|
| + StackTrace _stackTrace;
|
| + Event(void action())
|
| + : _action = (action == null) ? null : expectAsync(action) {
|
| + try { throw 0; } catch (_, s) { _stackTrace = s; }
|
| + }
|
| + Event.broadcast(void action(StreamSubscription sub))
|
| + : _action = (action == null) ? null : expectAsync(action) {
|
| + try { throw 0; } catch (_, s) { _stackTrace = s; }
|
| + }
|
| +
|
| + bool matchData(int id, var data) {
|
| + return false;
|
| + }
|
| +
|
| + bool matchError(int id, e) {
|
| + return false;
|
| + }
|
| +
|
| + bool matchDone(int id) {
|
| + return false;
|
| + }
|
| +
|
| + bool matchPause() {
|
| + if (!_testPause()) return false;
|
| + if (_action != null) _action();
|
| + return true;
|
| + }
|
| +
|
| + bool matchResume() {
|
| + if (!_testResume()) return false;
|
| + if (_action != null) _action();
|
| + return true;
|
| + }
|
| +
|
| + bool matchSubscribe() {
|
| + if (!_testSubscribe()) return false;
|
| + if (_action != null) _action();
|
| + return true;
|
| + }
|
| +
|
| + bool matchCancel() {
|
| + if (!_testCancel()) return false;
|
| + if (_action != null) _action();
|
| + return true;
|
| + }
|
| +
|
| + bool matchBroadcastListen(StreamSubscription sub) {
|
| + if (!_testBroadcastListen()) return false;
|
| + if (_action != null) _action(sub);
|
| + return true;
|
| + }
|
| +
|
| + bool matchBroadcastCancel(StreamSubscription sub) {
|
| + if (!_testBroadcastCancel()) return false;
|
| + if (_action != null) _action(sub);
|
| + return true;
|
| + }
|
| +
|
| + bool _testData(_) => false;
|
| + bool _testError(_) => false;
|
| + bool _testDone() => false;
|
| + bool _testPause() => false;
|
| + bool _testResume() => false;
|
| + bool _testSubscribe() => false;
|
| + bool _testCancel() => false;
|
| + bool _testBroadcastListen() => false;
|
| + bool _testBroadcastCancel() => false;
|
| +}
|
| +
|
| +class SubscriptionEvent extends Event {
|
| + SubscriptionProtocolTest subscription;
|
| + SubscriptionEvent(this.subscription, void action()) : super(action);
|
| +
|
| + bool matchData(int id, var data) {
|
| + if (subscription != null && subscription.id != id) return false;
|
| + if (!_testData(data)) return false;
|
| + if (_action != null) _action();
|
| + return true;
|
| + }
|
| +
|
| + bool matchError(int id, e) {
|
| + if (subscription != null && subscription.id != id) return false;
|
| + if (!_testError(e)) return false;
|
| + if (_action != null) _action();
|
| + return true;
|
| + }
|
| +
|
| + bool matchDone(int id) {
|
| + if (subscription != null && subscription.id != id) return false;
|
| + if (!_testDone()) return false;
|
| + if (_action != null) _action();
|
| + return true;
|
| + }
|
| +
|
| + String get _id => (subscription == null) ? "" : "#${subscription.id}";
|
| +}
|
| +
|
| +class MismatchEvent extends Event {
|
| + MismatchEvent() : super(null);
|
| + toString() => "[No event expected]";
|
| +}
|
| +
|
| +class DataEvent extends SubscriptionEvent {
|
| + final data;
|
| + DataEvent(SubscriptionProtocolTest sub, this.data, void action())
|
| + : super(sub, action);
|
| + bool _testData(var data) => this.data == data;
|
| + String toString() => "[Data$_id: $data]";
|
| +}
|
| +
|
| +class ErrorEvent extends SubscriptionEvent {
|
| + final error;
|
| + ErrorEvent(SubscriptionProtocolTest sub, this.error, void action())
|
| + : super(sub, action);
|
| + bool _testError(error) => this.error == error;
|
| + String toString() => "[Error$_id: $error]";
|
| +}
|
| +
|
| +class DoneEvent extends SubscriptionEvent {
|
| + DoneEvent(SubscriptionProtocolTest sub, void action()) : super(sub, action);
|
| + bool _testDone() => true;
|
| + String toString() => "[Done$_id]";
|
| +}
|
| +
|
| +class PauseCallbackEvent extends Event {
|
| + PauseCallbackEvent(void action()) : super(action);
|
| + bool _testPause() => true;
|
| + String toString() => "[Paused]";
|
| +}
|
| +
|
| +class ResumeCallbackEvent extends Event {
|
| + ResumeCallbackEvent(void action()) : super(action);
|
| + bool _testResume() => true;
|
| + String toString() => "[Resumed]";
|
| +}
|
| +
|
| +class SubscriptionCallbackEvent extends Event {
|
| + SubscriptionCallbackEvent(void action()) : super(action);
|
| + bool _testSubscribe() => true;
|
| + String toString() => "[Subscribed]";
|
| +}
|
| +
|
| +class CancelCallbackEvent extends Event {
|
| + CancelCallbackEvent(void action()) : super(action);
|
| + bool _testCancel() => true;
|
| + String toString() => "[Cancelled]";
|
| +}
|
| +
|
| +class BroadcastCancelCallbackEvent extends Event {
|
| + BroadcastCancelCallbackEvent(void action(StreamSubscription sub))
|
| + : super.broadcast(action);
|
| + bool _testBroadcastCancel() => true;
|
| + String toString() => "[BroadcastCancel]";
|
| +}
|
| +
|
| +class BroadcastListenCallbackEvent extends Event {
|
| + BroadcastListenCallbackEvent(void action(StreamSubscription sub))
|
| + : super.broadcast(action);
|
| + bool _testBroadcastListen() => true;
|
| + String toString() => "[BroadcastListen]";
|
| +}
|
| +
|
| +/** Event matcher that matches any other event. */
|
| +class LogAnyEvent extends Event {
|
| + String _actual = "*Not matched yet*";
|
| +
|
| + LogAnyEvent(void action()) : super(action);
|
| +
|
| + bool _testData(var data) {
|
| + _actual = "*[Data $data]";
|
| + return true;
|
| + }
|
| +
|
| + bool _testError(error) {
|
| + _actual = "*[Error ${error}]";
|
| + return true;
|
| + }
|
| +
|
| + bool _testDone() {
|
| + _actual = "*[Done]";
|
| + return true;
|
| + }
|
| +
|
| + bool _testPause() {
|
| + _actual = "*[Paused]";
|
| + return true;
|
| + }
|
| +
|
| + bool _testResume() {
|
| + _actual = "*[Resumed]";
|
| + return true;
|
| + }
|
| +
|
| + bool _testSubcribe() {
|
| + _actual = "*[Subscribed]";
|
| + return true;
|
| + }
|
| +
|
| + bool _testCancel() {
|
| + _actual = "*[Cancelled]";
|
| + return true;
|
| + }
|
| +
|
| + bool _testBroadcastListen() {
|
| + _actual = "*[BroadcastListen]";
|
| + return true;
|
| + }
|
| +
|
| + bool _testBroadcastCancel() {
|
| + _actual = "*[BroadcastCancel]";
|
| + return true;
|
| + }
|
| +
|
| + /** Returns a representation of the event it was tested against. */
|
| + String toString() => _actual;
|
| +}
|
|
|