| // 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_await_test; |
| |
| import "package:expect/expect.dart"; |
| import "package:async_helper/async_helper.dart"; |
| import "dart:async"; |
| |
| typedef dynamic DynamicToDynamic(dynamic d); |
| |
| main() { |
| asyncStart(); |
| group("basic", () { |
| test("async w/o await", () { |
| f() async { |
| return id(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("async starts synchronously", () { |
| // Calling an "async" function starts immediately. |
| var result = []; |
| f() async { |
| result.add(1); |
| return id(42); |
| } |
| |
| ; |
| var future = f(); |
| result.add(0); |
| return future.whenComplete(() { |
| expect(result, equals([1, 0])); |
| }); |
| }); |
| |
| test("async throws", () { |
| f() async { |
| throw "err"; |
| return id(42); |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("await future", () { |
| f() async { |
| var v = await new Future.value(42); |
| return v; |
| } |
| |
| ; |
| return expect42(f()); |
| }); |
| |
| test("await value", () { |
| f() async { |
| var v = await id(42); |
| return v; |
| } |
| |
| ; |
| return expect42(f()); |
| }); |
| |
| test("await null", () { |
| f() async { |
| var v = await null; |
| expect(v, equals(null)); |
| } |
| |
| ; |
| return f(); |
| }); |
| |
| test("await await", () { |
| f() async { |
| return await await new Future.value(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("await fake value future", () { |
| f() async { |
| return await new FakeValueFuture(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("await fake error future", () { |
| f() async { |
| return await new FakeErrorFuture("err"); |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("await value is delayed", () { |
| f() async { |
| bool x = false; |
| scheduleMicrotask(() { |
| x = true; |
| }); |
| var y = await true; |
| expect(x, equals(y)); |
| } |
| |
| return f(); |
| }); |
| |
| test("await throw", () { |
| f() async { |
| await (throw "err"); // Check grammar: Are parentheses necessary? |
| return id(42); |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("throw before await", () { |
| f() async { |
| var x = throw "err"; |
| await x; // Check grammar: Are parentheses necessary? |
| return id(42); |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| if (assertStatementsEnabled) { |
| test("assert before await", () { |
| f(v) async { |
| assert(v == 87); |
| return await new Future.microtask(() => 42); |
| } |
| |
| return f(42).then((_) { |
| fail("assert didn't throw"); |
| }, onError: (e, s) { |
| expect(e is AssertionError, isTrue); |
| }); |
| }); |
| |
| test("assert after await", () { |
| f(v) async { |
| var x = await new Future.microtask(() => 42); |
| assert(v == 87); |
| return x; |
| } |
| |
| return f(42).then((_) { |
| fail("assert didn't throw"); |
| }, onError: (e, s) { |
| expect(e is AssertionError, isTrue); |
| }); |
| }); |
| } |
| |
| test("async await error", () { |
| f() async { |
| await new Future.error("err"); |
| return id(42); |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("async flattens futures", () { |
| f() async { |
| return new Future.value(42); // Not awaited. |
| } |
| |
| ; |
| return f().then((v) { |
| expect(v, equals(42)); // And not a Future with value 42. |
| }); |
| }); |
| |
| test("async flattens futures, error", () { |
| f() async { |
| return new Future.error("err"); // Not awaited. |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("await for", () { |
| f(Stream<int> s) async { |
| int i = 0; |
| await for (int v in s) { |
| i += v; |
| } |
| return i; |
| } |
| |
| return f(mkStream()).then((v) { |
| expect(v, equals(45)); // 0 + 1 + ... + 9 |
| }); |
| }); |
| |
| test("await for w/ await", () { |
| f(Stream<int> s) async { |
| int i = 0; |
| await for (int v in s) { |
| i += await new Future<int>.value(v); |
| } |
| return i; |
| } |
| |
| return f(mkStream()).then((v) { |
| expect(v, equals(45)); // 0 + 1 + ... + 9 |
| }); |
| }); |
| |
| test("await for empty", () { |
| f(Stream<int> s) async { |
| int v = 0; |
| await for (int i in s) { |
| v += i; |
| } |
| return v; |
| } |
| |
| var s = (new StreamController<int>()..close()).stream; |
| return f(s).then((v) { |
| expect(v, equals(0)); |
| }); |
| }); |
| |
| if (assertStatementsEnabled) { |
| test("await for w/ await, asseert", () { |
| f(Stream<int> s) async { |
| int i = 0; |
| await for (int v in s) { |
| i += await new Future<int>.microtask(() => v); |
| assert(v < 8); |
| } |
| return i; |
| } |
| |
| return f(mkStream()).then((v) { |
| fail("assert didn't throw"); |
| }, onError: (e, s) { |
| expect(e is AssertionError, isTrue); |
| }); |
| }); |
| } |
| }); |
| |
| group("for", () { |
| test("await in for-loop", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i < 10; i++) { |
| v += await new Future<int>.value(42); |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(10 * id(42))); |
| }); |
| }); |
| |
| test("await in for-init", () { |
| f() async { |
| int v = 0; |
| for (int i = await new Future.value(42); i >= 0; i -= 10) { |
| v += 10; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(10 * 5)); |
| }); |
| }); |
| |
| test("await in for-test", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i < await new Future.value(42); i += 10) { |
| v += 10; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(10 * 5)); |
| }); |
| }); |
| |
| test("await in for-incr", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i < 100; i += await new Future<int>.value(42)) { |
| v += 10; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(10 * 3)); |
| }); |
| }); |
| |
| test("await err in for-loop", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i < 10; i++) { |
| v += await new Future<int>.error("err"); |
| } |
| return v; |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("await err in for-init", () { |
| f() async { |
| int v = 0; |
| for (int i = await new Future.error("err"); i >= 0; i -= 10) { |
| v += 10; |
| } |
| return v; |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("await err in for-test", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i < await new Future.error("err"); i += 10) { |
| v += 10; |
| } |
| return v; |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("await err in for-incr", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i < 100; i += await new Future<int>.error("err")) { |
| v += 10; |
| } |
| return v; |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("await in empty for-loop", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i > 0; i += 1) { |
| v += await new Future<int>.value(42); |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(0)); |
| }); |
| }); |
| |
| test("await in empty for-loop 2", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i > 0; i += await new Future<int>.value(1)) { |
| v += 1; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(0)); |
| }); |
| }); |
| |
| test("break before await in for-loop", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i < 10; i += 1) { |
| if (i == 2) break; |
| v += await new Future<int>.value(42); |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 2)); |
| }); |
| }); |
| |
| test("break before await in for-loop 2", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i < 10; i += await new Future<int>.value(1)) { |
| if (i == 2) break; |
| v += id(42) as int; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 2)); |
| }); |
| }); |
| |
| test("continue before await", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i < 10; i += 1) { |
| if (i == 2) continue; |
| v += await new Future<int>.value(42); |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 9)); |
| }); |
| }); |
| |
| test("continue after await", () { |
| f() async { |
| int v = 0; |
| for (int i = 0; i < 10; i += 1) { |
| var j = await new Future.value(42); |
| if (i == 2) continue; |
| v += j; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 9)); |
| }); |
| }); |
| }); |
| |
| group("while", () { |
| test("await in while-loop", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| while (i < 10) { |
| v += await new Future<int>.value(42); |
| i++; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(10 * id(42))); |
| }); |
| }); |
| |
| test("await in while-test", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| while (i < await new Future.value(42)) { |
| v += 10; |
| i += 10; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(10 * 5)); |
| }); |
| }); |
| |
| test("await err in loop", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| while (i < 10) { |
| v += await new Future<int>.error("err"); |
| i++; |
| } |
| return v; |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("await err in test", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| while (i < await new Future.error("err")) { |
| v += 10; |
| i += 10; |
| } |
| return v; |
| } |
| |
| return throwsErr(f()); |
| }); |
| |
| test("break before await", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| while (i < 10) { |
| if (i == 2) break; |
| v += await new Future<int>.value(42); |
| i += 1; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 2)); |
| }); |
| }); |
| |
| test("break after await", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| while (i < 10) { |
| v += await new Future<int>.value(42); |
| if (i == 2) break; |
| i += 1; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 3)); |
| }); |
| }); |
| |
| test("continue before await", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| while (i < 10) { |
| i += 1; |
| if (i == 2) continue; |
| v += await new Future<int>.value(42); |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 9)); |
| }); |
| }); |
| |
| test("continue after await", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| while (i < 10) { |
| i += 1; |
| int j = await new Future.value(42); |
| if (i == 2) continue; |
| v += j; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 9)); |
| }); |
| }); |
| }); |
| |
| group("do-while", () { |
| test("await in loop", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| do { |
| v += await new Future<int>.value(42); |
| i++; |
| } while (i < 10); |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(10 * id(42))); |
| }); |
| }); |
| |
| test("await in test", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| do { |
| v += 10; |
| i += 10; |
| } while (i < await new Future.value(42)); |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(10 * 5)); |
| }); |
| }); |
| |
| test("await err in loop", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| do { |
| v += await new Future<int>.error("err"); |
| i++; |
| } while (i < 10); |
| return v; |
| } |
| |
| return f().then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| }); |
| }); |
| |
| test("await err in test", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| do { |
| v += 10; |
| i += 10; |
| } while (i < await new Future.error("err")); |
| return v; |
| } |
| |
| return f().then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| }); |
| }); |
| |
| test("break before await", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| do { |
| if (i == 2) break; |
| v += await new Future<int>.value(42); |
| i += 1; |
| } while (i < 10); |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 2)); |
| }); |
| }); |
| |
| test("break after await", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| do { |
| v += await new Future<int>.value(42); |
| if (i == 2) break; |
| i += 1; |
| } while (i < 10); |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 3)); |
| }); |
| }); |
| |
| test("continue before await", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| do { |
| i += 1; |
| if (i == 2) continue; |
| v += await new Future<int>.value(42); |
| } while (i < 10); |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 9)); |
| }); |
| }); |
| |
| test("continue after await", () { |
| f() async { |
| int v = 0; |
| int i = 0; |
| do { |
| i += 1; |
| int j = await new Future.value(42); |
| if (i == 2) continue; |
| v += j; |
| } while (i < 10); |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42 * 9)); |
| }); |
| }); |
| }); |
| |
| group("for-in", () { |
| test("await in for-in", () { |
| f() async { |
| var v = 0; |
| for (var fut in [1, 2, 3].map((v) => new Future.value(v))) { |
| v += await fut; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(6)); |
| }); |
| }); |
| |
| test("await in for-in iterable", () { |
| f() async { |
| var v = 0; |
| for (var i in await new Future.value([1, 2, 3])) { |
| v += i; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(6)); |
| }); |
| }); |
| |
| test("await err in for-in", () { |
| f() async { |
| var v = 0; |
| for (var fut in [1, 2, 3].map((v) => (v != 1) |
| ? new Future<int>.value(v) |
| : new Future<int>.error("err"))) { |
| v += await fut; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| }); |
| }); |
| |
| test("await err in for-in iterable", () { |
| f() async { |
| var v = 0; |
| for (var i in await new Future<Iterable<int>>.error("err")) { |
| v += i; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| }); |
| }); |
| |
| test("break before await in for-in", () { |
| f() async { |
| var v = 0; |
| for (var fut in [1, 2, 3].map((v) => new Future.value(v))) { |
| if (v == 3) break; |
| v += await fut; |
| } |
| return v; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(3)); |
| }); |
| }); |
| }); |
| |
| group("try-catch", () { |
| test("try-no-catch", () { |
| f() async { |
| try { |
| return await id(42); |
| } catch (e) { |
| return 37; |
| } |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("await in body", () { |
| f() async { |
| try { |
| await new Future.error(42); |
| } on dynamic catch (e) { |
| return e; |
| } |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("throw before await in body", () { |
| int i = id(0); |
| f() async { |
| try { |
| if (i >= 0) throw id(42); |
| return await new Future.value(10); |
| } catch (e) { |
| return e; |
| } |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("try-catch await in catch", () { |
| f() async { |
| try { |
| throw id(42); |
| } catch (e) { |
| return await new Future.value(e); |
| } |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("try-catch await error in catch", () { |
| f() async { |
| try { |
| throw id(42); |
| } catch (e) { |
| await new Future.error("err"); |
| } |
| } |
| |
| return f().then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| }); |
| }); |
| |
| test("try-catch-rethrow", () { |
| f() async { |
| try { |
| await new Future.error("err"); |
| } catch (e) { |
| if (e == id(42)) return; |
| rethrow; |
| } |
| } |
| |
| return f().then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| }); |
| }); |
| }); |
| |
| group("try-finally", () { |
| test("await in body", () { |
| f() async { |
| try { |
| return await new Future.value(42); |
| } finally { |
| // Don't do anything. |
| } |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("await in finally", () { |
| var x = 0; |
| f() async { |
| try { |
| return id(42); |
| } finally { |
| x = await new Future.value(37); |
| } |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42)); |
| expect(x, equals(37)); |
| }); |
| }); |
| |
| test("await err in body", () { |
| f() async { |
| try { |
| return await new Future.error("err"); |
| } finally { |
| // Don't do anything. |
| } |
| } |
| |
| return f().then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| }); |
| }); |
| |
| test("await err in finally", () { |
| f() async { |
| try { |
| return id(42); |
| } finally { |
| await new Future.error("err"); |
| } |
| } |
| |
| return f().then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| }); |
| }); |
| |
| test("await err in both", () { |
| f() async { |
| try { |
| await new Future.error("not err"); |
| } finally { |
| await new Future.error("err"); |
| } |
| } |
| |
| return f().then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| }); |
| }); |
| |
| test("await err in body, override in finally", () { |
| f() async { |
| try { |
| return await new Future.error("err"); |
| } finally { |
| return id(42); |
| } |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("await in body, override in finally", () { |
| f() async { |
| label: |
| try { |
| return await new Future.value(37); |
| } finally { |
| break label; |
| } |
| return id(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("await, override in finally", () { |
| var x = 0; |
| f() async { |
| label: |
| try { |
| return 87; |
| } finally { |
| x = await new Future.value(37); |
| break label; |
| } |
| return id(42); |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42)); |
| expect(x, equals(37)); |
| }); |
| }); |
| |
| test("throw in body, await, override in finally 3", () { |
| var x = 0; |
| f() async { |
| label: |
| try { |
| throw "err"; |
| } finally { |
| x = await new Future.value(37); |
| break label; |
| } |
| return id(42); |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42)); |
| expect(x, equals(37)); |
| }); |
| }); |
| |
| test("await err in body, override in finally 2", () { |
| f() async { |
| label: |
| try { |
| return await new Future.error("err"); |
| } finally { |
| break label; |
| } |
| return id(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("await in body, no-exit in finally", () { |
| f() async { |
| for (int i = 0; i < 10; i++) { |
| try { |
| return await i; |
| } finally { |
| continue; |
| } |
| } |
| return id(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("no-exit after await in finally", () { |
| f() async { |
| int i = 0; |
| for (; i < 10; i++) { |
| try { |
| break; |
| } finally { |
| await new Future.value(42); |
| continue; |
| } |
| } |
| return id(i); |
| } |
| |
| return f().then((v) { |
| expect(v, equals(10)); |
| }); |
| }); |
| |
| test("exit after continue, await in finally", () { |
| f() async { |
| int i = 0; |
| for (; i < 10; i++) { |
| try { |
| continue; |
| } finally { |
| await new Future.value(42); |
| break; |
| } |
| } |
| return id(i); |
| } |
| |
| return f().then((v) { |
| expect(v, equals(0)); |
| }); |
| }); |
| |
| test("no-exit before await in finally 2", () { |
| f() async { |
| for (int i = 0; i < 10; i++) { |
| try { |
| return i; |
| } finally { |
| if (i >= 0) continue; |
| await new Future.value(42); |
| } |
| } |
| return id(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("no-exit after await in finally", () { |
| f() async { |
| for (int i = 0; i < 10; i++) { |
| try { |
| return i; |
| } finally { |
| await new Future.value(42); |
| continue; |
| } |
| } |
| return id(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("nested finallies", () { |
| var x = 0; |
| f() async { |
| try { |
| try { |
| return 42; |
| } finally { |
| x = await new Future.value(37); |
| } |
| } finally { |
| x += await new Future<int>.value(37); |
| } |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42)); |
| expect(x, equals(74)); |
| }); |
| }); |
| |
| test("nested finallies 2", () { |
| var x = 0; |
| f() async { |
| label: |
| try { |
| try { |
| break label; |
| } finally { |
| x = await new Future.value(37); |
| } |
| } finally { |
| x += await new Future<int>.value(37); |
| } |
| return 42; |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42)); |
| expect(x, equals(74)); |
| }); |
| }); |
| |
| test("nested finallies 3", () { |
| var x = 0; |
| f() async { |
| label: |
| try { |
| try { |
| break label; |
| } finally { |
| return await new Future.value(42); |
| } |
| } finally { |
| break label; |
| } |
| return 42; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("nested finallies, throw", () { |
| var x = 0; |
| f() async { |
| try { |
| try { |
| throw "err"; |
| } finally { |
| x = await new Future.value(37); |
| } |
| } finally { |
| x += await new Future<int>.value(37); |
| } |
| } |
| |
| return f().then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| expect(x, equals(2 * 37)); |
| }); |
| }); |
| }); |
| |
| group("try-catch-finally", () { |
| test("await in body", () { |
| f() async { |
| try { |
| return await new Future.value(42); |
| } catch (e) { |
| throw "!"; |
| } finally { |
| if (id(42) == id(10)) return 10; |
| } |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("await in catch, not hit", () { |
| f() async { |
| try { |
| return id(42); |
| } catch (e) { |
| await new Future.error("err"); |
| } finally { |
| if (id(42) == id(10)) return 10; |
| } |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("await in catch, hit", () { |
| f() async { |
| try { |
| return throw id(42); |
| } catch (e) { |
| return await new Future.value(e); |
| } finally { |
| if (id(42) == id(10)) return 10; |
| } |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("await in finally", () { |
| var x = 0; |
| f() async { |
| try { |
| return id(42); |
| } catch (e) { |
| throw "!"; |
| } finally { |
| x = await new Future.value(37); |
| if (id(42) == id(10)) return 10; |
| } |
| } |
| |
| return f().then((v) { |
| expect(v, equals(42)); |
| expect(x, equals(37)); |
| }); |
| }); |
| }); |
| |
| group("switch", () { |
| test("await in expression", () { |
| f(v) async { |
| switch (await new Future.value(v)) { |
| case 1: |
| return 1; |
| case 2: |
| return 42; |
| default: |
| return 3; |
| } |
| return null; |
| } |
| |
| return expect42(f(2)); |
| }); |
| |
| test("await err in expression", () { |
| f(v) async { |
| switch (await new Future.error("err")) { |
| case 1: |
| return 1; |
| case 2: |
| return 42; |
| default: |
| return 3; |
| } |
| return null; |
| } |
| |
| return throwsErr(f(2)); |
| }); |
| |
| test("await in case", () { |
| f(v) async { |
| switch (v) { |
| case 1: |
| return 1; |
| case 2: |
| return await new Future.value(42); |
| default: |
| return 3; |
| } |
| return null; |
| } |
| |
| return expect42(f(2)); |
| }); |
| |
| test("await err in case", () { |
| f(v) async { |
| switch (v) { |
| case 1: |
| return 1; |
| case 2: |
| return await new Future.error("err"); |
| default: |
| return 3; |
| } |
| return null; |
| } |
| |
| return throwsErr(f(2)); |
| }); |
| // TODO(jmesserly): restore this when we fix |
| // https://github.com/dart-lang/dev_compiler/issues/263 |
| /*test("continue before await in case", () { |
| f(v) async { |
| switch (v) { |
| label: |
| case 1: return 42; |
| case 2: |
| if (v <= 2) continue label; |
| return await new Future.value(10); |
| default: return 3; |
| } |
| return null; |
| } |
| return expect42(f(2)); |
| }); |
| |
| test("continue after await in case", () { |
| f(v) async { |
| switch (v) { |
| label: |
| case 1: return 42; |
| case 2: |
| await new Future.value(10); |
| continue label; |
| default: return 3; |
| } |
| return null; |
| } |
| return expect42(f(2)); |
| });*/ |
| }); |
| |
| group("if", () { |
| test("await in test", () { |
| f(bool v) async { |
| if (await new Future.value(v)) { |
| return 42; |
| } else { |
| return 37; |
| } |
| } |
| |
| return expect42(f(true)); |
| }); |
| |
| test("await err in test", () { |
| f(bool v) async { |
| if (await new Future.error("err")) { |
| return 42; |
| } else { |
| return 37; |
| } |
| } |
| |
| return throwsErr(f(true)); |
| }); |
| |
| test("await in then", () { |
| f(bool v) async { |
| if (v) { |
| return await new Future.value(42); |
| } |
| return 37; |
| } |
| |
| return expect42(f(true)); |
| }); |
| |
| test("await err in then", () { |
| f(bool v) async { |
| if (v) { |
| return await new Future.error("err"); |
| } |
| return 37; |
| } |
| |
| return throwsErr(f(true)); |
| }); |
| |
| test("await in then with else", () { |
| f(bool v) async { |
| if (v) { |
| return await new Future.value(42); |
| } else { |
| return 87; |
| } |
| return 37; |
| } |
| |
| return expect42(f(true)); |
| }); |
| |
| test("await err in then with else", () { |
| f(bool v) async { |
| if (v) { |
| return await new Future.error("err"); |
| } else { |
| return 87; |
| } |
| return 37; |
| } |
| |
| return throwsErr(f(true)); |
| }); |
| |
| test("await in else", () { |
| f(bool v) async { |
| if (v) { |
| return 37; |
| } else { |
| return await new Future.value(42); |
| } |
| return 87; |
| } |
| |
| return expect42(f(false)); |
| }); |
| |
| test("await err in else", () { |
| f(bool v) async { |
| if (v) { |
| return 37; |
| } else { |
| return await new Future.error("err"); |
| } |
| return 87; |
| } |
| |
| return throwsErr(f(false)); |
| }); |
| |
| test("await in else-if test", () { |
| f(bool v) async { |
| if (v) { |
| return 37; |
| } else if (!await new Future.value(v)) { |
| return 42; |
| } else { |
| return 37; |
| } |
| return 87; |
| } |
| |
| return expect42(f(false)); |
| }); |
| |
| test("await in else-if then", () { |
| f(bool v) async { |
| if (v) { |
| return 37; |
| } else if (!v) { |
| return await new Future.value(42); |
| } else { |
| return 37; |
| } |
| return 87; |
| } |
| |
| return expect42(f(false)); |
| }); |
| }); |
| |
| group("conditional operator", () { |
| test("await in test", () { |
| f(bool v) async { |
| return (await new Future.value(v)) ? 42 : 37; |
| } |
| |
| return expect42(f(true)); |
| }); |
| |
| test("await err in test", () { |
| f(bool v) async { |
| return (await new Future.error("err")) ? 42 : 37; |
| } |
| |
| return throwsErr(f(true)); |
| }); |
| |
| test("await in then", () { |
| f(bool v) async { |
| return v ? (await new Future.value(42)) : 37; |
| } |
| |
| return expect42(f(true)); |
| }); |
| |
| test("await err in then", () { |
| f(bool v) async { |
| return v ? (await new Future.error("err")) : 37; |
| } |
| |
| return throwsErr(f(true)); |
| }); |
| |
| test("await in else", () { |
| f(bool v) async { |
| return v ? 37 : (await new Future.value(42)); |
| } |
| |
| return expect42(f(false)); |
| }); |
| |
| test("await err in else", () { |
| f(bool v) async { |
| return v ? 37 : (await new Future.error("err")); |
| } |
| |
| return throwsErr(f(false)); |
| }); |
| }); |
| |
| group("async declarations", () { |
| var f42 = new Future.value(42); |
| |
| // Top-level declarations or local declarations in top-level functions. |
| test("topMethod", () { |
| return expect42(topMethod(f42)); |
| }); |
| |
| test("topArrowMethod", () { |
| return expect42(topArrowMethod(f42)); |
| }); |
| |
| test("topGetter", () { |
| return expect42(topGetter); |
| }); |
| |
| test("topArrowGetter", () { |
| return expect42(topArrowGetter); |
| }); |
| |
| test("topLocal", () { |
| return expect42(topLocal(f42)); |
| }); |
| |
| test("topArrowLocal", () { |
| return expect42(topArrowLocal(f42)); |
| }); |
| |
| test("topExpression", () { |
| return expect42(topExpression(f42)); |
| }); |
| |
| test("topArrowExpression", () { |
| return expect42(topArrowExpression(f42)); |
| }); |
| |
| test("topVarExpression", () { |
| return expect42(topVarExpression(f42)); |
| }); |
| |
| test("topVarArrowExpression", () { |
| return expect42(topVarArrowExpression(f42)); |
| }); |
| |
| // Static declarations or local declarations in static functions. |
| test("staticMethod", () { |
| return expect42(Async.staticMethod(f42)); |
| }); |
| |
| test("staticArrowMethod", () { |
| return expect42(Async.staticArrowMethod(f42)); |
| }); |
| |
| test("staticGetter", () { |
| return expect42(Async.staticGetter); |
| }); |
| |
| test("staticArrowGetter", () { |
| return expect42(Async.staticArrowGetter); |
| }); |
| |
| test("staticLocal", () { |
| return expect42(Async.staticLocal(f42)); |
| }); |
| |
| test("staticArrowLocal", () { |
| return expect42(Async.staticArrowLocal(f42)); |
| }); |
| |
| test("staticExpression", () { |
| return expect42(Async.staticExpression(f42)); |
| }); |
| |
| test("staticArrowExpression", () { |
| return expect42(Async.staticArrowExpression(f42)); |
| }); |
| |
| test("staticVarExpression", () { |
| return expect42(Async.staticVarExpression(f42)); |
| }); |
| |
| test("staticVarArrowExpression", () { |
| return expect42(Async.staticVarArrowExpression(f42)); |
| }); |
| |
| // Instance declarations or local declarations in instance functions. |
| var async = new Async(); |
| |
| test("instanceMethod", () { |
| return expect42(async.instanceMethod(f42)); |
| }); |
| |
| test("instanceArrowMethod", () { |
| return expect42(async.instanceArrowMethod(f42)); |
| }); |
| |
| test("instanceGetter", () { |
| return expect42(async.instanceGetter); |
| }); |
| |
| test("instanceArrowGetter", () { |
| return expect42(async.instanceArrowGetter); |
| }); |
| |
| test("instanceLocal", () { |
| return expect42(async.instanceLocal(f42)); |
| }); |
| |
| test("instanceArrowLocal", () { |
| return expect42(async.instanceArrowLocal(f42)); |
| }); |
| |
| test("instanceExpression", () { |
| return expect42(async.instanceExpression(f42)); |
| }); |
| |
| test("instanceArrowExpression", () { |
| return expect42(async.instanceArrowExpression(f42)); |
| }); |
| |
| test("instanceVarExpression", () { |
| return expect42(async.instanceVarExpression(f42)); |
| }); |
| |
| test("instanceVarArrowExpression", () { |
| return expect42(async.instanceVarArrowExpression(f42)); |
| }); |
| |
| // Local functions in constructor initializer list. |
| test("initializerExpression", () { |
| var async = new Async.initializer(f42); |
| return expect42(async.initValue); |
| }); |
| |
| test("initializerArrowExpression", () { |
| var async = new Async.initializerArrow(f42); |
| return expect42(async.initValue); |
| }); |
| |
| test("async in async", () { |
| return expect42(asyncInAsync(f42)); |
| }); |
| |
| test("sync in async", () { |
| return expect42(syncInAsync(f42)); |
| }); |
| |
| test("async in sync", () { |
| return expect42(asyncInSync(f42)); |
| }); |
| |
| test("Identical and equals", () { |
| expect(async.instanceMethod, equals(async.instanceMethod)); |
| expect(Async.staticMethod, same(Async.staticMethod)); |
| expect(topMethod, same(topMethod)); |
| }); |
| }); |
| |
| group("await expression", () { |
| const c42 = 42; |
| final v42 = 42; |
| |
| test("local variable", () { |
| var l42 = 42; |
| f() async { |
| return await l42; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("parameter", () { |
| f(p) async { |
| return await p; |
| } |
| |
| return expect42(f(42)); |
| }); |
| |
| test("final local variable", () { |
| f() async { |
| return await v42; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("const local variable", () { |
| f() async { |
| return await c42; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("unary prefix operator", () { |
| f() async { |
| return -await -42; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("suffix operator", () { |
| f() async { |
| var v = [42]; |
| return await v[0]; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("unary postfix operator", () { |
| f() async { |
| var x = 42; |
| return await x++; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("suffix operator + increment", () { |
| f() async { |
| var v = [42]; |
| return await v[0]++; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("suffix operator + increment 2", () { |
| f() async { |
| var v = [42]; |
| return await v[await 0]++; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("unary pre-increment operator", () { |
| f() async { |
| var x = 41; |
| return await ++x; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("suffix operator + pre-increment", () { |
| f() async { |
| var v = [41]; |
| return await ++v[0]; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("assignment operator", () { |
| f() async { |
| var x = 37; |
| return await (x = 42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("assignment-op operator", () { |
| f() async { |
| var x = 37; |
| return await (x += 5); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("binary operator", () { |
| f() async { |
| return await (10 + 11) + await (10 + 11); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("ternary operator", () { |
| f(v) async { |
| return await ((v == 10) ? new Future.value(42) : 37); |
| } |
| |
| return expect42(f(10)); |
| }); |
| |
| test("top-level function call", () { |
| f() async { |
| return await topMethod(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("static function call", () { |
| f() async { |
| return await Async.staticMethod(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("instance function call", () { |
| f() async { |
| var a = new Async(); |
| return await a.instanceMethod(42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("top-level function call w/ await", () { |
| f() async { |
| return await topMethod(await 42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("static function call w/ await", () { |
| f() async { |
| return await Async.staticMethod(await 42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("instance function call w/ await", () { |
| f() async { |
| var a = new Async(); |
| return await a.instanceMethod(await 42); |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("top-level getter call", () { |
| f() async { |
| return await topGetter; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("static getter call", () { |
| f() async { |
| return await Async.staticGetter; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| test("top-level getter call", () { |
| f() async { |
| var a = new Async(); |
| return await a.instanceGetter; |
| } |
| |
| return expect42(f()); |
| }); |
| |
| if (!assertStatementsEnabled) return; |
| |
| test("inside assert, true", () { // //# 03: ok |
| f() async { // //# 03: continued |
| assert(await new Future.microtask(() => true)); // //# 03: continued |
| return 42; // //# 03: continued |
| } // //# 03: continued |
| return expect42(f()); // //# 03: continued |
| }); // //# 03: continued |
| |
| test("inside assert, false", () { // //# 03: continued |
| f() async { // //# 03: continued |
| assert(await new Future.microtask(() => false)); // //# 03: continued |
| return 42; // //# 03: continued |
| } // //# 03: continued |
| return f().then((_) { // //# 03: continued |
| fail("assert didn't throw"); // //# 03: continued |
| }, onError: (e, s) { // //# 03: continued |
| expect(e is AssertionError, isTrue); // //# 03: continued |
| }); // //# 03: continued |
| }); // //# 03: continued |
| |
| test("inside assert, function -> false", () { // //# 03: continued |
| f() async { // //# 03: continued |
| assert(await new Future.microtask(() => false)); // //# 03: continued |
| return 42; // //# 03: continued |
| } // //# 03: continued |
| return f().then((_) { // //# 03: continued |
| fail("assert didn't throw"); // //# 03: continued |
| }, onError: (e, s) { // //# 03: continued |
| expect(e is AssertionError, isTrue); // //# 03: continued |
| }); // //# 03: continued |
| }); // //# 03: continued |
| }); |
| |
| group("syntax", () { |
| test("async as variable", () { |
| // Valid identifiers outside of async function. |
| var async = 42; |
| expect(async, equals(42)); |
| }); |
| |
| test("await as variable", () { // //# 02: ok |
| // Valid identifiers outside of async function. // //# 02: continued |
| var await = 42; // //# 02: continued |
| expect(await, equals(42)); // //# 02: continued |
| }); // //# 02: continued |
| |
| test("yield as variable", () { |
| // Valid identifiers outside of async function. |
| var yield = 42; |
| expect(yield, equals(42)); |
| }); |
| }); |
| asyncEnd(); |
| } |
| |
| // Mock test framework sufficient to run tests. |
| |
| String _currentName = ""; |
| |
| test(name, action()) { |
| var oldName = _currentName; |
| _currentName = [oldName, name].join(" "); |
| runZoned(() { |
| asyncTest(() => new Future.sync(action)); |
| }, zoneValues: {#testName: _currentName}); |
| _currentName = oldName; |
| } |
| |
| group(name, entries()) { |
| var oldName = _currentName; |
| _currentName = [oldName, name].join(" "); |
| entries(); |
| _currentName = oldName; |
| } |
| |
| expect(value, expectation) { |
| var name = Zone.current[#testName]; |
| if (expectation is bool) { |
| // Just for better error message. |
| (expectation ? Expect.isTrue : Expect.isFalse)(value, name); |
| return; |
| } |
| if (expectation is List) { |
| Expect.listEquals(expectation, value, name); |
| return; |
| } |
| if (expectation is Function(Object, String)) { |
| expectation(value, name); |
| return; |
| } |
| Expect.equals(expectation, value, name); |
| } |
| |
| equals(x) => x; |
| final isTrue = true; |
| same(v) => (Object o, String name) => Expect.identical(v, o, name); |
| fail(message) { |
| var name = Zone.current[#testName]; |
| Expect.fail("$name: $message"); |
| } |
| |
| // End mock. |
| |
| // Attempt to obfuscates value to avoid too much constant folding. |
| id(v) { |
| try { |
| if (v != null) throw v; |
| } catch (e) { |
| return e; |
| } |
| return null; |
| } |
| |
| // Create a stream for testing "async for-in". |
| Stream<int> mkStream() { |
| late StreamController<int> c; |
| int i = 0; |
| next() { |
| c.add(i++); |
| if (i == 10) { |
| c.close(); |
| } else { |
| scheduleMicrotask(next); |
| } |
| } |
| |
| c = new StreamController(onListen: () { |
| scheduleMicrotask(next); |
| }); |
| return c.stream; |
| } |
| |
| // Check that future contains the error "err". |
| Future throwsErr(Future future) { |
| return future.then((v) { |
| fail("didn't throw"); |
| }, onError: (e) { |
| expect(e, equals("err")); |
| }); |
| } |
| |
| // Check that future contains the value 42. |
| Future expect42(Future future) { |
| return future.then((v) { |
| expect(v, equals(42)); |
| }); |
| } |
| |
| // Various async declarations. |
| |
| Future topMethod(f) async { |
| return await f; |
| } |
| |
| Future topArrowMethod(f) async => await f; |
| |
| Future get topGetter async { |
| return await new Future.value(42); |
| } |
| |
| Future get topArrowGetter async => await new Future.value(42); |
| |
| Future topLocal(f) { |
| local() async { |
| return await f; |
| } |
| |
| return local(); |
| } |
| |
| Future topArrowLocal(f) { |
| local() async => await f; |
| return local(); |
| } |
| |
| Future topExpression(f) { |
| return () async { |
| return await f; |
| }(); |
| } |
| |
| Future topArrowExpression(f) { |
| return (() async => await f)(); |
| } |
| |
| DynamicToDynamic topVarExpression = (f) async { |
| return await f; |
| }; |
| |
| var topVarArrowExpression = (f) async => await f; |
| |
| class Async { |
| var initValue; |
| Async(); |
| |
| Async.initializer(f) |
| : initValue = (() async { |
| return await f; |
| }()); |
| |
| Async.initializerArrow(f) : initValue = ((() async => await f)()); |
| |
| /* static */ |
| static Future staticMethod(f) async { |
| return await f; |
| } |
| |
| static Future staticArrowMethod(f) async => await f; |
| |
| static Future get staticGetter async { |
| return await new Future.value(42); |
| } |
| |
| static Future get staticArrowGetter async => await new Future.value(42); |
| |
| static Future staticLocal(f) { |
| local() async { |
| return await f; |
| } |
| |
| return local(); |
| } |
| |
| static Future staticArrowLocal(f) { |
| local() async => await f; |
| return local(); |
| } |
| |
| static Future staticExpression(f) { |
| return () async { |
| return await f; |
| }(); |
| } |
| |
| static Future staticArrowExpression(f) { |
| return (() async => await f)(); |
| } |
| |
| static DynamicToDynamic staticVarExpression = (f) async { |
| return await f; |
| }; |
| |
| static var staticVarArrowExpression = (f) async => await f; |
| |
| /* instance */ |
| Future instanceMethod(f) async { |
| return await f; |
| } |
| |
| Future instanceArrowMethod(f) async => await f; |
| |
| Future get instanceGetter async { |
| return await new Future.value(42); |
| } |
| |
| Future get instanceArrowGetter async => await new Future.value(42); |
| |
| Future instanceLocal(f) { |
| local() async { |
| return await f; |
| } |
| |
| return local(); |
| } |
| |
| Future instanceArrowLocal(f) { |
| local() async => await f; |
| return local(); |
| } |
| |
| Future instanceExpression(f) { |
| return () async { |
| return await f; |
| }(); |
| } |
| |
| Future instanceArrowExpression(f) { |
| return (() async => await f)(); |
| } |
| |
| DynamicToDynamic instanceVarExpression = (f) async { |
| return await f; |
| }; |
| |
| var instanceVarArrowExpression = (f) async => await f; |
| } |
| |
| Future asyncInAsync(f) async { |
| inner(f) async { |
| return await f; |
| } |
| |
| return await inner(f); |
| } |
| |
| Future asyncInSync(f) { |
| inner(f) async { |
| return await f; |
| } |
| |
| return inner(f); |
| } |
| |
| Future syncInAsync(f) async { |
| inner(f) { |
| return f; |
| } |
| |
| return await inner(f); |
| } |
| |
| /** |
| * A non-standard implementation of Future with a value. |
| */ |
| class FakeValueFuture implements Future { |
| final _value; |
| FakeValueFuture(this._value); |
| Future<S> then<S>(callback(value), {Function? onError}) { |
| return new Future<S>.microtask(() => callback(_value)); |
| } |
| |
| Future whenComplete(callback()) { |
| return new Future.microtask(() { |
| callback(); |
| }); |
| } |
| |
| Future catchError(Function onError, {bool test(Object error)?}) => this; |
| Stream asStream() => (new StreamController() |
| ..add(_value) |
| ..close()) |
| .stream; |
| Future timeout(Duration duration, {onTimeout()?}) => this; |
| } |
| |
| typedef OnErrorCallback2 = dynamic Function(Object, StackTrace); |
| typedef OnErrorCallback1 = dynamic Function(Object); |
| |
| /** |
| * A non-standard implementation of Future with an error. |
| */ |
| class FakeErrorFuture implements Future { |
| final _error; |
| FakeErrorFuture(this._error); |
| Future<S> then<S>(callback(value), {Function? onError}) { |
| if (onError != null) { |
| if (onError is OnErrorCallback2) { |
| return new Future<S>.microtask(() => onError(_error, StackTrace.empty)); |
| } else if (onError is OnErrorCallback1) { |
| return new Future<S>.microtask(() => onError(_error)); |
| } else { |
| throw new ArgumentError.value( |
| onError, |
| "onError", |
| "Error handler must accept one Object or one Object and a StackTrace" |
| " as arguments, and return a valid result"); |
| } |
| } |
| return new Future<S>.error(_error); |
| } |
| |
| Future whenComplete(callback()) { |
| return new Future.microtask(() { |
| callback(); |
| }).then((_) => this); |
| } |
| |
| Future catchError(Function onError, {bool test(Object error)?}) { |
| return new Future.microtask(() { |
| if (test != null && !test(_error)) return this; |
| if (onError is OnErrorCallback2) { |
| return onError(_error, StackTrace.empty); |
| } else if (onError is OnErrorCallback1) { |
| return onError(_error); |
| } else { |
| throw new ArgumentError.value( |
| onError, |
| "onError", |
| "Error handler must accept one Object or one Object and a StackTrace" |
| " as arguments, and return a valid result"); |
| } |
| }); |
| } |
| |
| Stream asStream() => (new StreamController() |
| ..addError(_error) |
| ..close()) |
| .stream; |
| Future timeout(Duration duration, {onTimeout()?}) => this; |
| } |