| // 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 "dart:async"; | 
 |  | 
 | import "package:async_helper/async_minitest.dart"; | 
 |  | 
 | 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(Object 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 2", () { | 
 |       f(Object 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]); | 
 |     }); | 
 |  | 
 |     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); | 
 |           list.add(1); | 
 |           yield null; | 
 |           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), []); | 
 |       }); | 
 |     }); | 
 |  | 
 |     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, "-fisk", "done", ]); | 
 |       }); | 
 |     }); | 
 |   }); | 
 |  | 
 |   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 | 
 |     }); //                                                          //# 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 | 
 |     }); //                                                             //# 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); | 
 |  | 
 | StreamTransformer 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; | 
 |     } | 
 |   } | 
 | } |