// Copyright (c) 2012, 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 future_test; import 'package:async_helper/async_helper.dart'; import "package:expect/expect.dart"; import 'dart:async'; const Duration MS = const Duration(milliseconds: 1); void testValue() { final future = new Future.value("42"); asyncStart(); future.then((x) { Expect.equals("42", x); asyncEnd(); }); } void testSync() { compare(func) { // Compare the results of the following two futures. Future f1 = new Future.sync(func); Future f2 = new Future.value().then((_) => func()); f2.catchError((_) {}); // I'll get the error later. f1.then((v1) { f2.then((v2) { Expect.equals(v1, v2); }); }, onError: (e1) { f2.then((_) { Expect.fail("Expected error"); }, onError: (e2) { Expect.equals(e1, e2); }); }); } Future val = new Future.value(42); Future err1 = new Future.error("Error")..catchError((_) {}); try { throw new List(0); } catch (e, st) { Future err2 = new Future.error(e, st)..catchError((_) {}); } compare(() => 42); compare(() => val); compare(() { throw "Flif"; }); compare(() => err1); bool hasExecuted = false; compare(() { hasExecuted = true; return 499; }); Expect.isTrue(hasExecuted); } void testNeverComplete() { final completer = new Completer(); final future = completer.future; future.then((v) => Expect.fail("Value not expected")); future.catchError((e) => Expect.fail("Value not expected")); } void testComplete() { final completer = new Completer(); final future = completer.future; Expect.isFalse(completer.isCompleted); completer.complete(3); Expect.isTrue(completer.isCompleted); future.then((v) => Expect.equals(3, v)); } // Tests for [then] void testCompleteWithSuccessHandlerBeforeComplete() { final completer = new Completer(); final future = completer.future; int after; asyncStart(); future.then((int v) { after = v; }).then((_) { Expect.equals(3, after); asyncEnd(); }); completer.complete(3); Expect.isNull(after); } void testCompleteWithSuccessHandlerAfterComplete() { final completer = new Completer(); final future = completer.future; int after; completer.complete(3); Expect.isNull(after); asyncStart(); future.then((int v) { after = v; }).then((_) { Expect.equals(3, after); asyncEnd(); }); } void testCompleteManySuccessHandlers() { final completer = new Completer(); final future = completer.future; int before; int after1; int after2; var futures = >[]; futures.add(future.then((int v) { before = v; })); completer.complete(3); futures.add(future.then((int v) { after1 = v; })); futures.add(future.then((int v) { after2 = v; })); asyncStart(); Future.wait(futures).then((_) { Expect.equals(3, before); Expect.equals(3, after1); Expect.equals(3, after2); asyncEnd(); }); // Regression test for fix to issue: // https://github.com/dart-lang/sdk/issues/43445 asyncStart(); Future.wait(>[]).then((list) { Expect.equals(0, list.length); Expect.type>(list); Expect.notType>(list); Expect.notType>(list); asyncEnd(); }); } // Tests for [catchError] void testException() { final completer = new Completer(); final future = completer.future; final ex = new Exception(); asyncStart(); future.then((v) { throw "Value not expected"; }).catchError((error) { Expect.equals(error, ex); asyncEnd(); }, test: (e) => e == ex); completer.completeError(ex); } void testExceptionHandler() { final completer = new Completer(); final future = completer.future; final ex = new Exception(); var ex2; var done = future.catchError((error) { ex2 = error; }); Expect.isFalse(completer.isCompleted); completer.completeError(ex); Expect.isTrue(completer.isCompleted); asyncStart(); done.then((_) { Expect.equals(ex, ex2); asyncEnd(); }); } void testExceptionHandlerReturnsTrue() { final completer = new Completer(); final future = completer.future; final ex = new Exception(); bool reached = false; future.catchError((e) {}); future.catchError((e) { reached = true; }, test: (e) => false).catchError((e) {}); Expect.isFalse(completer.isCompleted); completer.completeError(ex); Expect.isTrue(completer.isCompleted); Expect.isFalse(reached); } void testExceptionHandlerReturnsTrue2() { final completer = new Completer(); final future = completer.future; final ex = new Exception(); bool reached = false; var done = future.catchError((e) {}, test: (e) => false).catchError((e) { reached = true; }); completer.completeError(ex); asyncStart(); done.then((_) { Expect.isTrue(reached); asyncEnd(); }); } void testExceptionHandlerReturnsFalse() { final completer = new Completer(); final future = completer.future; final ex = new Exception(); bool reached = false; future.catchError((e) {}); future.catchError((e) { reached = true; }, test: (e) => false).catchError((e) {}); completer.completeError(ex); Expect.isFalse(reached); } void testFutureAsStreamCompleteAfter() { var completer = new Completer(); bool gotValue = false; asyncStart(); completer.future.asStream().listen((data) { Expect.isFalse(gotValue); gotValue = true; Expect.equals("value", data); }, onDone: () { Expect.isTrue(gotValue); asyncEnd(); }); completer.complete("value"); } void testFutureAsStreamCompleteBefore() { var completer = new Completer(); bool gotValue = false; asyncStart(); completer.complete("value"); completer.future.asStream().listen((data) { Expect.isFalse(gotValue); gotValue = true; Expect.equals("value", data); }, onDone: () { Expect.isTrue(gotValue); asyncEnd(); }); } void testFutureAsStreamCompleteImmediate() { bool gotValue = false; asyncStart(); new Future.value("value").asStream().listen((data) { Expect.isFalse(gotValue); gotValue = true; Expect.equals("value", data); }, onDone: () { Expect.isTrue(gotValue); asyncEnd(); }); } void testFutureAsStreamCompleteErrorAfter() { var completer = new Completer(); bool gotError = false; asyncStart(); completer.future.asStream().listen((data) { Expect.fail("Unexpected data"); }, onError: (error) { Expect.isFalse(gotError); gotError = true; Expect.equals("error", error); }, onDone: () { Expect.isTrue(gotError); asyncEnd(); }); completer.completeError("error"); } void testFutureAsStreamWrapper() { var completer = new Completer(); bool gotValue = false; asyncStart(); completer.complete("value"); completer.future .catchError((_) { throw "not possible"; }) // Returns a future wrapper. .asStream() .listen((data) { Expect.isFalse(gotValue); gotValue = true; Expect.equals("value", data); }, onDone: () { Expect.isTrue(gotValue); asyncEnd(); }); } void testFutureWhenCompleteValue() { asyncStart(); int counter = 2; countDown() { if (--counter == 0) asyncEnd(); } var completer = new Completer(); Future future = completer.future; Future later = future.whenComplete(countDown); later.then((v) { Expect.equals(42, v); countDown(); }); completer.complete(42); } void testFutureWhenCompleteError() { asyncStart(); int counter = 2; countDown() { if (--counter == 0) asyncEnd(); } var completer = new Completer(); Future future = completer.future; Future later = future.whenComplete(countDown); later.catchError((error) { Expect.equals("error", error); countDown(); }); completer.completeError("error"); } void testFutureWhenCompleteValueNewError() { asyncStart(); int counter = 2; countDown() { if (--counter == 0) asyncEnd(); } var completer = new Completer(); Future future = completer.future; Future later = future.whenComplete(() { countDown(); throw "new error"; }); later.catchError((error) { Expect.equals("new error", error); countDown(); }); completer.complete(42); } void testFutureWhenCompleteErrorNewError() { asyncStart(); int counter = 2; countDown() { if (--counter == 0) asyncEnd(); } var completer = new Completer(); Future future = completer.future; Future later = future.whenComplete(() { countDown(); throw "new error"; }); later.catchError((error) { Expect.equals("new error", error); countDown(); }); completer.completeError("error"); } void testFutureWhenCompletePreValue() { asyncStart(); int counter = 2; countDown() { if (--counter == 0) asyncEnd(); } var completer = new Completer(); Future future = completer.future; completer.complete(42); Timer.run(() { Future later = future.whenComplete(countDown); later.then((v) { Expect.equals(42, v); countDown(); }); }); } void testFutureWhenValueFutureValue() { asyncStart(); int counter = 3; countDown(int expect) { Expect.equals(expect, counter); if (--counter == 0) asyncEnd(); } var completer = new Completer(); completer.future.whenComplete(() { countDown(3); var completer2 = new Completer(); new Timer(MS * 10, () { countDown(2); completer2.complete(37); }); return completer2.future; }).then((v) { Expect.equals(42, v); countDown(1); }); completer.complete(42); } void testFutureWhenValueFutureError() { asyncStart(); int counter = 3; countDown(int expect) { Expect.equals(expect, counter); if (--counter == 0) asyncEnd(); } var completer = new Completer(); completer.future.whenComplete(() { countDown(3); var completer2 = new Completer(); new Timer(MS * 10, () { countDown(2); completer2.completeError("Fail"); }); return completer2.future; }).then((v) { Expect.fail("should fail async"); }, onError: (error) { Expect.equals("Fail", error); countDown(1); }); completer.complete(42); } void testFutureWhenErrorFutureValue() { asyncStart(); int counter = 3; countDown(int expect) { Expect.equals(expect, counter); if (--counter == 0) asyncEnd(); } var completer = new Completer(); completer.future.whenComplete(() { countDown(3); var completer2 = new Completer(); new Timer(MS * 10, () { countDown(2); completer2.complete(37); }); return completer2.future; }).then((v) { Expect.fail("should fail async"); }, onError: (error) { Expect.equals("Error", error); countDown(1); }); completer.completeError("Error"); } void testFutureWhenErrorFutureError() { asyncStart(); int counter = 3; countDown(int expect) { Expect.equals(expect, counter); if (--counter == 0) asyncEnd(); } var completer = new Completer(); completer.future.whenComplete(() { countDown(3); var completer2 = new Completer(); new Timer(MS * 10, () { countDown(2); completer2.completeError("Fail"); }); return completer2.future; }).then((v) { Expect.fail("should fail async"); }, onError: (error) { Expect.equals("Fail", error); countDown(1); }); completer.completeError("Error"); } void testFutureThenThrowsAsync() { final completer = new Completer(); final future = completer.future; int error = 42; asyncStart(); future.then((v) { throw error; }).catchError((e) { Expect.identical(error, e); asyncEnd(); }); completer.complete(0); } void testFutureCatchThrowsAsync() { final completer = new Completer(); final future = completer.future; int error = 42; asyncStart(); future.catchError((e) { throw error; }).catchError((e) { Expect.identical(error, e); asyncEnd(); }); completer.completeError(0); } void testFutureCatchRethrowsAsync() { final completer = new Completer(); final future = completer.future; var error; asyncStart(); future.catchError((e) { error = e; throw e; }).catchError((e) { Expect.identical(error, e); asyncEnd(); }); completer.completeError(0); } void testFutureWhenThrowsAsync() { final completer = new Completer(); final future = completer.future; var error = 42; asyncStart(); future.whenComplete(() { throw error; }).catchError((e) { Expect.identical(error, e); asyncEnd(); }); completer.complete(0); } void testCompleteWithError() { final completer = new Completer(); final future = completer.future; var error = 42; asyncStart(); future.catchError((e) { Expect.identical(error, e); asyncEnd(); }); completer.completeError(error); } void testCompleteWithFutureSuccess() { asyncStart(); final completer = new Completer(); final completer2 = new Completer(); completer.complete(completer2.future); completer.future.then((v) { Expect.equals(42, v); asyncEnd(); }); completer2.complete(42); } void testCompleteWithFutureSuccess2() { asyncStart(); final completer = new Completer(); final result = new Future.value(42); completer.complete(result); completer.future.then((v) { Expect.equals(42, v); asyncEnd(); }); } void testCompleteWithFutureError() { asyncStart(); final completer = new Completer(); final completer2 = new Completer(); completer.complete(completer2.future); completer.future.then((v) { Expect.fail("Should not happen"); asyncEnd(); }, onError: (e) { Expect.equals("ERROR-tcwfe", e); asyncEnd(); }); completer2.completeError("ERROR-tcwfe"); } void testCompleteWithFutureError2() { asyncStart(); final completer = new Completer(); var result = new Future.error("ERROR-tcwfe2"); completer.complete(result); completer.future.then((v) { Expect.fail("Should not happen"); asyncEnd(); }, onError: (e) { Expect.equals("ERROR-tcwfe2", e); asyncEnd(); }); } void testCompleteErrorWithFuture() { asyncStart(); final completer = new Completer(); completer.completeError(new Future.value(42)); completer.future.then((_) { Expect.fail("Shouldn't happen"); }, onError: (e, s) { Future f = e; f.then((v) { Expect.equals(42, v); asyncEnd(); }); }); } void testCompleteWithCustomFutureSuccess() { asyncStart(); final completer = new Completer(); final completer2 = new Completer(); completer.complete(new CustomFuture(completer2.future)); completer.future.then((v) { Expect.equals(42, v); asyncEnd(); }); completer2.complete(42); } void testCompleteWithCustomFutureError() { asyncStart(); final completer = new Completer(); final completer2 = new Completer(); completer.complete(new CustomFuture(completer2.future)); completer.future.then((v) { Expect.fail("Should not happen"); asyncEnd(); }, onError: (e) { Expect.equals("ERROR-tcwcfe", e); asyncEnd(); }); completer2.completeError("ERROR-tcwcfe"); } void testCompleteErrorWithCustomFuture() { asyncStart(); final completer = new Completer(); var future = new CustomFuture(new Future.value(42)); completer.completeError(future); completer.future.then((_) { Expect.fail("Shouldn't happen"); }, onError: (e) { Future f = e; f.then((v) { Expect.equals(42, v); asyncEnd(); }); }); } void testCompleteErrorWithNull() { final completer = new Completer(); Expect.throwsTypeError(() { completer.completeError(null); }); } void testChainedFutureValue() { final completer = new Completer(); final future = completer.future; asyncStart(); future.then((v) => new Future.value(v * 2)).then((v) { Expect.equals(42, v); asyncEnd(); }); completer.complete(21); } void testChainedFutureValueDelay() { final completer = new Completer(); final future = completer.future; asyncStart(); future .then((v) => new Future.delayed(const Duration(milliseconds: 10), () => v * 2)) .then((v) { Expect.equals(42, v); asyncEnd(); }); completer.complete(21); } void testChainedFutureValue2Delay() { asyncStart(); new Future.delayed(const Duration(milliseconds: 10)).then((v) { Expect.isNull(v); asyncEnd(); }); } void testChainedFutureError() { final completer = new Completer(); final future = completer.future; asyncStart(); future.then((v) => new Future.error("Fehler")).then((v) { Expect.fail("unreachable!"); }, onError: (error) { Expect.equals("Fehler", error); asyncEnd(); }); completer.complete(21); } void testSyncFuture_i13368() { asyncStart(); final future = new Future.sync(() { return new Future.value(42); }); future.then((int val) { Expect.equals(val, 42); asyncEnd(); }); } void testWaitCleanUp() { asyncStart(); // Creates three futures with different completion times, and where some fail. // The `mask` specifies which futures fail (values 1-7), // and `permute` defines the order of completion. values 0-5. void doTest(int mask, int permute) { asyncStart(); String stringId = "waitCleanup-$mask-$permute"; List futures = new List(3); List cleanup = new List(3); int permuteTmp = permute; for (int i = 0; i < 3; i++) { bool throws = (mask & (1 << i)) != 0; var future = new Future.delayed(new Duration(milliseconds: 100 * (i + 1)), () => (throws ? throw "Error $i($mask-$permute)" : i)); int mod = 3 - i; int position = permuteTmp % mod; permuteTmp = permuteTmp ~/ mod; while (futures[position] != null) position++; futures[position] = future; cleanup[i] = throws; } void cleanUp(index) { Expect.isFalse(cleanup[index]); cleanup[index] = true; } Future.wait(futures, cleanUp: cleanUp).then((_) { Expect.fail("No error: $stringId"); }, onError: (e, s) { Expect.listEquals([true, true, true], cleanup); asyncEnd(); }); } for (int i = 1; i < 8; i++) { for (int j = 0; j < 6; j++) { doTest(i, j); } } asyncEnd(); } void testWaitCleanUpEager() { asyncStart(); // Creates three futures with different completion times, and where some fail. // The `mask` specifies which futures fail (values 1-7), // and `permute` defines the order of completion. values 0-5. void doTest(int mask, int permute) { asyncStart(); asyncStart(); bool done = false; String stringId = "waitCleanup-$mask-$permute"; List futures = new List(3); List cleanup = new List(3); int permuteTmp = permute; for (int i = 0; i < 3; i++) { bool throws = (mask & (1 << i)) != 0; var future = new Future.delayed(new Duration(milliseconds: 100 * (i + 1)), () => (throws ? throw "Error $i($mask-$permute)" : i)); int mod = 3 - i; int position = permuteTmp % mod; permuteTmp = permuteTmp ~/ mod; while (futures[position] != null) position++; futures[position] = future; cleanup[i] = throws; } void checkDone() { if (done) return; if (cleanup.every((v) => v)) { done = true; asyncEnd(); } } void cleanUp(index) { Expect.isFalse(cleanup[index]); cleanup[index] = true; // Cleanup might happen before and after the wait().then() callback. checkDone(); } Future.wait(futures, eagerError: true, cleanUp: cleanUp).then((_) { Expect.fail("No error: $stringId"); }, onError: (e, s) { asyncEnd(); checkDone(); }); } for (int i = 1; i < 8; i++) { for (int j = 0; j < 6; j++) { doTest(i, j); } } asyncEnd(); } void testWaitCleanUpError() { var cms = const Duration(milliseconds: 100); var cleanups = new List.filled(3, false); var uncaughts = new List.filled(3, false); asyncStart(); asyncStart(); asyncStart(); runZoned(() { Future.wait([ new Future.delayed(cms, () => 0), new Future.delayed(cms * 2, () => throw 1), new Future.delayed(cms * 3, () => 2) ], cleanUp: (index) { Expect.isTrue(index == 0 || index == 2, "$index"); Expect.isFalse(cleanups[index]); cleanups[index] = true; throw index; }).catchError((e) { Expect.equals(e, 1); asyncEnd(); }); }, onError: (e, s) { int index = e; Expect.isTrue(index == 0 || index == 2, "$index"); Expect.isFalse(uncaughts[index]); uncaughts[index] = true; asyncEnd(); }); } void testWaitSyncError() { var cms = const Duration(milliseconds: 100); var cleanups = new List.filled(3, false); asyncStart(); asyncStart(); runZoned(() { Future.wait( new Iterable.generate(5, (i) { if (i != 3) return new Future.delayed(cms * (i + 1), () => i); throw "throwing synchronously in iterable"; }), cleanUp: (index) { Expect.isFalse(cleanups[index]); cleanups[index] = true; if (cleanups.every((x) => x)) asyncEnd(); }); }, onError: (e, s) { asyncEnd(); }); } void testWaitSyncError2() { asyncStart(); Future.wait([null]).catchError((e, st) { // Makes sure that the `catchError` is invoked. // Regression test: an earlier version of `Future.wait` would propagate // the error too soon for the code to install an error handler. // `testWaitSyncError` didn't show this problem, because the `runZoned` // was already installed. asyncEnd(); }); } // Future.wait transforms synchronous errors into asynchronous ones. // This function tests that zones can intercept them. void testWaitSyncError3() { var caughtError; var count = 0; AsyncError errorCallback(Zone self, ZoneDelegate parent, Zone zone, Object error, StackTrace stackTrace) { Expect.equals(0, count); count++; caughtError = error; return parent.errorCallback(zone, error, stackTrace); } asyncStart(); runZoned(() { Future.wait([null]).catchError((e, st) { Expect.identical(e, caughtError); Expect.equals(1, count); asyncEnd(); }); }, zoneSpecification: new ZoneSpecification(errorCallback: errorCallback)); } void testBadFuture() { var bad = new BadFuture(); // Completing with bad future (then call throws) puts error in result. asyncStart(); Completer completer = new Completer(); completer.complete(bad); completer.future.then((_) { Expect.fail("unreachable"); }, onError: (e, s) { Expect.isTrue(completer.isCompleted); asyncEnd(); }); asyncStart(); var f = new Future.value().then((_) => bad); f.then((_) { Expect.fail("unreachable"); }, onError: (e, s) { asyncEnd(); }); } void testTypes() { // Test that future is a Future and not something less precise. testType(name, future, [depth = 2]) { var desc = "$name${".whenComplete" * (2 - depth)}"; Expect.isTrue(future is Future, "$desc is Future"); Expect.isFalse(future is Future, "$desc is! Future"); var stream = future.asStream(); Expect.isTrue(stream is Stream, "$desc.asStream() is Stream"); Expect.isFalse( stream is Stream, "$desc.asStream() is! Stream"); if (depth > 0) { testType(name, future.whenComplete(() {}), depth - 1); } } for (var value in [42, null]) { testType("Future($value)", new Future(() => value)); testType("Future.delayed($value)", new Future.delayed(Duration.zero, () => value)); testType( "Future.microtask($value)", new Future.microtask(() => value)); testType("Future.sync($value)", new Future.sync(() => value)); testType("Future.sync(future($value))", new Future.sync(() => new Future.value(value))); testType("Future.value($value)", new Future.value(value)); } testType("Completer.future", new Completer().future); testType("Future.error", new Future.error("ERR")..catchError((_) {})); } void testAnyValue() { asyncStart(); var cs = new List.generate(3, (_) => new Completer()); var result = Future.any(cs.map((x) => x.future)); result.then((v) { Expect.equals(42, v); asyncEnd(); }, onError: (e, s) { Expect.fail("Unexpected error: $e"); }); cs[1].complete(42); cs[2].complete(10); cs[0].complete(20); } void testAnyError() { asyncStart(); var cs = new List.generate(3, (_) => new Completer()); var result = Future.any(cs.map((x) => x.future)); result.then((v) { Expect.fail("Unexpected value: $v"); }, onError: (e, s) { Expect.equals(42, e); asyncEnd(); }); cs[1].completeError(42); cs[2].complete(10); cs[0].complete(20); } void testAnyIgnoreIncomplete() { asyncStart(); var cs = new List.generate(3, (_) => new Completer()); var result = Future.any(cs.map((x) => x.future)); result.then((v) { Expect.equals(42, v); asyncEnd(); }, onError: (e, s) { Expect.fail("Unexpected error: $e"); }); cs[1].complete(42); // The other two futures never complete. } void testAnyIgnoreError() { asyncStart(); var cs = new List.generate(3, (_) => new Completer()); var result = Future.any(cs.map((x) => x.future)); result.then((v) { Expect.equals(42, v); asyncEnd(); }, onError: (e, s) { Expect.fail("Unexpected error: $e"); }); cs[1].complete(42); // The errors are ignored, not uncaught. cs[2].completeError("BAD"); cs[0].completeError("BAD"); } void testFutureResult() { asyncStart(); () async { var f = new UglyFuture(5); // Sanity check that our future is as mis-behaved as we think. f.then((v) { Expect.equals(UglyFuture(4), v); }); var v = await f; // The static type of await is Flatten(static-type-of-expression), so it // suggests that it flattens. In practice it currently doesn't. // The specification doesn't say anything special, so v should be the // completion value of the UglyFuture future which is a future. Expect.equals(UglyFuture(4), v); // We no longer flatten recursively in situations like this. var w = new Future.value(42).then((_) => f); Expect.equals(UglyFuture(4), await w); asyncEnd(); }(); } void testFutureOfFuture() async { // Plain Future. asyncStart(); var future = Future>.value(Future.value(42)); Expect.type>>(future); future.then((innerFuture) { Expect.type>(innerFuture); innerFuture.then((number) { Expect.equals(42, number); asyncEnd(); }); }); // With completer. asyncStart(); var completer = Completer>(); Expect.type>>(completer.future); completer.future.then((innerFuture) { Expect.type>(innerFuture); innerFuture.then((number) { Expect.equals(42, number); asyncEnd(); }); }); completer.complete(Future.value(42)); } main() { asyncStart(); testValue(); testSync(); testNeverComplete(); testComplete(); testCompleteWithSuccessHandlerBeforeComplete(); testCompleteWithSuccessHandlerAfterComplete(); testCompleteManySuccessHandlers(); testCompleteWithError(); testCompleteWithFutureSuccess(); testCompleteWithFutureSuccess2(); testCompleteWithFutureError(); testCompleteWithFutureError2(); testCompleteErrorWithFuture(); testCompleteWithCustomFutureSuccess(); testCompleteWithCustomFutureError(); testCompleteErrorWithCustomFuture(); testCompleteErrorWithNull(); testException(); testExceptionHandler(); testExceptionHandlerReturnsTrue(); testExceptionHandlerReturnsTrue2(); testExceptionHandlerReturnsFalse(); testFutureAsStreamCompleteAfter(); testFutureAsStreamCompleteBefore(); testFutureAsStreamCompleteImmediate(); testFutureAsStreamCompleteErrorAfter(); testFutureAsStreamWrapper(); testFutureWhenCompleteValue(); testFutureWhenCompleteError(); testFutureWhenCompleteValueNewError(); testFutureWhenCompleteErrorNewError(); testFutureWhenValueFutureValue(); testFutureWhenErrorFutureValue(); testFutureWhenValueFutureError(); testFutureWhenErrorFutureError(); testFutureThenThrowsAsync(); testFutureCatchThrowsAsync(); testFutureWhenThrowsAsync(); testFutureCatchRethrowsAsync(); testChainedFutureValue(); testChainedFutureValueDelay(); testChainedFutureError(); testSyncFuture_i13368(); testWaitCleanUp(); testWaitCleanUpError(); testWaitSyncError(); testWaitSyncError2(); testWaitSyncError3(); testBadFuture(); testTypes(); testAnyValue(); testAnyError(); testAnyIgnoreIncomplete(); testAnyIgnoreError(); testFutureResult(); testFutureOfFuture(); asyncEnd(); } /// A well-behaved Future that isn't recognizable as a _Future. class CustomFuture implements Future { Future _realFuture; CustomFuture(this._realFuture); Future then(action(T result), {Function onError}) => _realFuture.then(action, onError: onError); Future catchError(Function onError, {bool test(e)}) => _realFuture.catchError(onError, test: test); Future whenComplete(action()) => _realFuture.whenComplete(action); Future timeout(Duration timeLimit, {void onTimeout()}) => _realFuture.timeout(timeLimit, onTimeout: onTimeout); Stream asStream() => _realFuture.asStream(); String toString() => "CustomFuture@${_realFuture.hashCode}"; int get hashCode => _realFuture.hashCode; } /// A bad future that throws on every method. class BadFuture implements Future { Future then(action(T result), {Function onError}) { throw "then GOTCHA!"; } Future catchError(Function onError, {bool test(Object e)}) { throw "catch GOTCHA!"; } Future whenComplete(action()) { throw "finally GOTCHA!"; } Stream asStream() { throw "asStream GOTCHA!"; } Future timeout(Duration duration, {onTimeout()}) { throw "timeout GOTCHA!"; } } // An evil future that completes with another future. class UglyFuture implements Future { final _result; final int _badness; UglyFuture(int badness) : _badness = badness, _result = (badness == 0) ? 42 : new UglyFuture(badness - 1); Future then(action(value), {Function onError}) { var c = new Completer(); c.complete(new Future.microtask(() => action(_result))); return c.future; } Future catchError(onError, {test}) => this; // Never an error. Future whenComplete(action()) { return new Future.microtask(action).then((_) => this); } Stream asStream() { return (new StreamController() ..add(_result) ..close()) .stream; } Future timeout(Duration duration, {onTimeout()}) { return this; } int get hashCode => _badness; bool operator ==(Object other) => other is UglyFuture && _badness == other._badness; String toString() => "UglyFuture($_badness)"; }