Add a hooks_testing library (#1952)
Previous test for methods like `expect`, `expectLater`, and
`expectAsync` used internal details of the test runner like `LiveTest`
to check behaviors such as holding the test open and specific failure
behavior.
Add an abstraction `TestCaseMonitor` to hide the implementation details
of creating and running a `LocalTest`. Expose only the parts of
`LiveTest` which are used by current tests - the state and errors.
The new class is defined in a new library `hooks_testing` since it is
intended primarily for writing tests for code that uses the `hooks`
library.
Add a new `State` enum to hide some details of `LiveTest`.
There is already a class named `State` that holds the `Status` and
`Result` for a `LiveTest`. This `State` attempts to encode the useful
parts of the status and result into a single enum. We don't get much use
out of the separate status and result. The `Result` is always `passed`
when the `Status` is anything other than `complete` (it shouldn't be
read), and changing the `Result` to anything else is _always_
accompanied by setting the `status` to `complete`. The new enum also
lets us hide the `failure` and `error` distinction instead of adding new
dependencies to it.
This works towards #1465
Migrate tests under `test_api/test/frontend` for the tests which will be
migrating to `package:matcher` to use the new APIs. Move to a
`utils_new.dart` file which will be moved along with these tests, and
the old `utils.dart` file will remain for testing the scaffolding APIs.
Do not replace the `expectTestsBlock` utility, the usage of this utility
is not any more clear or readable than spelling out the full behavior in
the test.
Add tests in `checks` for the behavior of holding the test for pending
work, and for unawaited failures.
Add utilities for testing against a `TestMonitor` locally within the
test.
diff --git a/pkgs/checks/pubspec.yaml b/pkgs/checks/pubspec.yaml
index 2c35809..6cf9a7a 100644
--- a/pkgs/checks/pubspec.yaml
+++ b/pkgs/checks/pubspec.yaml
@@ -11,7 +11,7 @@
dependencies:
async: ^2.8.0
meta: ^1.9.0
- test_api: ^0.4.0
+ test_api: ^0.5.0
dev_dependencies:
test: ^1.21.3
diff --git a/pkgs/checks/pubspec_overrides.yaml b/pkgs/checks/pubspec_overrides.yaml
new file mode 100644
index 0000000..7d9aba4
--- /dev/null
+++ b/pkgs/checks/pubspec_overrides.yaml
@@ -0,0 +1,7 @@
+dependency_overrides:
+ test_api:
+ path: ../test_api
+ test_core:
+ path: ../test_core
+ test:
+ path: ../test
diff --git a/pkgs/checks/test/context_test.dart b/pkgs/checks/test/context_test.dart
new file mode 100644
index 0000000..3fd92ac
--- /dev/null
+++ b/pkgs/checks/test/context_test.dart
@@ -0,0 +1,176 @@
+// Copyright (c) 2023, 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.
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:async/async.dart' hide Result;
+import 'package:checks/checks.dart';
+import 'package:checks/context.dart';
+import 'package:test/scaffolding.dart';
+import 'package:test_api/hooks.dart';
+import 'package:test_api/hooks_testing.dart';
+
+import 'test_shared.dart';
+
+void main() {
+ group('Context', () {
+ test('expectAsync holds test open', () async {
+ late void Function() callback;
+ final monitor = TestCaseMonitor.start(() {
+ check(null).context.expectAsync(() => [''], (actual) async {
+ final completer = Completer<void>();
+ callback = completer.complete;
+ await completer.future;
+ return null;
+ });
+ });
+ await pumpEventQueue();
+ check(monitor).state.equals(State.running);
+ callback();
+ await monitor.onDone;
+ check(monitor).didPass();
+ });
+
+ test('expectAsync does not hold test open past exception', () async {
+ late void Function() callback;
+ final monitor = TestCaseMonitor.start(() {
+ check(null).context.expectAsync(() => [''], (actual) async {
+ final completer = Completer<void>();
+ callback = completer.complete;
+ await completer.future;
+ throw 'oh no!';
+ });
+ });
+ await pumpEventQueue();
+ check(monitor).state.equals(State.running);
+ callback();
+ await monitor.onDone;
+ check(monitor)
+ ..state.equals(State.failed)
+ ..errors.single.has((e) => e.error, 'error').equals('oh no!');
+ });
+
+ test('nestAsync holds test open', () async {
+ late void Function() callback;
+ final monitor = TestCaseMonitor.start(() {
+ check(null).context.nestAsync(() => [''], (actual) async {
+ final completer = Completer<void>();
+ callback = completer.complete;
+ await completer.future;
+ return Extracted.value(null);
+ }, null);
+ });
+ await pumpEventQueue();
+ check(monitor).state.equals(State.running);
+ callback();
+ await monitor.onDone;
+ check(monitor).didPass();
+ });
+
+ test('nestAsync holds test open past async condition', () async {
+ late void Function() callback;
+ final monitor = TestCaseMonitor.start(() {
+ check(null).context.nestAsync(() => [''], (actual) async {
+ return Extracted.value(null);
+ }, LazyCondition((it) async {
+ final completer = Completer<void>();
+ callback = completer.complete;
+ await completer.future;
+ }));
+ });
+ await pumpEventQueue();
+ check(monitor).state.equals(State.running);
+ callback();
+ await monitor.onDone;
+ check(monitor).didPass();
+ });
+
+ test('nestAsync does not hold test open past exception', () async {
+ late void Function() callback;
+ final monitor = TestCaseMonitor.start(() {
+ check(null).context.nestAsync(() => [''], (actual) async {
+ final completer = Completer<void>();
+ callback = completer.complete;
+ await completer.future;
+ throw 'oh no!';
+ }, null);
+ });
+ await pumpEventQueue();
+ check(monitor).state.equals(State.running);
+ callback();
+ await monitor.onDone;
+ check(monitor)
+ ..state.equals(State.failed)
+ ..errors.single.has((e) => e.error, 'error').equals('oh no!');
+ });
+
+ test('expectUnawaited can fail the test after it completes', () async {
+ late void Function() callback;
+ final monitor = await TestCaseMonitor.run(() {
+ check(null).context.expectUnawaited(() => [''], (actual, reject) {
+ final completer = Completer<void>()
+ ..future.then((_) {
+ reject(Rejection(which: ['foo']));
+ });
+ callback = completer.complete;
+ });
+ });
+ check(monitor).state.equals(State.passed);
+ callback();
+ await pumpEventQueue();
+ check(monitor)
+ ..state.equals(State.failed)
+ ..errors.unorderedMatches([
+ it()
+ ..isA<AsyncError>()
+ .has((e) => e.error, 'error')
+ .isA<TestFailure>()
+ .has((f) => f.message, 'message')
+ .isNotNull()
+ .endsWith('Which: foo'),
+ it()
+ ..isA<AsyncError>()
+ .has((e) => e.error, 'error')
+ .isA<String>()
+ .startsWith('This test failed after it had already completed.')
+ ]);
+ });
+ });
+
+ group('SkipExtension', () {
+ test('marks the test as skipped', () async {
+ final monitor = await TestCaseMonitor.run(() {
+ check(null).skip('skip').isNotNull();
+ });
+ check(monitor).state.equals(State.skipped);
+ });
+ });
+}
+
+extension _MonitorChecks on Subject<TestCaseMonitor> {
+ Subject<State> get state => has((m) => m.state, 'state');
+ Subject<Iterable<AsyncError>> get errors => has((m) => m.errors, 'errors');
+ Subject<StreamQueue<AsyncError>> get onError =>
+ has((m) => m.onError, 'onError').withQueue;
+
+ /// Expects that the monitored test is completed as success with no errors.
+ ///
+ /// Sets up an unawaited expectation that the test does not emit errors in the
+ /// future in addition to checking there have been no errors yet.
+ void didPass() {
+ errors.isEmpty();
+ state.equals(State.passed);
+ onError.context.expectUnawaited(() => ['emits no further errors'],
+ (actual, reject) async {
+ await for (var error in actual.rest) {
+ reject(Rejection(which: [
+ ...prefixFirst('threw late error', literal(error.error)),
+ ...(const LineSplitter().convert(
+ TestHandle.current.formatStackTrace(error.stackTrace).toString()))
+ ]));
+ }
+ });
+ }
+}
diff --git a/pkgs/checks/test/test_shared.dart b/pkgs/checks/test/test_shared.dart
index 9d15a05..8c6b257 100644
--- a/pkgs/checks/test/test_shared.dart
+++ b/pkgs/checks/test/test_shared.dart
@@ -59,7 +59,7 @@
]);
}
return Extracted.value(failure.rejection);
- }, _LazyCondition((rejection) {
+ }, LazyCondition((rejection) {
if (didRunCallback) {
rejection
.has((r) => r.actual, 'actual')
@@ -96,9 +96,9 @@
///
/// Allows basing the following condition in `isRejectedByAsync` on the actual
/// value.
-class _LazyCondition<T> implements Condition<T> {
+class LazyCondition<T> implements Condition<T> {
final FutureOr<void> Function(Subject<T>) _apply;
- _LazyCondition(this._apply);
+ LazyCondition(this._apply);
@override
void apply(Subject<T> subject) {
diff --git a/pkgs/test_api/CHANGELOG.md b/pkgs/test_api/CHANGELOG.md
index e3fb57d..0b5dd84 100644
--- a/pkgs/test_api/CHANGELOG.md
+++ b/pkgs/test_api/CHANGELOG.md
@@ -6,6 +6,8 @@
major release.
* **BREAKING** Add required `defaultCompiler` and `supportedCompilers` fields
to `Runtime`.
+* Add `package:test_api/hooks_testing.dart` library for writing tests against
+ code that uses `package:test_api/hooks.dart`.
## 0.4.18
diff --git a/pkgs/test_api/lib/hooks_testing.dart b/pkgs/test_api/lib/hooks_testing.dart
new file mode 100644
index 0000000..8e4a1e9
--- /dev/null
+++ b/pkgs/test_api/lib/hooks_testing.dart
@@ -0,0 +1,156 @@
+// Copyright (c) 2023, 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.
+
+import 'dart:async';
+
+import 'src/backend/group.dart';
+import 'src/backend/invoker.dart';
+import 'src/backend/live_test.dart';
+import 'src/backend/metadata.dart';
+import 'src/backend/runtime.dart';
+import 'src/backend/state.dart';
+import 'src/backend/suite.dart';
+import 'src/backend/suite_platform.dart';
+
+export 'src/backend/state.dart' show Result, Status;
+export 'src/backend/test_failure.dart' show TestFailure;
+
+/// A monitor for the behavior of a callback when it is run as the body of a
+/// test case.
+///
+/// Allows running a callback as the body of a local test case and querying for
+/// the current [state], and [errors], and subscribing to future errors.
+///
+/// Use [run] to run a test body and query for the success or failure.
+///
+/// Use [start] to start a test and query for whether it has finished running.
+class TestCaseMonitor {
+ final LiveTest _liveTest;
+ final _done = Completer<void>();
+ TestCaseMonitor._(FutureOr<void> Function() body)
+ : _liveTest = _createTest(body);
+
+ /// Run [body] as a test case and return a [TestCaseMonitor] with the result.
+ ///
+ /// The [state] will either [State.passed], [State.skipped], or
+ /// [State.failed], the test will no longer be running.
+ ///
+ /// {@template result-late-fail}
+ /// Note that a test can change state from [State.passed] to [State.failed]
+ /// if the test surfaces an unawaited asynchronous error.
+ /// {@endtemplate}
+ ///
+ /// ```dart
+ /// final monitor = await TestCaseMonitor.run(() {
+ /// fail('oh no!');
+ /// });
+ /// assert(monitor.state == State.failed);
+ /// assert((monitor.errors.single.error as TestFailure).message == 'oh no!');
+ /// ```
+ static Future<TestCaseMonitor> run(FutureOr<void> Function() body) async {
+ final monitor = TestCaseMonitor.start(body);
+ await monitor.onDone;
+ return monitor;
+ }
+
+ /// Start [body] as a test case and return a [TestCaseMonitor] with the status
+ /// and result.
+ ///
+ /// The [state] will start as [State.pending] if queried synchronously, but it
+ /// will switch to [State.running]. After `onDone` completes the state will be
+ /// one of [State.passed], [State.skipped], or [State.failed].
+ ///
+ /// {@macro result-late-fail}
+ ///
+ /// ```dart
+ /// late void Function() completeWork;
+ /// final monitor = TestCaseMonitor.start(() {
+ /// final outstandingWork = TestHandle.current.markPending();
+ /// completeWork = outstandingWork.complete;
+ /// });
+ /// await pumpEventQueue();
+ /// assert(monitor.state == State.running);
+ /// completeWork();
+ /// await monitor.onDone;
+ /// assert(monitor.state == State.passed);
+ /// ```
+ static TestCaseMonitor start(FutureOr<void> Function() body) =>
+ TestCaseMonitor._(body).._start();
+
+ void _start() {
+ _liveTest.run().whenComplete(_done.complete);
+ }
+
+ /// A future that completes after this test has finished running, or has
+ /// surfaced an error.
+ Future<void> get onDone => _done.future;
+
+ /// The running and success or failure status for the test case.
+ State get state {
+ final status = _liveTest.state.status;
+ if (status == Status.pending) return State.pending;
+ if (status == Status.running) return State.running;
+ final result = _liveTest.state.result;
+ if (result == Result.skipped) return State.skipped;
+ if (result == Result.success) return State.passed;
+ // result == Result.failure || result == Result.error
+ return State.failed;
+ }
+
+ /// The errors surfaced by the test.
+ ///
+ /// A test with any errors will have a [state] of [State.failed].
+ ///
+ /// {@macro result-late-fail}
+ ///
+ /// A test may have more than one error if there were unhandled asynchronous
+ /// errors surfaced after the test is done.
+ Iterable<AsyncError> get errors => _liveTest.errors;
+
+ /// A stream of errors surfaced by the test.
+ ///
+ /// This stream will not close, asynchronous errors may be surfaced within the
+ /// test's error zone at any point.
+ Stream<AsyncError> get onError => _liveTest.onError;
+}
+
+/// Returns a local [LiveTest] that runs [body].
+LiveTest _createTest(FutureOr<void> Function() body) {
+ var test = LocalTest('test', Metadata(chainStackTraces: true), body);
+ var suite = Suite(Group.root([test]), _suitePlatform, ignoreTimeouts: false);
+ return test.load(suite);
+}
+
+/// A dummy suite platform to use for testing suites.
+final _suitePlatform =
+ SuitePlatform(Runtime.vm, compiler: Runtime.vm.defaultCompiler);
+
+/// The running and success state of a test monitored by a [TestCaseMonitor].
+enum State {
+ /// The test is has not yet started.
+ pending,
+
+ /// The test is running and has not yet failed.
+ running,
+
+ /// The test has completed without any error.
+ ///
+ /// This implies that the test body has completed, and no error has surfaced
+ /// *yet*. However, it this doesn't mean that the test won't fail in the
+ /// future.
+ passed,
+
+ /// The test, or some part of it, has been skipped.
+ ///
+ /// This does not imply that the test has not had an error, but if there are
+ /// errors they are ignored.
+ skipped,
+
+ /// The test has failed.
+ ///
+ /// An test fails when any exception, typically a [TestFailure], is thrown in
+ /// the test's zone. A test that has failed may still have additional errors
+ /// that surface as unhandled asynchronous errors.
+ failed,
+}
diff --git a/pkgs/test_api/test/frontend/expect_async_test.dart b/pkgs/test_api/test/frontend/expect_async_test.dart
index dde2e41..3752b7b 100644
--- a/pkgs/test_api/test/frontend/expect_async_test.dart
+++ b/pkgs/test_api/test/frontend/expect_async_test.dart
@@ -6,41 +6,40 @@
import 'package:fake_async/fake_async.dart';
import 'package:test/test.dart';
-import 'package:test_api/src/backend/live_test.dart';
-import 'package:test_api/src/backend/state.dart';
+import 'package:test_api/hooks_testing.dart';
-import '../utils.dart';
+import '../utils_new.dart';
void main() {
group('supports a function with this many arguments:', () {
test('0', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync0(() {
callbackRun = true;
})();
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
test('1', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync1((int arg) {
expect(arg, equals(1));
callbackRun = true;
})(1);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
test('2', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync2((arg1, arg2) {
expect(arg1, equals(1));
expect(arg2, equals(2));
@@ -48,13 +47,13 @@
})(1, 2);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
test('3', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync3((arg1, arg2, arg3) {
expect(arg1, equals(1));
expect(arg2, equals(2));
@@ -63,13 +62,13 @@
})(1, 2, 3);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
test('4', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync4((arg1, arg2, arg3, arg4) {
expect(arg1, equals(1));
expect(arg2, equals(2));
@@ -79,13 +78,13 @@
})(1, 2, 3, 4);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
test('5', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync5((arg1, arg2, arg3, arg4, arg5) {
expect(arg1, equals(1));
expect(arg2, equals(2));
@@ -96,13 +95,13 @@
})(1, 2, 3, 4, 5);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
test('6', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync6((arg1, arg2, arg3, arg4, arg5, arg6) {
expect(arg1, equals(1));
expect(arg2, equals(2));
@@ -114,7 +113,7 @@
})(1, 2, 3, 4, 5, 6);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
});
@@ -122,46 +121,55 @@
group('with optional arguments', () {
test('allows them to be passed', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync1(([arg = 1]) {
expect(arg, equals(2));
callbackRun = true;
})(2);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
test('allows them not to be passed', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync1(([arg = 1]) {
expect(arg, equals(1));
callbackRun = true;
})();
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
});
group('by default', () {
- test("won't allow the test to complete until it's called", () {
- return expectTestBlocks(
- () => expectAsync0(() {}), (callback) => callback());
+ test("won't allow the test to complete until it's called", () async {
+ late void Function() callback;
+ final monitor = TestCaseMonitor.start(() {
+ callback = expectAsync0(() {});
+ });
+
+ await pumpEventQueue();
+ expect(monitor.state, equals(State.running));
+ callback();
+ await monitor.onDone;
+
+ expectTestPassed(monitor);
});
test('may only be called once', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
var callback = expectAsync0(() {});
callback();
callback();
});
expectTestFailed(
- liveTest, 'Callback called more times than expected (1).');
+ monitor, 'Callback called more times than expected (1).');
});
});
@@ -169,36 +177,31 @@
test(
"won't allow the test to complete until it's called at least that "
'many times', () async {
- late LiveTest liveTest;
- late Future future;
- liveTest = createTest(() {
- var callback = expectAsync0(() {}, count: 3);
-
- future = () async {
- await pumpEventQueue();
- expect(liveTest.state.status, equals(Status.running));
- callback();
-
- await pumpEventQueue();
- expect(liveTest.state.status, equals(Status.running));
- callback();
-
- await pumpEventQueue();
- expect(liveTest.state.status, equals(Status.running));
- callback();
- }();
+ late void Function() callback;
+ final monitor = TestCaseMonitor.start(() {
+ callback = expectAsync0(() {}, count: 3);
});
- await liveTest.run();
- expectTestPassed(liveTest);
- // Ensure that the outer test doesn't complete until the inner future
- // completes.
- await future;
+ await pumpEventQueue();
+ expect(monitor.state, equals(State.running));
+ callback();
+
+ await pumpEventQueue();
+ expect(monitor.state, equals(State.running));
+ callback();
+
+ await pumpEventQueue();
+ expect(monitor.state, equals(State.running));
+ callback();
+
+ await monitor.onDone;
+
+ expectTestPassed(monitor);
});
test("will throw an error if it's called more than that many times",
() async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
var callback = expectAsync0(() {}, count: 3);
callback();
callback();
@@ -207,7 +210,7 @@
});
expectTestFailed(
- liveTest, 'Callback called more times than expected (3).');
+ monitor, 'Callback called more times than expected (3).');
});
group('0,', () {
@@ -216,12 +219,12 @@
});
test("will throw an error if it's ever called", () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync0(() {}, count: 0)();
});
expectTestFailed(
- liveTest, 'Callback called more times than expected (0).');
+ monitor, 'Callback called more times than expected (0).');
});
});
});
@@ -241,7 +244,7 @@
test("will throw an error if it's called more than that many times",
() async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
var callback = expectAsync0(() {}, max: 3);
callback();
callback();
@@ -250,7 +253,7 @@
});
expectTestFailed(
- liveTest, 'Callback called more times than expected (3).');
+ monitor, 'Callback called more times than expected (3).');
});
test('-1, will allow the callback to be called any number of times', () {
@@ -268,25 +271,25 @@
group('expectAsyncUntil()', () {
test("won't allow the test to complete until isDone returns true",
() async {
- late LiveTest liveTest;
+ late TestCaseMonitor monitor;
late Future future;
- liveTest = createTest(() {
+ monitor = TestCaseMonitor.start(() {
var done = false;
var callback = expectAsyncUntil0(() {}, () => done);
future = () async {
await pumpEventQueue();
- expect(liveTest.state.status, equals(Status.running));
+ expect(monitor.state, equals(State.running));
callback();
await pumpEventQueue();
- expect(liveTest.state.status, equals(Status.running));
+ expect(monitor.state, equals(State.running));
done = true;
callback();
}();
});
+ await monitor.onDone;
- await liveTest.run();
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
// Ensure that the outer test doesn't complete until the inner future
// completes.
await future;
@@ -302,77 +305,77 @@
});
test('allows errors', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(expectAsync0(() => throw 'oh no'), throwsA('oh no'));
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
});
test('may be called in a non-test zone', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
var callback = expectAsync0(() {});
Zone.root.run(callback);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
});
test('may be called in a FakeAsync zone that does not run further', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
FakeAsync().run((_) {
var callback = expectAsync0(() {});
callback();
});
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
});
group('old-style expectAsync()', () {
test('works with no arguments', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync(() {
callbackRun = true;
})();
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
test('works with dynamic arguments', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync((arg1, arg2) {
callbackRun = true;
})(1, 2);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
test('works with non-nullable arguments', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync((int arg1, int arg2) {
callbackRun = true;
})(1, 2);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
test('works with 6 arguments', () async {
var callbackRun = false;
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expectAsync((arg1, arg2, arg3, arg4, arg5, arg6) {
callbackRun = true;
})(1, 2, 3, 4, 5, 6);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
expect(callbackRun, isTrue);
});
diff --git a/pkgs/test_api/test/frontend/expect_test.dart b/pkgs/test_api/test/frontend/expect_test.dart
index ee8d243..cae3511 100644
--- a/pkgs/test_api/test/frontend/expect_test.dart
+++ b/pkgs/test_api/test/frontend/expect_test.dart
@@ -4,7 +4,7 @@
import 'package:test/test.dart';
-import '../utils.dart';
+import '../utils_new.dart';
void main() {
group('returned Future from expectLater()', () {
diff --git a/pkgs/test_api/test/frontend/matcher/completion_test.dart b/pkgs/test_api/test/frontend/matcher/completion_test.dart
index 1aee139..4f114af 100644
--- a/pkgs/test_api/test/frontend/matcher/completion_test.dart
+++ b/pkgs/test_api/test/frontend/matcher/completion_test.dart
@@ -5,18 +5,18 @@
import 'dart:async';
import 'package:test/test.dart';
-import 'package:test_api/src/backend/state.dart';
+import 'package:test_api/hooks_testing.dart';
-import '../../utils.dart';
+import '../../utils_new.dart';
void main() {
group('[doesNotComplete]', () {
test('fails when provided a non future', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(10, doesNotComplete);
});
- expectTestFailed(liveTest, contains('10 is not a Future'));
+ expectTestFailed(monitor, contains('10 is not a Future'));
});
test('succeeds when a future does not complete', () {
@@ -25,33 +25,33 @@
});
test('fails when a future does complete', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
var completer = Completer();
completer.complete(null);
expect(completer.future, doesNotComplete);
});
expectTestFailed(
- liveTest,
+ monitor,
'Future was not expected to complete but completed with a value of'
' null');
});
test('fails when a future completes after the expect', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
var completer = Completer();
expect(completer.future, doesNotComplete);
completer.complete(null);
});
expectTestFailed(
- liveTest,
+ monitor,
'Future was not expected to complete but completed with a value of'
' null');
});
test('fails when a future eventually completes', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
var completer = Completer();
expect(completer.future, doesNotComplete);
Future(() async {
@@ -60,46 +60,48 @@
});
expectTestFailed(
- liveTest,
+ monitor,
'Future was not expected to complete but completed with a value of'
' null');
});
});
group('[completes]', () {
- test('blocks the test until the Future completes', () {
- return expectTestBlocks(() {
- var completer = Completer();
+ test('blocks the test until the Future completes', () async {
+ final completer = Completer<void>();
+ final monitor = TestCaseMonitor.start(() {
expect(completer.future, completes);
- return completer;
- }, (completer) => completer.complete());
+ });
+ await pumpEventQueue();
+ expect(monitor.state, State.running);
+ completer.complete();
+ await monitor.onDone;
+ expectTestPassed(monitor);
});
test('with an error', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(Future.error('X'), completes);
});
- expect(liveTest.state.status, equals(Status.complete));
- expect(liveTest.state.result, equals(Result.error));
- expect(liveTest.errors, hasLength(1));
- expect(liveTest.errors.first.error, equals('X'));
+ expect(monitor.state, equals(State.failed));
+ expect(monitor.errors, [isAsyncError(equals('X'))]);
});
test('with a failure', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(Future.error(TestFailure('oh no')), completes);
});
- expectTestFailed(liveTest, 'oh no');
+ expectTestFailed(monitor, 'oh no');
});
test('with a non-future', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(10, completes);
});
expectTestFailed(
- liveTest,
+ monitor,
'Expected: completes successfully\n'
' Actual: <10>\n'
' Which: was not a Future\n');
@@ -111,52 +113,54 @@
});
group('[completion]', () {
- test('blocks the test until the Future completes', () {
- return expectTestBlocks(() {
- var completer = Completer();
+ test('blocks the test until the Future completes', () async {
+ final completer = Completer<Object?>();
+ final monitor = TestCaseMonitor.start(() {
expect(completer.future, completion(isNull));
- return completer;
- }, (completer) => completer.complete());
+ });
+ await pumpEventQueue();
+ expect(monitor.state, State.running);
+ completer.complete(null);
+ await monitor.onDone;
+ expectTestPassed(monitor);
});
test('with an error', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(Future.error('X'), completion(isNull));
});
- expect(liveTest.state.status, equals(Status.complete));
- expect(liveTest.state.result, equals(Result.error));
- expect(liveTest.errors, hasLength(1));
- expect(liveTest.errors.first.error, equals('X'));
+ expect(monitor.state, equals(State.failed));
+ expect(monitor.errors, [isAsyncError(equals('X'))]);
});
test('with a failure', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(Future.error(TestFailure('oh no')), completion(isNull));
});
- expectTestFailed(liveTest, 'oh no');
+ expectTestFailed(monitor, 'oh no');
});
test('with a non-future', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(10, completion(equals(10)));
});
expectTestFailed(
- liveTest,
+ monitor,
'Expected: completes to a value that <10>\n'
' Actual: <10>\n'
' Which: was not a Future\n');
});
test('with an incorrect value', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(Future.value('a'), completion(equals('b')));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: completes to a value that 'b'\n"
' Actual: <'),
diff --git a/pkgs/test_api/test/frontend/matcher/prints_test.dart b/pkgs/test_api/test/frontend/matcher/prints_test.dart
index 92ab6a7..337a812 100644
--- a/pkgs/test_api/test/frontend/matcher/prints_test.dart
+++ b/pkgs/test_api/test/frontend/matcher/prints_test.dart
@@ -5,8 +5,9 @@
import 'dart:async';
import 'package:test/test.dart';
+import 'package:test_api/hooks_testing.dart';
-import '../../utils.dart';
+import '../../utils_new.dart';
void main() {
group('synchronous', () {
@@ -27,12 +28,12 @@
test('describes a failure nicely', () async {
void local() => print('Hello, world!');
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(local, prints('Goodbye, world!\n'));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: prints 'Goodbye, world!\\n'\n"
" ''\n"
@@ -50,12 +51,12 @@
test('describes a failure with a non-descriptive Matcher nicely', () async {
void local() => print('Hello, world!');
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(local, prints(contains('Goodbye')));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: prints contains 'Goodbye'\n"
' Actual: <'),
@@ -67,12 +68,12 @@
test('describes a failure with no text nicely', () async {
void local() {}
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(local, prints(contains('Goodbye')));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: prints contains 'Goodbye'\n"
' Actual: <'),
@@ -82,12 +83,12 @@
});
test('with a non-function', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(10, prints(contains('Goodbye')));
});
expectTestFailed(
- liveTest,
+ monitor,
"Expected: prints contains 'Goodbye'\n"
' Actual: <10>\n'
' Which: was not a unary Function\n');
@@ -116,12 +117,12 @@
test('describes a failure nicely', () async {
void local() => Future(() => print('Hello, world!'));
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(local, prints('Goodbye, world!\n'));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: prints 'Goodbye, world!\\n'\n"
" ''\n"
@@ -139,12 +140,12 @@
test('describes a failure with a non-descriptive Matcher nicely', () async {
void local() => Future(() => print('Hello, world!'));
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(local, prints(contains('Goodbye')));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: prints contains 'Goodbye'\n"
' Actual: <'),
@@ -156,12 +157,12 @@
test('describes a failure with no text nicely', () async {
void local() => Future.value();
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(local, prints(contains('Goodbye')));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: prints contains 'Goodbye'\n"
' Actual: <'),
@@ -170,12 +171,16 @@
]));
});
- test("won't let the test end until the Future completes", () {
- return expectTestBlocks(() {
- var completer = Completer();
+ test("won't let the test end until the Future completes", () async {
+ final completer = Completer<void>();
+ final monitor = TestCaseMonitor.start(() {
expect(() => completer.future, prints(isEmpty));
- return completer;
- }, (completer) => completer.complete());
+ });
+ await pumpEventQueue();
+ expect(monitor.state, State.running);
+ completer.complete();
+ await monitor.onDone;
+ expectTestPassed(monitor);
});
test("blocks expectLater's Future", () async {
diff --git a/pkgs/test_api/test/frontend/matcher/throws_test.dart b/pkgs/test_api/test/frontend/matcher/throws_test.dart
index 40b2be6..a9e76af 100644
--- a/pkgs/test_api/test/frontend/matcher/throws_test.dart
+++ b/pkgs/test_api/test/frontend/matcher/throws_test.dart
@@ -5,8 +5,9 @@
import 'dart:async';
import 'package:test/test.dart';
+import 'package:test_api/hooks_testing.dart';
-import '../../utils.dart';
+import '../../utils_new.dart';
void main() {
group('synchronous', () {
@@ -17,12 +18,12 @@
test("with a function that doesn't throw", () async {
void local() {}
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(local, throws);
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith('Expected: throws\n'
' Actual: <'),
@@ -32,12 +33,12 @@
});
test('with a non-function', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(10, throws);
});
expectTestFailed(
- liveTest,
+ monitor,
'Expected: throws\n'
' Actual: <10>\n'
' Which: was not a Function or Future\n');
@@ -55,12 +56,12 @@
test("with a function that doesn't throw", () async {
void local() {}
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(local, throwsA('oh no'));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: throws 'oh no'\n"
' Actual: <'),
@@ -70,24 +71,24 @@
});
test('with a non-function', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(10, throwsA('oh no'));
});
expectTestFailed(
- liveTest,
+ monitor,
"Expected: throws 'oh no'\n"
' Actual: <10>\n'
' Which: was not a Function or Future\n');
});
test('with a function that throws the wrong error', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(() => throw 'aw dang', throwsA('oh no'));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: throws 'oh no'\n"
' Actual: <'),
@@ -111,12 +112,12 @@
});
test("with a Future that doesn't throw", () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(Future.value(), throws);
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith('Expected: throws\n'
' Actual: <'),
@@ -130,12 +131,12 @@
});
test("with a closure that returns a Future that doesn't throw", () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(() => Future.value(), throws);
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith('Expected: throws\n'
' Actual: <'),
@@ -144,12 +145,18 @@
]));
});
- test("won't let the test end until the Future completes", () {
- return expectTestBlocks(() {
- var completer = Completer();
+ test("won't let the test end until the Future completes", () async {
+ late void Function() callback;
+ final monitor = TestCaseMonitor.start(() {
+ final completer = Completer<void>();
expect(completer.future, throws);
- return completer;
- }, (completer) => completer.completeError('oh no'));
+ callback = () => completer.completeError('oh no');
+ });
+ await pumpEventQueue();
+ expect(monitor.state, State.running);
+ callback();
+ await monitor.onDone;
+ expectTestPassed(monitor);
});
});
@@ -164,12 +171,12 @@
});
test("with a Future that doesn't throw", () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(Future.value(), throwsA('oh no'));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: throws 'oh no'\n"
' Actual: <'),
@@ -179,12 +186,12 @@
});
test('with a Future that throws the wrong error', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(Future.error('aw dang'), throwsA('oh no'));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: throws 'oh no'\n"
' Actual: <'),
@@ -200,12 +207,12 @@
});
test("with a closure that returns a Future that doesn't throw", () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(() => Future.value(), throwsA('oh no'));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: throws 'oh no'\n"
' Actual: <'),
@@ -216,12 +223,12 @@
test('with closure that returns a Future that throws the wrong error',
() async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
expect(() => Future.error('aw dang'), throwsA('oh no'));
});
expectTestFailed(
- liveTest,
+ monitor,
allOf([
startsWith("Expected: throws 'oh no'\n"
' Actual: <'),
@@ -230,12 +237,19 @@
]));
});
- test("won't let the test end until the Future completes", () {
- return expectTestBlocks(() {
- var completer = Completer();
+ test("won't let the test end until the Future completes", () async {
+ late void Function() callback;
+ final monitor = TestCaseMonitor.start(() {
+ final completer = Completer<void>();
expect(completer.future, throwsA('oh no'));
- return completer;
- }, (completer) => completer.completeError('oh no'));
+ callback = () => completer.completeError('oh no');
+ });
+ await pumpEventQueue();
+ expect(monitor.state, State.running);
+ callback();
+ await monitor.onDone;
+
+ expectTestPassed(monitor);
});
test("blocks expectLater's Future", () async {
diff --git a/pkgs/test_api/test/frontend/matcher/throws_type_test.dart b/pkgs/test_api/test/frontend/matcher/throws_type_test.dart
index 6e0a482..dfad890 100644
--- a/pkgs/test_api/test/frontend/matcher/throws_type_test.dart
+++ b/pkgs/test_api/test/frontend/matcher/throws_type_test.dart
@@ -3,8 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:test/test.dart';
+import 'package:test_api/hooks_testing.dart';
-import '../../utils.dart';
+import '../../utils_new.dart';
void main() {
group('[throwsArgumentError]', () {
@@ -13,7 +14,7 @@
});
test('fails when a non-ArgumentError is thrown', () async {
- var liveTest = await runTestBody(() {
+ var liveTest = await TestCaseMonitor.run(() {
expect(() => throw Exception(), throwsArgumentError);
});
@@ -29,7 +30,7 @@
});
test('fails when a non-ConcurrentModificationError is thrown', () async {
- var liveTest = await runTestBody(() {
+ var liveTest = await TestCaseMonitor.run(() {
expect(() => throw Exception(), throwsConcurrentModificationError);
});
@@ -49,7 +50,7 @@
});
test('fails when a non-CyclicInitializationError is thrown', () async {
- var liveTest = await runTestBody(() {
+ var liveTest = await TestCaseMonitor.run(() {
expect(() => throw Exception(), throwsCyclicInitializationError);
});
@@ -64,7 +65,7 @@
});
test('fails when a non-Exception is thrown', () async {
- var liveTest = await runTestBody(() {
+ var liveTest = await TestCaseMonitor.run(() {
expect(() => throw 'oh no', throwsException);
});
@@ -79,7 +80,7 @@
});
test('fails when a non-FormatException is thrown', () async {
- var liveTest = await runTestBody(() {
+ var liveTest = await TestCaseMonitor.run(() {
expect(() => throw Exception(), throwsFormatException);
});
@@ -96,7 +97,7 @@
});
test('fails when a non-NoSuchMethodError is thrown', () async {
- var liveTest = await runTestBody(() {
+ var liveTest = await TestCaseMonitor.run(() {
expect(() => throw Exception(), throwsNoSuchMethodError);
});
@@ -111,7 +112,7 @@
});
test('fails when a non-RangeError is thrown', () async {
- var liveTest = await runTestBody(() {
+ var liveTest = await TestCaseMonitor.run(() {
expect(() => throw Exception(), throwsRangeError);
});
@@ -126,7 +127,7 @@
});
test('fails when a non-StateError is thrown', () async {
- var liveTest = await runTestBody(() {
+ var liveTest = await TestCaseMonitor.run(() {
expect(() => throw Exception(), throwsStateError);
});
@@ -141,7 +142,7 @@
});
test('fails when a non-UnimplementedError is thrown', () async {
- var liveTest = await runTestBody(() {
+ var liveTest = await TestCaseMonitor.run(() {
expect(() => throw Exception(), throwsUnimplementedError);
});
@@ -156,7 +157,7 @@
});
test('fails when a non-UnsupportedError is thrown', () async {
- var liveTest = await runTestBody(() {
+ var liveTest = await TestCaseMonitor.run(() {
expect(() => throw Exception(), throwsUnsupportedError);
});
diff --git a/pkgs/test_api/test/frontend/never_called_test.dart b/pkgs/test_api/test/frontend/never_called_test.dart
index e9170f9..f4160a3 100644
--- a/pkgs/test_api/test/frontend/never_called_test.dart
+++ b/pkgs/test_api/test/frontend/never_called_test.dart
@@ -4,8 +4,9 @@
import 'package:term_glyph/term_glyph.dart' as glyph;
import 'package:test/test.dart';
+import 'package:test_api/hooks_testing.dart';
-import '../utils.dart';
+import '../utils_new.dart';
void main() {
setUpAll(() {
@@ -13,32 +14,32 @@
});
test("doesn't throw if it isn't called", () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
const Stream.empty().listen(neverCalled);
});
- expectTestPassed(liveTest);
+ expectTestPassed(monitor);
});
group("if it's called", () {
test('throws', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
neverCalled();
});
expectTestFailed(
- liveTest,
+ monitor,
'Callback should never have been called, but it was called with no '
'arguments.');
});
test('pretty-prints arguments', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
neverCalled(1, 'foo\nbar');
});
expectTestFailed(
- liveTest,
+ monitor,
'Callback should never have been called, but it was called with:\n'
'* <1>\n'
"* 'foo\\n'\n"
@@ -46,18 +47,18 @@
});
test('keeps the test alive', () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
pumpEventQueue(times: 10).then(neverCalled);
});
expectTestFailed(
- liveTest,
+ monitor,
'Callback should never have been called, but it was called with:\n'
'* <null>');
});
test("can't be caught", () async {
- var liveTest = await runTestBody(() {
+ var monitor = await TestCaseMonitor.run(() {
try {
neverCalled();
} catch (_) {
@@ -66,7 +67,7 @@
});
expectTestFailed(
- liveTest,
+ monitor,
'Callback should never have been called, but it was called with '
'no arguments.');
});
diff --git a/pkgs/test_api/test/frontend/stream_matcher_test.dart b/pkgs/test_api/test/frontend/stream_matcher_test.dart
index deb21fb..37d37bf 100644
--- a/pkgs/test_api/test/frontend/stream_matcher_test.dart
+++ b/pkgs/test_api/test/frontend/stream_matcher_test.dart
@@ -8,7 +8,7 @@
import 'package:term_glyph/term_glyph.dart' as glyph;
import 'package:test/test.dart';
-import '../utils.dart';
+import '../utils_new.dart';
void main() {
setUpAll(() {
diff --git a/pkgs/test_api/test/utils.dart b/pkgs/test_api/test/utils.dart
index c8c4741..ca3d729 100644
--- a/pkgs/test_api/test/utils.dart
+++ b/pkgs/test_api/test/utils.dart
@@ -6,14 +6,10 @@
import 'package:test/test.dart';
import 'package:test_api/src/backend/declarer.dart';
-import 'package:test_api/src/backend/group.dart';
import 'package:test_api/src/backend/group_entry.dart';
-import 'package:test_api/src/backend/invoker.dart';
import 'package:test_api/src/backend/live_test.dart';
-import 'package:test_api/src/backend/metadata.dart';
import 'package:test_api/src/backend/runtime.dart';
import 'package:test_api/src/backend/state.dart';
-import 'package:test_api/src/backend/suite.dart';
import 'package:test_api/src/backend/suite_platform.dart';
import 'package:test_core/src/runner/engine.dart';
import 'package:test_core/src/runner/plugin/environment.dart';
@@ -77,34 +73,12 @@
]);
}
-/// Returns a matcher that matches a callback or Future that throws a
-/// [TestFailure] with the given [message].
-///
-/// [message] can be a string or a [Matcher].
-Matcher throwsTestFailure(message) => throwsA(isTestFailure(message));
-
/// Returns a matcher that matches a [TestFailure] with the given [message].
///
/// [message] can be a string or a [Matcher].
Matcher isTestFailure(message) => const TypeMatcher<TestFailure>()
.having((e) => e.message, 'message', message);
-/// Returns a local [LiveTest] that runs [body].
-LiveTest createTest(dynamic Function() body) {
- var test = LocalTest('test', Metadata(chainStackTraces: true), body);
- var suite = Suite(Group.root([test]), suitePlatform, ignoreTimeouts: false);
- return test.load(suite);
-}
-
-/// Runs [body] as a test.
-///
-/// Once it completes, returns the [LiveTest] used to run it.
-Future<LiveTest> runTestBody(dynamic Function() body) async {
- var liveTest = createTest(body);
- await liveTest.run();
- return liveTest;
-}
-
/// Asserts that [liveTest] has completed and passed.
///
/// If the test had any errors, they're surfaced nicely into the outer test.
@@ -131,29 +105,6 @@
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(
- dynamic Function() test, dynamic Function(dynamic) stopBlocking) async {
- late LiveTest liveTest;
- late Future future;
- liveTest = createTest(() {
- var value = test();
- future = pumpEventQueue().then((_) {
- expect(liveTest.state.status, equals(Status.running));
- stopBlocking(value);
- });
- });
-
- await liveTest.run();
- expectTestPassed(liveTest);
- // Ensure that the outer test doesn't complete until the inner future
- // completes.
- return future;
-}
-
/// Runs [body] with a declarer, runs all the declared tests, and asserts that
/// they pass.
///
diff --git a/pkgs/test_api/test/utils_new.dart b/pkgs/test_api/test/utils_new.dart
new file mode 100644
index 0000000..5aa6974
--- /dev/null
+++ b/pkgs/test_api/test/utils_new.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2023, 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.
+
+import 'dart:async';
+
+import 'package:test_api/expect.dart';
+import 'package:test_api/hooks_testing.dart';
+
+/// Asserts that [liveTest] has completed and passed.
+///
+/// If the test had any errors, they're surfaced nicely into the outer test.
+void expectTestPassed(TestCaseMonitor monitor) {
+ // Since the test is expected to pass, we forward any current or future errors
+ // to the running test, because they're definitely unexpected and it is most
+ // useful for the error to point directly to the throw point.
+ for (var error in monitor.errors) {
+ Zone.current.handleUncaughtError(error.error, error.stackTrace);
+ }
+ monitor.onError.listen((error) {
+ Zone.current.handleUncaughtError(error.error, error.stackTrace);
+ });
+
+ expect(monitor.state, State.passed);
+}
+
+/// Asserts that [liveTest] failed with a single [TestFailure] whose message
+/// matches [message].
+void expectTestFailed(TestCaseMonitor monitor, message) {
+ expect(monitor.state, State.failed);
+ expect(monitor.errors, [isAsyncError(isTestFailure(message))]);
+}
+
+/// Returns a matcher that matches a [AsyncError] with an `error` field matching
+/// [errorMatcher].
+Matcher isAsyncError(Matcher errorMatcher) =>
+ isA<AsyncError>().having((e) => e.error, 'error', errorMatcher);
+
+/// Returns a matcher that matches a [TestFailure] with the given [message].
+///
+/// [message] can be a string or a [Matcher].
+Matcher isTestFailure(message) => const TypeMatcher<TestFailure>()
+ .having((e) => e.message, 'message', message);
+
+/// Returns a matcher that matches a callback or Future that throws a
+/// [TestFailure] with the given [message].
+///
+/// [message] can be a string or a [Matcher].
+Matcher throwsTestFailure(message) => throwsA(isTestFailure(message));