blob: d5a001e77e25e3355e5dbc8a356d0f1c2e93b7a7 [file] [log] [blame]
// Copyright (c) 2011, 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.
// @dart = 2.9
part of Expect;
const ONE_MS = const Duration(milliseconds: 1);
typedef CreateStreamFunction = Stream<T> Function<T>(Iterable<T> values);
typedef CreateStreamWithErrorsFunction =
Stream<T> Function<T>(Iterable<T> values, {bool Function(T element) isError});
Duration durationMs(delay) {
return delay == null ? Duration.zero : ONE_MS * delay;
}
Future runLater(void action(), [int delay = 0]) {
asyncStart();
return new Future.delayed(durationMs(delay), (){
action();
asyncEnd();
});
}
Future runAfter(Future f, void action()) {
asyncStart();
return f.whenComplete((){
action();
asyncEnd();
});
}
/// Let the test driver know the test is asynchronous and
/// continues after the method main() exits.
/// see co19 issue #423
/// http://code.google.com/p/co19/issues/detail?id=423
Completer _completer = new Completer();
Future asyncCompleted = _completer.future;
int _asyncTestStart() {
print("unittest-suite-wait-for-done");
return 0;
}
int _asyncCounter=_asyncTestStart();
void asyncStart() {
_asyncCounter++;
// print("asyncStart");
}
void asyncMultiStart(int delta) {
// print("asyncMultiStart $delta");
_asyncCounter += delta;
}
void asyncEnd() {
// print("asyncEnd");
Expect.isFalse(_asyncCounter == 0, "asyncEnd: _asyncCounter==0");
_asyncCounter--;
if (_asyncCounter == 0) {
print("unittest-suite-success");
_completer.complete(null);
}
}
abstract class StreamListener<T> {
void onData(T event);
void onError(error){}
void onDone(){}
bool cancelOnError=false;
StreamSubscription<T> subscription;
StreamSubscription<T> listenTo(Stream<T> stream) {
if (subscription!=null) {
subscription.cancel();
}
subscription=stream.listen(onData, onError:onError, onDone:onDone,
cancelOnError:cancelOnError);
return subscription;
}
}
class Sync2<T> {
Function fire;
bool firstPut = false, secondPut = false;
T val1, val2;
Sync2(this.fire);
void put1(T val) {
val1=val;
firstPut = true;
if (secondPut) {
fire(val1, val2);
}
}
void put2(T val) {
val2 = val;
secondPut = true;
if (firstPut) {
fire(val1, val2);
}
}
}
/// asyncTest is intended for executing tests with asynchronous nature.
/// If [setup] is provided, it will be executed first in order to prepare
/// necessary environment for the test (i.e. create some files, start some
/// services, etc). The [setup] may return some value, which will be passed to
/// [test] and to [cleanup].
/// [test] is main test code. It should return Future instance, which completes
/// when test is over. The future may complete with error, in this case the whole
/// test will fail.
/// If [cleanup] is provided it will be executed after Future returned by [test]
/// is completed. [cleanup] is always called, regardless of test's status.
void asyncTest<T>(Future test(T value), {Future<T> setup(), void cleanup(T value)}) {
asyncStart();
Future<T> setupFuture = (setup != null) ? setup() : new Future.value(null);
setupFuture.then((T setupValue) {
test(setupValue)
.then((_) => asyncEnd())
.whenComplete(() {
if (cleanup != null) {
cleanup(setupValue);
}
});
});
}
/// AsyncExpect is intended for async test to ease checking
/// Future completion value and checking Stream content.
class AsyncExpect {
/// Checks whether the given future completes with expected value.
/// Returns the Future, that may be used for test cleanup via method
/// whenComplete(). If checks are passed, the returned future completes
/// the same way as supplied one. Otherwise, the returned future completes
/// with error.
static Future<T> value<T>(T expected, Future<T> future, [String reason = null]) {
if (reason==null){
reason = StackTrace.current.toString();
}
asyncStart();
return future.then((T value){
if (expected is List) {
Expect.listEquals(expected, value, reason);
} else if (expected is Set) {
Expect.setEquals(expected, value as Iterable, reason);
} else {
Expect.equals(expected, value, reason);
}
asyncEnd();
return value;
});
}
/// Checks whether the given future completes with expected error.
/// Returns the Future, that may be used for test cleanup via method
/// whenComplete(). If checks are passed, the returned future completes
/// the same way as supplied one. Otherwise, the returned future completes
/// with error.
static Future<T> error<T>(Object error, Future<T> future, [String reason = null]) {
if (reason==null){
reason = StackTrace.current.toString();
}
asyncStart();
return future.then(
(_) {
Expect.fail("The future is expected to complete with error " + reason);
return null; // return anything to make analyzer happy
},
onError: (e){
if (error is Function){
Expect.isTrue(Function.apply(error, [e]), reason);
} else {
Expect.equals(error, e, reason);
}
asyncEnd();
// throw e;
}
);
}
/// Checks whether the given stream contains expected data events.
/// Any error in the stream is unexpected and wil fail the test.
static Future<bool> data<T>(List<T> data, Stream<T> stream, [String reason = null]) {
if (reason==null){
reason = StackTrace.current.toString();
}
Completer<bool> completer = new Completer<bool>();
List<T> actual = [];
asyncStart();
stream.listen(
(T value) {
actual.add(value);
},
onDone: () {
Expect.listEquals(data, actual, reason);
asyncEnd();
completer.complete(true);
}
);
return completer.future;
}
/// Checks whether the given stream contains expected data and error events.
static Future<bool> events<T>(List<T> data, List errors, Stream<T> stream, [String reason = null]) {
if (reason==null){
reason = StackTrace.current.toString();
}
Completer<bool> completer = new Completer<bool>();
List<T> actualData = [];
List actualErrors = [];
asyncStart();
stream.listen(
(T value) {
actualData.add(value);
},
onError: (error) {
actualErrors.add(error);
},
onDone: () {
Expect.listEquals(data, actualData, reason);
Expect.listEquals(errors, actualErrors, reason);
asyncEnd();
completer.complete(true);
}
);
return completer.future;
}
}