Index: test/codegen/language/async_star_test.dart |
diff --git a/test/codegen/language/async_star_test.dart b/test/codegen/language/async_star_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1e8cef6a8c3a230bff374711744570a70e622e68 |
--- /dev/null |
+++ b/test/codegen/language/async_star_test.dart |
@@ -0,0 +1,932 @@ |
+// 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_start_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; } |
+ // TODO(jmesserly): use tear off. For now this is a workaround for: |
+ // https://github.com/dart-lang/dev_compiler/issues/269 |
+ var res = f().forEach((x) => list.add(x)); |
+ 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 = []; |
+ // TODO(jmesserly): use tear off. For now this is a workaround for: |
+ // https://github.com/dart-lang/dev_compiler/issues/269 |
+ f().listen((x) => list.add(x), |
+ 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]); |
+ }); |
+ |
+ // VM issue 2238 |
+ test("labeled 2", () { /// 01: ok |
+ f() async* { /// 01: continued |
+ label1: label2: yield 0; /// 01: continued |
+ } /// 01: continued |
+ return expectList(f(), [0]); /// 01: continued |
+ }); /// 01: continued |
+ |
+ 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]); |
+ }); |
+ |
+ // TODO(jmesserly): restore this when we fix |
+ // https://github.com/dart-lang/dev_compiler/issues/263 |
+ /*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), []); |
+ }); |
+ }); |
+ |
+ // Crashes dart2js. |
+ // test("regression-fugl/fisk", () { |
+ // var res = []; |
+ // fisk() async* { |
+ // res.add("+fisk"); |
+ // try { |
+ // for (int i = 0; i < 2; i++) { |
+ // yield await new Future.microtask(() => i); |
+ // } |
+ // } finally { |
+ // res.add("-fisk"); |
+ // } |
+ // } |
+ |
+ // fugl(int count) async { |
+ // res.add("fisk $count"); |
+ // try { |
+ // await for(int i in fisk().take(count)) res.add(i); |
+ // } finally { |
+ // res.add("done"); |
+ // } |
+ // } |
+ |
+ // return fugl(3).whenComplete(() => fugl(2)) |
+ // .whenComplete(() => fugl(1)) |
+ // .whenComplete(() { |
+ // expect(res, ["fisk 3", "+fisk", 0, 1, "-fisk", "done", |
+ // "fisk 2", "+fisk", 0, 1, "-fisk", "done", |
+ // "fisk 1", "+fisk", 0, "done", "-fisk", ]); |
+ // }); |
+ // }); |
+ |
+ }); |
+ |
+ 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); |
+ }); |
+ }); |
+ |
+ test("canceling while paused at yield", () { /// 02: ok |
+ var list = []; /// 02: continued |
+ var sync = new Sync(); /// 02: continued |
+ f() async* { /// 02: continued |
+ list.add("*1"); /// 02: continued |
+ yield 1; /// 02: continued |
+ await sync.wait(); /// 02: continued |
+ sync.release(); /// 02: continued |
+ list.add("*2"); /// 02: continued |
+ yield 2; /// 02: continued |
+ list.add("*3"); /// 02: continued |
+ }; /// 02: continued |
+ var stream = f(); /// 02: continued |
+ // TODO(jmesserly): added workaround for: |
+ // https://github.com/dart-lang/dev_compiler/issues/269 |
+ var sub = stream.listen((x) => list.add(x)); /// 02: continued |
+ return sync.wait().whenComplete(() { /// 02: continued |
+ expect(list, equals(["*1", 1])); /// 02: continued |
+ sub.pause(); /// 02: continued |
+ return sync.wait(); /// 02: continued |
+ }).whenComplete(() { /// 02: continued |
+ expect(list, equals(["*1", 1, "*2"])); /// 02: continued |
+ sub.cancel(); /// 02: continued |
+ return new Future.delayed(MS * 200, () { /// 02: continued |
+ // Should not have yielded 2 or added *3 while paused. /// 02: continued |
+ expect(list, equals(["*1", 1, "*2"])); /// 02: continued |
+ }); /// 02: continued |
+ }); /// 02: continued |
+ }, skip: "other impls aren't passing this test, see " |
+ "https://github.com/dart-lang/sdk/issues/22853"); /// 02: continued |
+ }); |
+ |
+ 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 - take", () { /// 03: ok |
+ f(s) async { /// 03: continued |
+ var r = 0; /// 03: continued |
+ await for(var v in s.take(5)) r += v; /// 03: continued |
+ return r; /// 03: continued |
+ } /// 03: continued |
+ return f(mkStream(10)).then((v) { /// 03: continued |
+ expect(v, equals(10)); /// 03: continued |
+ }); /// 03: continued |
+ }); /// 03: continued |
+ |
+ 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("simple stream - take, reyield", () { /// 04: ok |
+ f(s) async* { /// 04: continued |
+ var r = 0; /// 04: continued |
+ await for(var v in s.take(5)) yield r += v; /// 04: continued |
+ } /// 04: continued |
+ return expectList(f(mkStream(10)), [0, 1, 3, 6, 10]); /// 04: continued |
+ }); /// 04: continued |
+ |
+ 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))); |
+ }); |
+ }); |
+ |
+ test("await pauses loop", () { /// 05: ok |
+ var sc; /// 05: continued |
+ var i = 0; /// 05: continued |
+ void send() { /// 05: continued |
+ if (i == 5) { /// 05: continued |
+ sc.close(); /// 05: continued |
+ } else { /// 05: continued |
+ sc.add(i++); /// 05: continued |
+ } /// 05: continued |
+ } /// 05: continued |
+ sc = new StreamController(onListen: send, onResume: send); /// 05: continued |
+ f(s) async { /// 05: continued |
+ var r = 0; /// 05: continued |
+ await for (var i in s) { /// 05: continued |
+ r += await new Future.delayed(MS * 10, () => i); /// 05: continued |
+ } /// 05: continued |
+ return r; /// 05: continued |
+ } /// 05: continued |
+ return f(sc.stream).then((v) { /// 05: continued |
+ expect(v, equals(10)); /// 05: continued |
+ }); /// 05: continued |
+ }, skip: "other impls aren't passing this test, see " |
+ "https://github.com/dart-lang/sdk/issues/22853"); /// 05: continued |
+ }); |
+} |
+ |
+// 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."); |
+ } |
+} |
+ |
+/** |
+ * Allows two asynchronous executions to synchronize. |
+ * |
+ * Calling [wait] and waiting for the returned future to complete will |
+ * wait for the other executions to call [wait] again. At that point, |
+ * the waiting execution is allowed to continue (the returned future completes), |
+ * and the more resent call to [wait] is now the waiting execution. |
+ */ |
+class Sync { |
+ Completer _completer = null; |
+ // Release whoever is currently waiting and start waiting yourself. |
+ Future wait([v]) { |
+ if (_completer != null) _completer.complete(v); |
+ _completer = new Completer(); |
+ return _completer.future; |
+ } |
+ |
+ // Release whoever is currently waiting. |
+ void release([v]) { |
+ if (_completer != null) { |
+ _completer.complete(v); |
+ _completer = null; |
+ } |
+ } |
+} |