| Index: test/utils.dart
|
| diff --git a/test/utils.dart b/test/utils.dart
|
| index 64a3da7101a1c4781ad48037ee6d04dfd605ede5..d8e3e9decd3983a39a0e9cddc53c376b97a5a180 100644
|
| --- a/test/utils.dart
|
| +++ b/test/utils.dart
|
| @@ -7,10 +7,12 @@ library unittest.test.utils;
|
| import 'dart:async';
|
| import 'dart:collection';
|
|
|
| +import 'package:unittest/src/invoker.dart';
|
| import 'package:unittest/src/live_test.dart';
|
| import 'package:unittest/src/load_exception.dart';
|
| import 'package:unittest/src/remote_exception.dart';
|
| import 'package:unittest/src/state.dart';
|
| +import 'package:unittest/src/suite.dart';
|
| import 'package:unittest/unittest.dart';
|
|
|
| // The last state change detected via [expectStates].
|
| @@ -63,19 +65,102 @@ void expectSingleError(LiveTest liveTest) {
|
| }
|
|
|
| /// Returns a matcher that matches a [TestFailure] with the given [message].
|
| -Matcher isTestFailure(String message) => predicate(
|
| - (error) => error is TestFailure && error.message == message,
|
| - 'is a TestFailure with message "$message"');
|
| +///
|
| +/// [message] can be a string or a [Matcher].
|
| +Matcher isTestFailure(message) => new _IsTestFailure(wrapMatcher(message));
|
| +
|
| +class _IsTestFailure extends Matcher {
|
| + final Matcher _message;
|
| +
|
| + _IsTestFailure(this._message);
|
| +
|
| + bool matches(item, Map matchState) =>
|
| + item is TestFailure && _message.matches(item.message, matchState);
|
| +
|
| + Description describe(Description description) =>
|
| + description.add('a TestFailure with message ').addDescriptionOf(_message);
|
| +
|
| + Description describeMismatch(item, Description mismatchDescription,
|
| + Map matchState, bool verbose) {
|
| + if (item is! TestFailure) {
|
| + return mismatchDescription.addDescriptionOf(item)
|
| + .add('is not a TestFailure');
|
| + } else {
|
| + return mismatchDescription
|
| + .add('message ')
|
| + .addDescriptionOf(item.message)
|
| + .add(' is not ')
|
| + .addDescriptionOf(_message);
|
| + }
|
| + }
|
| +}
|
|
|
| /// Returns a matcher that matches a [RemoteException] with the given [message].
|
| -Matcher isRemoteException(String message) => predicate(
|
| - (error) => error is RemoteException && error.message == message,
|
| - 'is a RemoteException with message "$message"');
|
| +///
|
| +/// [message] can be a string or a [Matcher].
|
| +Matcher isRemoteException(message) =>
|
| + new _IsRemoteException(wrapMatcher(message));
|
| +
|
| +class _IsRemoteException extends Matcher {
|
| + final Matcher _message;
|
| +
|
| + _IsRemoteException(this._message);
|
| +
|
| + bool matches(item, Map matchState) =>
|
| + item is RemoteException && _message.matches(item.message, matchState);
|
| +
|
| + Description describe(Description description) =>
|
| + description.add('a RemoteException with message ')
|
| + .addDescriptionOf(_message);
|
| +
|
| + Description describeMismatch(item, Description mismatchDescription,
|
| + Map matchState, bool verbose) {
|
| + if (item is! RemoteException) {
|
| + return mismatchDescription.addDescriptionOf(item)
|
| + .add('is not a RemoteException');
|
| + } else {
|
| + return mismatchDescription
|
| + .add('message ')
|
| + .addDescriptionOf(item)
|
| + .add(' is not ')
|
| + .addDescriptionOf(_message);
|
| + }
|
| + }
|
| +}
|
| +
|
| +/// Returns a matcher that matches a [LoadException] with the given
|
| +/// [innerError].
|
| +///
|
| +/// [innerError] can be a string or a [Matcher].
|
| +Matcher isLoadException(innerError) =>
|
| + new _IsLoadException(wrapMatcher(innerError));
|
| +
|
| +class _IsLoadException extends Matcher {
|
| + final Matcher _innerError;
|
| +
|
| + _IsLoadException(this._innerError);
|
|
|
| -/// Returns a matcher that matches a [LoadException] with the given [message].
|
| -Matcher isLoadException(String message) => predicate(
|
| - (error) => error is LoadException && error.innerError == message,
|
| - 'is a LoadException with message "$message"');
|
| + bool matches(item, Map matchState) =>
|
| + item is LoadException && _innerError.matches(item.innerError, matchState);
|
| +
|
| + Description describe(Description description) =>
|
| + description.add('a LoadException with message ')
|
| + .addDescriptionOf(_innerError);
|
| +
|
| + Description describeMismatch(item, Description mismatchDescription,
|
| + Map matchState, bool verbose) {
|
| + if (item is! LoadException) {
|
| + return mismatchDescription.addDescriptionOf(item)
|
| + .add('is not a LoadException');
|
| + } else {
|
| + return mismatchDescription
|
| + .add('inner error ')
|
| + .addDescriptionOf(item)
|
| + .add(' is not ')
|
| + .addDescriptionOf(_innerError);
|
| + }
|
| + }
|
| +}
|
|
|
| /// Returns a [Future] that completes after pumping the event queue [times]
|
| /// times.
|
| @@ -90,3 +175,67 @@ Future pumpEventQueue([int times=20]) {
|
| // method.
|
| return new Future(() => pumpEventQueue(times - 1));
|
| }
|
| +
|
| +/// Returns a local [LiveTest] that runs [body].
|
| +LiveTest createTest(body()) {
|
| + var test = new LocalTest("test", body);
|
| + var suite = new Suite("suite", [test]);
|
| + return test.load(suite);
|
| +}
|
| +
|
| +/// Runs [body] as a test.
|
| +///
|
| +/// Once it completes, returns the [LiveTest] used to run it.
|
| +Future<LiveTest> runTest(body()) {
|
| + var liveTest = createTest(body);
|
| + return liveTest.run().then((_) => liveTest);
|
| +}
|
| +
|
| +/// Asserts that [liveTest] has completed and passed.
|
| +///
|
| +/// If the test had any errors, they're surfaced nicely into the outer test.
|
| +void expectTestPassed(LiveTest liveTest) {
|
| + // Since the test is expected to pass, we forward any current or future errors
|
| + // to the outer test, because they're definitely unexpected.
|
| + for (var error in liveTest.errors) {
|
| + registerException(error.error, error.stackTrace);
|
| + }
|
| + liveTest.onError.listen((error) {
|
| + registerException(error.error, error.stackTrace);
|
| + });
|
| +
|
| + expect(liveTest.state.status, equals(Status.complete));
|
| + expect(liveTest.state.result, equals(Result.success));
|
| +}
|
| +
|
| +/// Asserts that [liveTest] failed with a single [TestFailure] whose message
|
| +/// matches [message].
|
| +void expectTestFailed(LiveTest liveTest, message) {
|
| + expect(liveTest.state.status, equals(Status.complete));
|
| + expect(liveTest.state.result, equals(Result.failure));
|
| + expect(liveTest.errors, hasLength(1));
|
| + expect(liveTest.errors.first.error, isTestFailure(message));
|
| +}
|
| +
|
| +/// Assert that the [test] callback causes a test to block until [stopBlocking]
|
| +/// is called at some later time.
|
| +///
|
| +/// [stopBlocking] is passed the return value of [test].
|
| +Future expectTestBlocks(test(), stopBlocking(value)) {
|
| + var liveTest;
|
| + var future;
|
| + liveTest = createTest(() {
|
| + var value = test();
|
| + future = pumpEventQueue().then((_) {
|
| + expect(liveTest.state.status, equals(Status.running));
|
| + stopBlocking(value);
|
| + });
|
| + });
|
| +
|
| + return liveTest.run().then((_) {
|
| + expectTestPassed(liveTest);
|
| + // Ensure that the outer test doesn't complete until the inner future
|
| + // completes.
|
| + return future;
|
| + });
|
| +}
|
|
|