blob: 0a41089065225870f0187997a05df7749537ba74 [file] [log] [blame]
// Copyright (c) 2015, 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.
// TODO(nweiz): Get rid of this when https://codereview.chromium.org/1241723003/
// lands.
import 'dart:async';
import 'package:test/test.dart';
import 'package:test/src/util/forkable_stream.dart';
import 'package:test/src/util/stream_queue.dart';
void main() {
var controller;
var stream;
setUp(() {
var cancelFuture = new Future.value(42);
controller = new StreamController<int>(onCancel: () => cancelFuture);
stream = new ForkableStream<int>(controller.stream);
});
group("with no forks", () {
test("forwards events, errors, and close", () async {
var queue = new StreamQueue(stream);
controller.add(1);
expect(await queue.next, equals(1));
controller.add(2);
expect(await queue.next, equals(2));
controller.addError("error");
expect(queue.next, throwsA("error"));
await flushMicrotasks();
controller.add(3);
expect(await queue.next, equals(3));
controller.close();
expect(await queue.hasNext, isFalse);
});
test("listens to, pauses, and cancels the controller", () {
expect(controller.hasListener, isFalse);
var sub = stream.listen(null);
expect(controller.hasListener, isTrue);
sub.pause();
expect(controller.isPaused, isTrue);
sub.resume();
expect(controller.isPaused, isFalse);
sub.cancel();
expect(controller.hasListener, isFalse);
});
test("unpauses the controller when a fork is listened", () {
stream.listen(null).pause();
expect(controller.isPaused, isTrue);
var fork = stream.fork();
expect(controller.isPaused, isTrue);
fork.listen(null);
expect(controller.isPaused, isFalse);
});
});
group("with a fork created before the stream was listened", () {
var fork;
setUp(() {
fork = stream.fork();
});
test("forwards events, errors, and close to both branches", () async {
var queue = new StreamQueue(stream);
var forkQueue = new StreamQueue(fork);
controller.add(1);
expect(await queue.next, equals(1));
expect(await forkQueue.next, equals(1));
controller.add(2);
expect(await queue.next, equals(2));
expect(await forkQueue.next, equals(2));
controller.addError("error");
expect(queue.next, throwsA("error"));
expect(forkQueue.next, throwsA("error"));
await flushMicrotasks();
controller.add(3);
expect(await queue.next, equals(3));
expect(await forkQueue.next, equals(3));
controller.close();
expect(await queue.hasNext, isFalse);
expect(await forkQueue.hasNext, isFalse);
});
test('listens to the source when the original is listened', () {
expect(controller.hasListener, isFalse);
stream.listen(null);
expect(controller.hasListener, isTrue);
});
test('listens to the source when the fork is listened', () {
expect(controller.hasListener, isFalse);
fork.listen(null);
expect(controller.hasListener, isTrue);
});
});
test("with a fork created after the stream emitted a few events, forwards "
"future events, errors, and close to both branches", () async {
var queue = new StreamQueue(stream);
controller.add(1);
expect(await queue.next, equals(1));
controller.add(2);
expect(await queue.next, equals(2));
var fork = stream.fork();
var forkQueue = new StreamQueue(fork);
controller.add(3);
expect(await queue.next, equals(3));
expect(await forkQueue.next, equals(3));
controller.addError("error");
expect(queue.next, throwsA("error"));
expect(forkQueue.next, throwsA("error"));
await flushMicrotasks();
controller.close();
expect(await queue.hasNext, isFalse);
expect(await forkQueue.hasNext, isFalse);
});
group("with multiple forks", () {
var fork1;
var fork2;
var fork3;
var fork4;
setUp(() {
fork1 = stream.fork();
fork2 = stream.fork();
fork3 = stream.fork();
fork4 = stream.fork();
});
test("forwards events, errors, and close to all branches", () async {
var queue1 = new StreamQueue(stream);
var queue2 = new StreamQueue(fork1);
var queue3 = new StreamQueue(fork2);
var queue4 = new StreamQueue(fork3);
var queue5 = new StreamQueue(fork4);
controller.add(1);
expect(await queue1.next, equals(1));
expect(await queue2.next, equals(1));
expect(await queue3.next, equals(1));
expect(await queue4.next, equals(1));
expect(await queue5.next, equals(1));
controller.add(2);
expect(await queue1.next, equals(2));
expect(await queue2.next, equals(2));
expect(await queue3.next, equals(2));
expect(await queue4.next, equals(2));
expect(await queue5.next, equals(2));
controller.addError("error");
expect(queue1.next, throwsA("error"));
expect(queue2.next, throwsA("error"));
expect(queue3.next, throwsA("error"));
expect(queue4.next, throwsA("error"));
expect(queue5.next, throwsA("error"));
await flushMicrotasks();
controller.add(3);
expect(await queue1.next, equals(3));
expect(await queue2.next, equals(3));
expect(await queue3.next, equals(3));
expect(await queue4.next, equals(3));
expect(await queue5.next, equals(3));
controller.close();
expect(await queue1.hasNext, isFalse);
expect(await queue2.hasNext, isFalse);
expect(await queue3.hasNext, isFalse);
expect(await queue4.hasNext, isFalse);
expect(await queue5.hasNext, isFalse);
});
test("forwards events in order of forking", () async {
var queue1 = new StreamQueue(stream);
var queue2 = new StreamQueue(fork1);
var queue3 = new StreamQueue(fork2);
var queue4 = new StreamQueue(fork3);
var queue5 = new StreamQueue(fork4);
for (var i = 0; i < 4; i++) {
controller.add(i);
var queue1Fired = false;
var queue2Fired = false;
var queue3Fired = false;
var queue4Fired = false;
var queue5Fired = false;
queue5.next.then(expectAsync1((_) {
queue5Fired = true;
expect(queue1Fired, isTrue);
expect(queue2Fired, isTrue);
expect(queue3Fired, isTrue);
expect(queue4Fired, isTrue);
}));
queue1.next.then(expectAsync1((_) {
queue1Fired = true;
expect(queue2Fired, isFalse);
expect(queue3Fired, isFalse);
expect(queue4Fired, isFalse);
expect(queue5Fired, isFalse);
}));
queue4.next.then(expectAsync1((_) {
queue4Fired = true;
expect(queue1Fired, isTrue);
expect(queue2Fired, isTrue);
expect(queue3Fired, isTrue);
expect(queue5Fired, isFalse);
}));
queue2.next.then(expectAsync1((_) {
queue2Fired = true;
expect(queue1Fired, isTrue);
expect(queue3Fired, isFalse);
expect(queue4Fired, isFalse);
expect(queue5Fired, isFalse);
}));
queue3.next.then(expectAsync1((_) {
queue3Fired = true;
expect(queue1Fired, isTrue);
expect(queue2Fired, isTrue);
expect(queue4Fired, isFalse);
expect(queue5Fired, isFalse);
}));
}
});
test("pauses the source when all forks are paused and/or not listening",
() {
var sub1 = stream.listen(null);
var sub2 = fork1.listen(null);
expect(controller.isPaused, isFalse);
sub1.pause();
expect(controller.isPaused, isFalse);
sub2.pause();
expect(controller.isPaused, isTrue);
var sub3 = fork2.listen(null);
expect(controller.isPaused, isFalse);
sub3.pause();
expect(controller.isPaused, isTrue);
sub2.resume();
expect(controller.isPaused, isFalse);
sub2.cancel();
expect(controller.isPaused, isTrue);
});
test("cancels the source when all forks are canceled", () async {
var sub1 = stream.listen(null);
expect(controller.hasListener, isTrue);
var sub2 = fork1.listen(null);
expect(controller.hasListener, isTrue);
expect(sub1.cancel(), completion(isNull));
await flushMicrotasks();
expect(controller.hasListener, isTrue);
expect(sub2.cancel(), completion(isNull));
await flushMicrotasks();
expect(controller.hasListener, isTrue);
expect(fork2.listen(null).cancel(), completion(isNull));
await flushMicrotasks();
expect(controller.hasListener, isTrue);
expect(fork3.listen(null).cancel(), completion(isNull));
await flushMicrotasks();
expect(controller.hasListener, isTrue);
expect(fork4.listen(null).cancel(), completion(equals(42)));
await flushMicrotasks();
expect(controller.hasListener, isFalse);
});
});
group("modification during dispatch:", () {
test("forking during onCancel", () {
controller = new StreamController<int>(onCancel: expectAsync0(() {
expect(stream.fork().toList(), completion(isEmpty));
}));
stream = new ForkableStream<int>(controller.stream);
stream.listen(null).cancel();
});
test("forking during onPause", () {
controller = new StreamController<int>(onPause: expectAsync0(() {
stream.fork().listen(null);
}));
stream = new ForkableStream<int>(controller.stream);
stream.listen(null).pause();
// The fork created in onPause should have resumed the stream.
expect(controller.isPaused, isFalse);
});
test("forking during onData", () {
var sub;
sub = stream.listen(expectAsync1((value1) {
expect(value1, equals(1));
stream.fork().listen(expectAsync1((value2) {
expect(value2, equals(2));
}));
sub.cancel();
}));
controller.add(1);
controller.add(2);
});
test("canceling a fork during onData", () {
var fork = stream.fork();
var forkSub = fork.listen(expectAsync1((_) {}, count: 0));
stream.listen(expectAsync1((_) => forkSub.cancel()));
controller.add(null);
});
test("forking during onError", () {
var sub;
sub = stream.listen(null, onError: expectAsync1((error1) {
expect(error1, equals("error 1"));
stream.fork().listen(null, onError: expectAsync1((error2) {
expect(error2, equals("error 2"));
}));
sub.cancel();
}));
controller.addError("error 1");
controller.addError("error 2");
});
test("canceling a fork during onError", () {
var fork = stream.fork();
var forkSub = fork.listen(expectAsync1((_) {}, count: 0));
stream.listen(null, onError: expectAsync1((_) => forkSub.cancel()));
controller.addError("error");
});
test("forking during onDone", () {
stream.listen(null, onDone: expectAsync0(() {
expect(stream.fork().toList(), completion(isEmpty));
}));
controller.close();
});
test("canceling a fork during onDone", () {
var fork = stream.fork();
var forkSub = fork.listen(null, onDone: expectAsync0(() {}, count: 0));
stream.listen(null, onDone: expectAsync0(() => forkSub.cancel()));
controller.close();
});
});
group("throws an error when", () {
test("a cancelled stream is forked", () {
stream.listen(null).cancel();
expect(stream.fork().toList(), completion(isEmpty));
});
test("a cancelled stream is forked even when other forks are alive", () {
stream.fork().listen(null);
stream.listen(null).cancel();
expect(controller.hasListener, isTrue);
expect(stream.fork().toList(), completion(isEmpty));
});
test("a closed stream is forked", () async {
controller.close();
await stream.listen(null).asFuture();
expect(stream.fork().toList(), completion(isEmpty));
});
});
}
Future flushMicrotasks() => new Future.delayed(Duration.ZERO);