Files
sdk/tests/language/async_star_test.dart
T
Jacob Richman 2dcd56ef43 Format all tests.
There are far too many files here to review everyone carefully.
Spot checking most of the diffs look good as test code is generally written
with less care than application code so lots of ugly formatting get through.
If people notice files where the automated formatting bothers them feel free
to comment indicating file names and I'll move spaces within comments to make
the formatting cleaner and use comments to force block formatting as I have
done for other case where formatting looked bad.

BUG=
R=efortuna@google.com

Review-Url: https://codereview.chromium.org/2771453003 .
2017-04-17 14:53:02 -07:00

887 lines
17 KiB
Dart

// 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.
library async_star_test;
import "package:unittest/unittest.dart";
import "dart:async";
main() {
group("basic", () {
test("empty", () {
f() async* {}
return f().toList().then((v) {
expect(v, equals([]));
});
});
test("single", () {
f() async* {
yield 42;
}
return f().toList().then((v) {
expect(v, equals([42]));
});
});
test("call delays", () {
var list = [];
f() async* {
list.add(1);
yield 2;
}
var res = f().forEach(list.add);
list.add(0);
return res.whenComplete(() {
expect(list, equals([0, 1, 2]));
});
});
test("throws", () {
f() async* {
yield 1;
throw 2;
}
var completer = new Completer();
var list = [];
f().listen(list.add,
onError: (v) => list.add("$v"), onDone: completer.complete);
return completer.future.whenComplete(() {
expect(list, equals([1, "2"]));
});
});
test("multiple", () {
f() async* {
for (int i = 0; i < 10; i++) {
yield i;
}
}
return expectList(f(), new List.generate(10, id));
});
test("allows await", () {
f() async* {
var x = await new Future.value(42);
yield x;
x = await new Future.value(42);
}
return expectList(f(), [42]);
});
test("allows await in loop", () {
f() async* {
for (int i = 0; i < 10; i++) {
yield await i;
}
}
return expectList(f(), new List.generate(10, id));
});
test("allows yield*", () {
f() async* {
yield* new Stream.fromIterable([1, 2, 3]);
}
return expectList(f(), [1, 2, 3]);
});
test("allows yield* of async*", () {
f(n) async* {
yield n;
if (n == 0) return;
yield* f(n - 1);
yield n;
}
return expectList(f(3), [3, 2, 1, 0, 1, 2, 3]);
});
test("Cannot yield* non-stream", () {
f(s) async* {
yield* s;
}
return f(42).transform(getErrors).single.then((v) {
// Not implementing Stream.
expect(v is Error, isTrue);
});
});
test("Cannot yield* non-stream", () {
f(s) async* {
yield* s;
}
return f(new NotAStream()).transform(getErrors).single.then((v) {
// Not implementing Stream.
expect(v is Error, isTrue);
});
});
});
group("yield statement context", () {
test("plain", () {
f() async* {
yield 0;
}
return expectList(f(), [0]);
});
test("if-then-else", () {
f(b) async* {
if (b)
yield 0;
else
yield 1;
}
return expectList(f(true), [0]).whenComplete(() {
expectList(f(false), [1]);
});
});
test("block", () {
f() async* {
yield 0;
{
yield 1;
}
yield 2;
}
return expectList(f(), [0, 1, 2]);
});
test("labeled", () {
f() async* {
label1:
yield 0;
}
return expectList(f(), [0]);
});
test("for-loop", () {
f() async* {
for (int i = 0; i < 3; i++) yield i;
}
return expectList(f(), [0, 1, 2]);
});
test("for-in-loop", () {
f() async* {
for (var i in [0, 1, 2]) yield i;
}
return expectList(f(), [0, 1, 2]);
});
test("await for-in-loop", () {
f() async* {
await for (var i in new Stream.fromIterable([0, 1, 2])) yield i;
}
return expectList(f(), [0, 1, 2]);
});
test("while-loop", () {
f() async* {
int i = 0;
while (i < 3) yield i++;
}
return expectList(f(), [0, 1, 2]);
});
test("do-while-loop", () {
f() async* {
int i = 0;
do yield i++; while (i < 3);
}
return expectList(f(), [0, 1, 2]);
});
test("try-catch-finally", () {
f() async* {
try {
yield 0;
} catch (e) {
yield 1;
} finally {
yield 2;
}
}
return expectList(f(), [0, 2]);
});
test("try-catch-finally 2", () {
f() async* {
try {
yield throw 0;
} catch (e) {
yield 1;
} finally {
yield 2;
}
}
return expectList(f(), [1, 2]);
});
test("switch-case", () {
f(v) async* {
switch (v) {
case 0:
yield 0;
continue label1;
label1:
case 1:
yield 1;
break;
default:
yield 2;
}
}
return expectList(f(0), [0, 1]).whenComplete(() {
return expectList(f(1), [1]);
}).whenComplete(() {
return expectList(f(2), [2]);
});
});
test("dead-code return", () {
f() async* {
return;
yield 1;
}
return expectList(f(), []);
});
test("dead-code throw", () {
f() async* {
try {
throw 0;
yield 1;
} catch (_) {}
}
return expectList(f(), []);
});
test("dead-code break", () {
f() async* {
while (true) {
break;
yield 1;
}
}
return expectList(f(), []);
});
test("dead-code break 2", () {
f() async* {
label:
{
break label;
yield 1;
}
}
return expectList(f(), []);
});
test("dead-code continue", () {
f() async* {
do {
continue;
yield 1;
} while (false);
}
return expectList(f(), []);
});
});
group("yield expressions", () {
test("local variable", () {
f() async* {
var x = 42;
yield x;
}
return expectList(f(), [42]);
});
test("constant variable", () {
f() async* {
const x = 42;
yield x;
}
return expectList(f(), [42]);
});
test("function call", () {
g() => 42;
f() async* {
yield g();
}
return expectList(f(), [42]);
});
test("unary operator", () {
f() async* {
var x = -42;
yield -x;
}
return expectList(f(), [42]);
});
test("binary operator", () {
f() async* {
var x = 21;
yield x + x;
}
return expectList(f(), [42]);
});
test("ternary operator", () {
f() async* {
var x = 21;
yield x == 21 ? x + x : x;
}
return expectList(f(), [42]);
});
test("suffix post-increment", () {
f() async* {
var x = 42;
yield x++;
}
return expectList(f(), [42]);
});
test("suffix pre-increment", () {
f() async* {
var x = 41;
yield ++x;
}
return expectList(f(), [42]);
});
test("assignment", () {
f() async* {
var x = 37;
yield x = 42;
}
return expectList(f(), [42]);
});
test("assignment op", () {
f() async* {
var x = 41;
yield x += 1;
}
return expectList(f(), [42]);
});
test("await", () {
f() async* {
yield await new Future.value(42);
}
return expectList(f(), [42]);
});
test("index operator", () {
f() async* {
var x = [42];
yield x[0];
}
return expectList(f(), [42]);
});
test("function expression block", () {
var o = new Object();
f() async* {
yield () {
return o;
};
}
return f().first.then((v) {
expect(v(), same(o));
});
});
test("function expression arrow", () {
var o = new Object();
f() async* {
yield () => o;
}
return f().first.then((v) {
expect(v(), same(o));
});
});
test("function expression block async", () {
var o = new Object();
f() async* {
yield () async {
return o;
};
}
return f().first.then((v) => v()).then((v) {
expect(v, same(o));
});
});
test("function expression arrow async", () {
var o = new Object();
f() async* {
yield () async => o;
}
return f().first.then((v) => v()).then((v) {
expect(v, same(o));
});
});
test("function expression block async*", () {
var o = new Object();
f() async* {
yield () async* {
yield o;
};
}
return f().first.then((v) => v().first).then((v) {
expect(v, same(o));
});
});
});
group("loops", () {
test("simple yield", () {
f() async* {
for (int i = 0; i < 3; i++) {
yield i;
}
}
return expectList(f(), [0, 1, 2]);
});
test("yield in double loop", () {
f() async* {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
yield i * 2 + j;
}
}
}
return expectList(f(), [0, 1, 2, 3, 4, 5]);
});
test("yield in try body", () {
var list = [];
f() async* {
for (int i = 0; i < 3; i++) {
try {
yield i;
} finally {
list.add("$i");
}
}
}
return expectList(f(), [0, 1, 2]).whenComplete(() {
expect(list, equals(["0", "1", "2"]));
});
});
test("yield in catch", () {
var list = [];
f() async* {
for (int i = 0; i < 3; i++) {
try {
throw i;
} catch (e) {
yield e;
} finally {
list.add("$i");
}
}
}
return expectList(f(), [0, 1, 2]).whenComplete(() {
expect(list, equals(["0", "1", "2"]));
});
});
test("yield in finally", () {
var list = [];
f() async* {
for (int i = 0; i < 3; i++) {
try {
throw i;
} finally {
yield i;
list.add("$i");
continue;
}
}
}
return expectList(f(), [0, 1, 2]).whenComplete(() {
expect(list, equals(["0", "1", "2"]));
});
});
test("keep yielding after cancel", () {
f() async* {
for (int i = 0; i < 10; i++) {
try {
yield i;
} finally {
continue;
}
}
}
return expectList(f().take(3), [0, 1, 2]);
});
});
group("canceling", () {
// Stream.take(n) automatically cancels after seeing the n'th value.
test("cancels at yield", () {
Completer exits = new Completer();
var list = [];
f() async* {
try {
list.add(0);
yield list.add(1);
list.add(2);
} finally {
exits.complete(3);
}
}
// No events must be fired synchronously in response to a listen.
var subscription = f().listen((v) {
fail("Received event $v");
}, onDone: () {
fail("Received done");
});
// No events must be delivered after a cancel.
subscription.cancel();
return exits.future.then((v) {
expect(v, equals(3));
expect(list, equals([0, 1]));
});
});
test("does cancel eventually", () {
var exits = new Completer();
var list = [];
f() async* {
int i = 0;
try {
while (true) yield i++;
} finally {
list.add("a");
exits.complete(i);
}
}
return expectList(f().take(5), [0, 1, 2, 3, 4])
.then((_) => exits.future)
.then((v) {
expect(v, greaterThan(4));
expect(list, ["a"]);
});
});
group("at index", () {
f() async* {
try {
yield await new Future.microtask(() => 1);
} finally {
try {
yield await new Future.microtask(() => 2);
} finally {
yield await new Future.microtask(() => 3);
}
}
}
test("- all, sanity check", () {
return expectList(f(), [1, 2, 3]);
});
test("after end", () {
return expectList(f().take(4), [1, 2, 3]);
});
test("at end", () {
return expectList(f().take(3), [1, 2, 3]);
});
test("before end", () {
return expectList(f().take(2), [1, 2]);
});
test("early", () {
return expectList(f().take(1), [1]);
});
test("at start", () {
return expectList(f().take(0), []);
});
});
});
group("pausing", () {
test("pauses execution at yield for at least a microtask", () {
var list = [];
f() async* {
list.add(1);
yield 2;
list.add(3);
yield 4;
list.add(5);
}
var done = new Completer();
var sub = f().listen((v) {
if (v == 2) {
expect(list, equals([1]));
} else if (v == 4) {
expect(list, equals([1, 3]));
} else {
fail("Unexpected value $v");
}
}, onDone: () {
expect(list, equals([1, 3, 5]));
done.complete();
});
return done.future;
});
test("pause stops execution at yield", () {
var list = [];
f() async* {
list.add(1);
yield 2;
list.add(3);
yield 4;
list.add(5);
}
var done = new Completer();
var sub;
sub = f().listen((v) {
if (v == 2) {
expect(list, equals([1]));
sub.pause();
new Timer(MS * 300, () {
expect(list.length, lessThan(3));
sub.resume();
});
} else if (v == 4) {
expect(list, equals([1, 3]));
} else {
fail("Unexpected value $v");
}
}, onDone: () {
expect(list, equals([1, 3, 5]));
done.complete();
});
return done.future;
});
test("pause stops execution at yield 2", () {
var list = [];
f() async* {
int i = 0;
while (true) {
yield i;
list.add(i);
i++;
}
}
int expected = 0;
var done = new Completer();
var sub;
sub = f().listen((v) {
expect(v, equals(expected++));
if (v % 5 == 0) {
sub.pause(new Future.delayed(MS * 300));
} else if (v == 17) {
sub.cancel();
done.complete();
}
}, onDone: () {
fail("Unexpected done!");
});
return done.future.whenComplete(() {
expect(list.length == 18 || list.length == 19, isTrue);
});
});
});
group("await for", () {
mkStream(int n) async* {
for (int i = 0; i < n; i++) yield i;
}
test("simple stream", () {
f(s) async {
var r = 0;
await for (var v in s) r += v;
return r;
}
return f(mkStream(5)).then((v) {
expect(v, equals(10));
});
});
test("simple stream, await", () {
f(s) async {
var r = 0;
await for (var v in s) r += await new Future.microtask(() => v);
return r;
}
return f(mkStream(5)).then((v) {
expect(v, equals(10));
});
});
test("simple stream reyield", () {
f(s) async* {
var r = 0;
await for (var v in s) yield r += v;
}
return expectList(f(mkStream(5)), [0, 1, 3, 6, 10]);
});
test("simple stream, await, reyield", () {
f(s) async* {
var r = 0;
await for (var v in s) yield r += await new Future.microtask(() => v);
}
return expectList(f(mkStream(5)), [0, 1, 3, 6, 10]);
});
test("nested", () {
f() async {
var r = 0;
await for (var i in mkStream(5)) {
await for (var j in mkStream(3)) {
r += i * j;
}
}
return r;
}
return f().then((v) {
expect(v, equals((1 + 2 + 3 + 4) * (1 + 2)));
});
});
test("nested, await", () {
f() async {
var r = 0;
await for (var i in mkStream(5)) {
await for (var j in mkStream(3)) {
r += await new Future.microtask(() => i * j);
}
}
return r;
}
return f().then((v) {
expect(v, equals((1 + 2 + 3 + 4) * (1 + 2)));
});
});
test("nested, await * 2", () {
f() async {
var r = 0;
await for (var i in mkStream(5)) {
var ai = await new Future.microtask(() => i);
await for (var j in mkStream(3)) {
r += await new Future.microtask(() => ai * j);
}
}
return r;
}
return f().then((v) {
expect(v, equals((1 + 2 + 3 + 4) * (1 + 2)));
});
});
});
}
// Obscuring identity function.
id(x) {
try {
if (x != null) throw x;
} catch (e) {
return e;
}
return null;
}
expectList(stream, list) {
return stream.toList().then((v) {
expect(v, equals(list));
});
}
const MS = const Duration(milliseconds: 1);
var getErrors = new StreamTransformer.fromHandlers(handleData: (data, sink) {
fail("Unexpected value");
}, handleError: (e, s, sink) {
sink.add(e);
}, handleDone: (sink) {
sink.close();
});
class NotAStream {
listen(oData, {onError, onDone, cancelOnError}) {
fail("Not implementing Stream.");
}
}