Files
sdk/tests/lib_2/async/future_test.dart
T
Lasse R.H. Nielsen 478602ad79 Fix errors in Future implementation around Future<Future<X>>.
Previously, when completing a future with another future,
we always chained to the other future. This effectively awaits
the latter future and then completes the former with its result.
This is incorrect behavior if the former future is, say,
a `Future<Future<int>>` and the latter is a `Future<int>`.
In that case we *must not* await the latter future
because we can't complete the former future with an `int`.
We should just complete the future directly with the latter future
as a value.

We now check first whether to chain a `Future<T>` to another
future, and only does so if the latter future is a `Future<T>`
(or it's not a `T`, which shouldn't happen,
but currently does in some places).

Add test for behavior.

Change-Id: I57e27111c2fc7b7792dcf4ae9b7c1d5d504d0c0f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/53602
Commit-Queue: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Nate Bosch <nbosch@google.com>
Reviewed-by: Erik Ernst <eernst@google.com>
2020-12-14 16:34:28 +00:00

1286 lines
31 KiB
Dart

// 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<String>.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<int>();
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<int>();
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<int>();
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<int>();
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<int>();
final future = completer.future;
int before;
int after1;
int after2;
var futures = <Future<int>>[];
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<int>(<Future<int>>[]).then((list) {
Expect.equals(0, list.length);
Expect.type<List<int>>(list);
Expect.notType<List<Null>>(list);
Expect.notType<List<Never>>(list);
asyncEnd();
});
}
// Tests for [catchError]
void testException() {
final completer = new Completer<int>();
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<int>();
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<int>();
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<int>();
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<int>();
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<int>();
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<int>();
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<int>();
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<int>();
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<int>();
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<int>();
final completer2 = new Completer<int>();
completer.complete(completer2.future);
completer.future.then((v) {
Expect.equals(42, v);
asyncEnd();
});
completer2.complete(42);
}
void testCompleteWithFutureSuccess2() {
asyncStart();
final completer = new Completer<int>();
final result = new Future<int>.value(42);
completer.complete(result);
completer.future.then((v) {
Expect.equals(42, v);
asyncEnd();
});
}
void testCompleteWithFutureError() {
asyncStart();
final completer = new Completer<int>();
final completer2 = new Completer<int>();
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<int>();
var result = new Future<int>.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<int>();
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<int>();
final completer2 = new Completer<int>();
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<int>();
final completer2 = new Completer<int>();
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<int>();
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<int>();
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<int>.sync(() {
return new Future<int>.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<Future> 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<Future> futures = new List<Future>(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<int> and not something less precise.
testType(name, future, [depth = 2]) {
var desc = "$name${".whenComplete" * (2 - depth)}";
Expect.isTrue(future is Future<int>, "$desc is Future<int>");
Expect.isFalse(future is Future<String>, "$desc is! Future<String>");
var stream = future.asStream();
Expect.isTrue(stream is Stream<int>, "$desc.asStream() is Stream<int>");
Expect.isFalse(
stream is Stream<String>, "$desc.asStream() is! Stream<String>");
if (depth > 0) {
testType(name, future.whenComplete(() {}), depth - 1);
}
}
for (var value in [42, null]) {
testType("Future($value)", new Future<int>(() => value));
testType("Future.delayed($value)",
new Future<int>.delayed(Duration.zero, () => value));
testType(
"Future.microtask($value)", new Future<int>.microtask(() => value));
testType("Future.sync($value)", new Future<int>.sync(() => value));
testType("Future.sync(future($value))",
new Future<int>.sync(() => new Future<int>.value(value)));
testType("Future.value($value)", new Future<int>.value(value));
}
testType("Completer.future", new Completer<int>().future);
testType("Future.error", new Future<int>.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<Future<int>>.value(Future<int>.value(42));
Expect.type<Future<Future<int>>>(future);
future.then((innerFuture) {
Expect.type<Future<int>>(innerFuture);
innerFuture.then((number) {
Expect.equals(42, number);
asyncEnd();
});
});
// With completer.
asyncStart();
var completer = Completer<Future<int>>();
Expect.type<Future<Future<int>>>(completer.future);
completer.future.then((innerFuture) {
Expect.type<Future<int>>(innerFuture);
innerFuture.then((number) {
Expect.equals(42, number);
asyncEnd();
});
});
completer.complete(Future<int>.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<T> implements Future<T> {
Future<T> _realFuture;
CustomFuture(this._realFuture);
Future<S> then<S>(action(T result), {Function onError}) =>
_realFuture.then(action, onError: onError);
Future<T> catchError(Function onError, {bool test(e)}) =>
_realFuture.catchError(onError, test: test);
Future<T> whenComplete(action()) => _realFuture.whenComplete(action);
Future<T> timeout(Duration timeLimit, {void onTimeout()}) =>
_realFuture.timeout(timeLimit, onTimeout: onTimeout);
Stream<T> asStream() => _realFuture.asStream();
String toString() => "CustomFuture@${_realFuture.hashCode}";
int get hashCode => _realFuture.hashCode;
}
/// A bad future that throws on every method.
class BadFuture<T> implements Future<T> {
Future<S> then<S>(action(T result), {Function onError}) {
throw "then GOTCHA!";
}
Future<T> catchError(Function onError, {bool test(Object e)}) {
throw "catch GOTCHA!";
}
Future<T> whenComplete(action()) {
throw "finally GOTCHA!";
}
Stream<T> asStream() {
throw "asStream GOTCHA!";
}
Future<T> timeout(Duration duration, {onTimeout()}) {
throw "timeout GOTCHA!";
}
}
// An evil future that completes with another future.
class UglyFuture implements Future<dynamic> {
final _result;
final int _badness;
UglyFuture(int badness)
: _badness = badness,
_result = (badness == 0) ? 42 : new UglyFuture(badness - 1);
Future<S> then<S>(action(value), {Function onError}) {
var c = new Completer<S>();
c.complete(new Future<S>.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)";
}