From dccf14dd3e316fd00cdb3486a419432310695eda Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 9 Mar 2023 17:38:33 +0000 Subject: [PATCH 1/9] Move to `expect` from `package:matcher` - Remove the dependency on `matcher` from `test_api` and `test_core`. - Re-export from `package:matcher/expect.dart` directly to maintain the same library surface from `package:test/test.dart` and `package:test/expect.dart`. - Delete the `test_api` definition of the code that moved to `package:matcher`. - Remove some documentation about specific matchers from the README. Temporarily add dependency overrides on `matcher` while it is unpublished. --- integration_tests/spawn_hybrid/pubspec.yaml | 4 + integration_tests/wasm/pubspec.yaml | 4 + legacy_tests/nnbd_opted_in/pubspec.yaml | 4 + .../nnbd_opted_in_with_optout/pubspec.yaml | 4 + legacy_tests/nnbd_opted_out/pubspec.yaml | 4 + .../spawn_hybrid_with_optout/pubspec.yaml | 4 + legacy_tests/unit_tests/pubspec.yaml | 4 + pkgs/checks/pubspec_overrides.yaml | 4 + pkgs/test/README.md | 251 +------- pkgs/test/lib/expect.dart | 2 +- pkgs/test/lib/test.dart | 1 + pkgs/test/pubspec.yaml | 3 + pkgs/test/pubspec_overrides.yaml | 4 + pkgs/test_api/CHANGELOG.md | 2 + pkgs/test_api/lib/expect.dart | 61 -- pkgs/test_api/lib/src/backend/invoker.dart | 12 +- .../lib/src/expect/async_matcher.dart | 57 -- pkgs/test_api/lib/src/expect/expect.dart | 145 ----- .../test_api/lib/src/expect/expect_async.dart | 585 ------------------ .../lib/src/expect/future_matchers.dart | 115 ---- .../test_api/lib/src/expect/never_called.dart | 68 -- .../lib/src/expect/prints_matcher.dart | 71 --- .../lib/src/expect/stream_matcher.dart | 194 ------ .../lib/src/expect/stream_matchers.dart | 375 ----------- .../lib/src/expect/throws_matcher.dart | 135 ---- .../lib/src/expect/throws_matchers.dart | 69 --- .../lib/src/expect/util/placeholder.dart | 16 - .../lib/src/expect/util/pretty_print.dart | 47 -- .../lib/src/frontend/async_matcher.dart | 9 - pkgs/test_api/lib/src/frontend/expect.dart | 9 - pkgs/test_api/lib/test_api.dart | 5 - pkgs/test_api/pubspec.yaml | 4 - pkgs/test_api/pubspec_overrides.yaml | 4 + pkgs/test_api/test/backend/invoker_test.dart | 7 +- .../test/frontend/expect_async_test.dart | 388 ------------ pkgs/test_api/test/frontend/expect_test.dart | 35 -- .../frontend/matcher/completion_test.dart | 192 ------ .../test/frontend/matcher/prints_test.dart | 206 ------ .../test/frontend/matcher/throws_test.dart | 271 -------- .../frontend/matcher/throws_type_test.dart | 173 ------ .../test/frontend/never_called_test.dart | 75 --- .../test/frontend/stream_matcher_test.dart | 358 ----------- pkgs/test_api/test/utils_new.dart | 49 -- pkgs/test_core/CHANGELOG.md | 1 + pkgs/test_core/lib/test_core.dart | 4 - pkgs/test_core/pubspec_overrides.yaml | 4 + 46 files changed, 64 insertions(+), 3975 deletions(-) delete mode 100644 pkgs/test_api/lib/expect.dart delete mode 100644 pkgs/test_api/lib/src/expect/async_matcher.dart delete mode 100644 pkgs/test_api/lib/src/expect/expect.dart delete mode 100644 pkgs/test_api/lib/src/expect/expect_async.dart delete mode 100644 pkgs/test_api/lib/src/expect/future_matchers.dart delete mode 100644 pkgs/test_api/lib/src/expect/never_called.dart delete mode 100644 pkgs/test_api/lib/src/expect/prints_matcher.dart delete mode 100644 pkgs/test_api/lib/src/expect/stream_matcher.dart delete mode 100644 pkgs/test_api/lib/src/expect/stream_matchers.dart delete mode 100644 pkgs/test_api/lib/src/expect/throws_matcher.dart delete mode 100644 pkgs/test_api/lib/src/expect/throws_matchers.dart delete mode 100644 pkgs/test_api/lib/src/expect/util/placeholder.dart delete mode 100644 pkgs/test_api/lib/src/expect/util/pretty_print.dart delete mode 100644 pkgs/test_api/lib/src/frontend/async_matcher.dart delete mode 100644 pkgs/test_api/lib/src/frontend/expect.dart delete mode 100644 pkgs/test_api/test/frontend/expect_async_test.dart delete mode 100644 pkgs/test_api/test/frontend/expect_test.dart delete mode 100644 pkgs/test_api/test/frontend/matcher/completion_test.dart delete mode 100644 pkgs/test_api/test/frontend/matcher/prints_test.dart delete mode 100644 pkgs/test_api/test/frontend/matcher/throws_test.dart delete mode 100644 pkgs/test_api/test/frontend/matcher/throws_type_test.dart delete mode 100644 pkgs/test_api/test/frontend/never_called_test.dart delete mode 100644 pkgs/test_api/test/frontend/stream_matcher_test.dart delete mode 100644 pkgs/test_api/test/utils_new.dart diff --git a/integration_tests/spawn_hybrid/pubspec.yaml b/integration_tests/spawn_hybrid/pubspec.yaml index d5b58e1e6..d5cb8d002 100644 --- a/integration_tests/spawn_hybrid/pubspec.yaml +++ b/integration_tests/spawn_hybrid/pubspec.yaml @@ -18,3 +18,7 @@ dependency_overrides: path: ../../pkgs/test_api test_core: path: ../../pkgs/test_core + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3fc9b3c35c3c0465ebef9158971f8203a4fc9416 diff --git a/integration_tests/wasm/pubspec.yaml b/integration_tests/wasm/pubspec.yaml index 145898c0c..e216abafc 100644 --- a/integration_tests/wasm/pubspec.yaml +++ b/integration_tests/wasm/pubspec.yaml @@ -11,3 +11,7 @@ dependency_overrides: path: ../../pkgs/test_api test_core: path: ../../pkgs/test_core + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3fc9b3c35c3c0465ebef9158971f8203a4fc9416 diff --git a/legacy_tests/nnbd_opted_in/pubspec.yaml b/legacy_tests/nnbd_opted_in/pubspec.yaml index 2d7af3d7f..7547ecf79 100644 --- a/legacy_tests/nnbd_opted_in/pubspec.yaml +++ b/legacy_tests/nnbd_opted_in/pubspec.yaml @@ -12,3 +12,7 @@ dependency_overrides: path: ../../pkgs/test_api test_core: path: ../../pkgs/test_core + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3fc9b3c35c3c0465ebef9158971f8203a4fc9416 diff --git a/legacy_tests/nnbd_opted_in_with_optout/pubspec.yaml b/legacy_tests/nnbd_opted_in_with_optout/pubspec.yaml index f75f97d5d..711761295 100644 --- a/legacy_tests/nnbd_opted_in_with_optout/pubspec.yaml +++ b/legacy_tests/nnbd_opted_in_with_optout/pubspec.yaml @@ -12,3 +12,7 @@ dependency_overrides: path: ../../pkgs/test_api test_core: path: ../../pkgs/test_core + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3fc9b3c35c3c0465ebef9158971f8203a4fc9416 diff --git a/legacy_tests/nnbd_opted_out/pubspec.yaml b/legacy_tests/nnbd_opted_out/pubspec.yaml index 9a03e1bc9..37cd7e710 100644 --- a/legacy_tests/nnbd_opted_out/pubspec.yaml +++ b/legacy_tests/nnbd_opted_out/pubspec.yaml @@ -12,3 +12,7 @@ dependency_overrides: path: ../../pkgs/test_api test_core: path: ../../pkgs/test_core + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3fc9b3c35c3c0465ebef9158971f8203a4fc9416 diff --git a/legacy_tests/spawn_hybrid_with_optout/pubspec.yaml b/legacy_tests/spawn_hybrid_with_optout/pubspec.yaml index d02bf9f11..f9c0b2200 100644 --- a/legacy_tests/spawn_hybrid_with_optout/pubspec.yaml +++ b/legacy_tests/spawn_hybrid_with_optout/pubspec.yaml @@ -12,3 +12,7 @@ dependency_overrides: path: ../../pkgs/test_api test_core: path: ../../pkgs/test_core + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3fc9b3c35c3c0465ebef9158971f8203a4fc9416 diff --git a/legacy_tests/unit_tests/pubspec.yaml b/legacy_tests/unit_tests/pubspec.yaml index 4703f1ee6..6da383b0a 100644 --- a/legacy_tests/unit_tests/pubspec.yaml +++ b/legacy_tests/unit_tests/pubspec.yaml @@ -16,3 +16,7 @@ dependency_overrides: path: ../../pkgs/test_api test_core: path: ../../pkgs/test_core + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3fc9b3c35c3c0465ebef9158971f8203a4fc9416 diff --git a/pkgs/checks/pubspec_overrides.yaml b/pkgs/checks/pubspec_overrides.yaml index 7d9aba4d4..d7fd34cee 100644 --- a/pkgs/checks/pubspec_overrides.yaml +++ b/pkgs/checks/pubspec_overrides.yaml @@ -5,3 +5,7 @@ dependency_overrides: path: ../test_core test: path: ../test + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3b48930a01f678e2921cea16563af67460e6f765 diff --git a/pkgs/test/README.md b/pkgs/test/README.md index 1dd8f69d7..35b6296af 100644 --- a/pkgs/test/README.md +++ b/pkgs/test/README.md @@ -35,12 +35,12 @@ ## Writing Tests -Tests are specified using the top-level [`test()`] function, and test assertions -are made using [`expect()`]: +Tests are specified using the top-level [`test()`] function. +Test asserts can be made using [`expect` from `package:matcher`][expect] [`test()`]: https://pub.dev/documentation/test_core/latest/test_core.scaffolding/test.html -[`expect()`]: https://pub.dev/documentation/test_api/latest/expect/expect.html +[expect]: https://pub.dev/documentation/matcher/latest/expect/expect.html ```dart import 'package:test/test.dart'; @@ -91,68 +91,6 @@ void main() { } ``` -Any matchers from the [`matcher`] package can be used with `expect()` to do -complex validations: - -[`matcher`]: https://pub.dev/documentation/matcher/latest/matcher/matcher-library.html - -```dart -import 'package:test/test.dart'; - -void main() { - test('.split() splits the string on the delimiter', () { - expect('foo,bar,baz', allOf([ - contains('foo'), - isNot(startsWith('bar')), - endsWith('baz') - ])); - }); -} -``` - -You can also test exceptions with the [`throwsA()`] function or a matcher -such as [`throwsFormatException`]: - -[`throwsA()`]: https://pub.dev/documentation/test_api/latest/expect/throwsA.html - -[`throwsFormatException`]: https://pub.dev/documentation/test_api/latest/expect/throwsFormatException-constant.html - -```dart -import 'package:test/test.dart'; - -void main() { - test('.parse() fails on invalid input', () { - expect(() => int.parse('X'), throwsFormatException); - }); -} -``` - -You can use the [`setUp()`] and [`tearDown()`] functions to share code between -tests. The `setUp()` callback will run before every test in a group or test -suite, and `tearDown()` will run after. `tearDown()` will run even if a test -fails, to ensure that it has a chance to clean up after itself. - -```dart -import 'package:test/test.dart'; - -void main() { - late HttpServer server; - late Uri url; - setUp(() async { - server = await HttpServer.bind('localhost', 0); - url = Uri.parse('http://${server.address.host}:${server.port}'); - }); - - tearDown(() async { - await server.close(force: true); - server = null; - url = null; - }); - - // ... -} -``` - [`setUp()`]: https://pub.dev/documentation/test_core/latest/test_core.scaffolding/setUp.html [`tearDown()`]: https://pub.dev/documentation/test_core/latest/test_core.scaffolding/tearDown.html @@ -478,189 +416,6 @@ Avoid uncaught async errors by ensuring that all futures have an error handler [early-handler]:https://dart.dev/guides/libraries/futures-error-handling#potential-problem-failing-to-register-error-handlers-early -### Future Matchers - -There are a number of useful functions and matchers for more advanced -asynchrony. The [`completion()`] matcher can be used to test `Futures`; it -ensures that the test doesn't finish until the `Future` completes, and runs a -matcher against that `Future`'s value. - -[`completion()`]: https://pub.dev/documentation/test_api/latest/expect/completion.html - -```dart -import 'dart:async'; - -import 'package:test/test.dart'; - -void main() { - test('Future.value() returns the value', () { - expect(Future.value(10), completion(equals(10))); - }); -} -``` - -The [`throwsA()`] matcher and the various [`throwsExceptionType`] matchers work -with both synchronous callbacks and asynchronous `Future`s. They ensure that a -particular type of exception is thrown: - -[`throwsExceptionType`]: https://pub.dev/documentation/test_api/latest/expect/throwsException-constant.html - -```dart -import 'dart:async'; - -import 'package:test/test.dart'; - -void main() { - test('Future.error() throws the error', () { - expect(Future.error('oh no'), throwsA(equals('oh no'))); - expect(Future.error(StateError('bad state')), throwsStateError); - }); -} -``` - -The [`expectAsync()`] function wraps another function and has two jobs. First, -it asserts that the wrapped function is called a certain number of times, and -will cause the test to fail if it's called too often; second, it keeps the test -from finishing until the function is called the requisite number of times. - -```dart -import 'dart:async'; - -import 'package:test/test.dart'; - -void main() { - test('Stream.fromIterable() emits the values in the iterable', () { - var stream = Stream.fromIterable([1, 2, 3]); - - stream.listen(expectAsync1((number) { - expect(number, inInclusiveRange(1, 3)); - }, count: 3)); - }); -} -``` - -[`expectAsync()`]: https://pub.dev/documentation/test_api/latest/test_api/expectAsync.html - -### Stream Matchers - -The `test` package provides a suite of powerful matchers for dealing with -[asynchronous streams][Stream]. They're expressive and composable, and make it -easy to write complex expectations about the values emitted by a stream. For -example: - -[Stream]: https://api.dart.dev/stable/dart-async/Stream-class.html - -```dart -import 'dart:async'; - -import 'package:test/test.dart'; - -void main() { - test('process emits status messages', () { - // Dummy data to mimic something that might be emitted by a process. - var stdoutLines = Stream.fromIterable([ - 'Ready.', - 'Loading took 150ms.', - 'Succeeded!' - ]); - - expect(stdoutLines, emitsInOrder([ - // Values match individual events. - 'Ready.', - - // Matchers also run against individual events. - startsWith('Loading took'), - - // Stream matchers can be nested. This asserts that one of two events are - // emitted after the "Loading took" line. - emitsAnyOf(['Succeeded!', 'Failed!']), - - // By default, more events are allowed after the matcher finishes - // matching. This asserts instead that the stream emits a done event and - // nothing else. - emitsDone - ])); - }); -} -``` - -A stream matcher can also match the [`async`] package's [`StreamQueue`] class, -which allows events to be requested from a stream rather than pushed to the -consumer. The matcher will consume the matched events, but leave the rest of the -queue alone so that it can still be used by the test, unlike a normal `Stream` -which can only have one subscriber. For example: - -[`async`]: https://pub.dev/packages/async - -[`StreamQueue`]: https://pub.dev/documentation/async/latest/async/StreamQueue-class.html - -```dart -import 'dart:async'; - -import 'package:async/async.dart'; -import 'package:test/test.dart'; - -void main() { - test('process emits a WebSocket URL', () async { - // Wrap the Stream in a StreamQueue so that we can request events. - var stdout = StreamQueue(Stream.fromIterable([ - 'WebSocket URL:', - 'ws://localhost:1234/', - 'Waiting for connection...' - ])); - - // Ignore lines from the process until it's about to emit the URL. - await expectLater(stdout, emitsThrough('WebSocket URL:')); - - // Parse the next line as a URL. - var url = Uri.parse(await stdout.next); - expect(url.host, equals('localhost')); - - // You can match against the same StreamQueue multiple times. - await expectLater(stdout, emits('Waiting for connection...')); - }); -} -``` - -The following built-in stream matchers are available: - -* [`emits()`] matches a single data event. -* [`emitsError()`] matches a single error event. -* [`emitsDone`] matches a single done event. -* [`mayEmit()`] consumes events if they match an inner matcher, without - requiring them to match. -* [`mayEmitMultiple()`] works like `mayEmit()`, but it matches events against - the matcher as many times as possible. -* [`emitsAnyOf()`] consumes events matching one (or more) of several possible - matchers. -* [`emitsInOrder()`] consumes events matching multiple matchers in a row. -* [`emitsInAnyOrder()`] works like `emitsInOrder()`, but it allows the matchers - to match in any order. -* [`neverEmits()`] matches a stream that finishes *without* matching an inner - matcher. - -You can also define your own custom stream matchers with [`StreamMatcher()`]. - -[`emits()`]: https://pub.dev/documentation/test_api/latest/expect/emits.html - -[`emitsError()`]: https://pub.dev/documentation/test_api/latest/expect/emitsError.html - -[`emitsDone`]: https://pub.dev/documentation/test_api/latest/expect/emitsDone.html - -[`mayEmit()`]: https://pub.dev/documentation/test_api/latest/expect/mayEmit.html - -[`mayEmitMultiple()`]: https://pub.dev/documentation/test_api/latest/expect/mayEmitMultiple.html - -[`emitsAnyOf()`]: https://pub.dev/documentation/test_api/latest/expect/emitsAnyOf.html - -[`emitsInOrder()`]: https://pub.dev/documentation/test_api/latest/expect/emitsInOrder.html - -[`emitsInAnyOrder()`]: https://pub.dev/documentation/test_api/latest/expect/emitsInAnyOrder.html - -[`neverEmits()`]: https://pub.dev/documentation/test_api/latest/expect/neverEmits.html - -[`StreamMatcher()`]: https://pub.dev/documentation/test_api/latest/expect/StreamMatcher-class.html - ## Running Tests With Custom HTML By default, the test runner will generate its own empty HTML file for browser diff --git a/pkgs/test/lib/expect.dart b/pkgs/test/lib/expect.dart index 28f64f7c4..5e62622fe 100644 --- a/pkgs/test/lib/expect.dart +++ b/pkgs/test/lib/expect.dart @@ -2,4 +2,4 @@ // 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. -export 'package:test_api/expect.dart'; +export 'package:matcher/expect.dart'; diff --git a/pkgs/test/lib/test.dart b/pkgs/test/lib/test.dart index 961dae172..97a62b0ce 100644 --- a/pkgs/test/lib/test.dart +++ b/pkgs/test/lib/test.dart @@ -2,5 +2,6 @@ // 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. +export 'package:matcher/expect.dart'; // ignore: deprecated_member_use export 'package:test_core/test_core.dart'; diff --git a/pkgs/test/pubspec.yaml b/pkgs/test/pubspec.yaml index b9ae53f50..b30df97f6 100644 --- a/pkgs/test/pubspec.yaml +++ b/pkgs/test/pubspec.yaml @@ -34,6 +34,9 @@ dependencies: # Use an exact version until the test_api and test_core package are stable. test_api: 0.5.0 test_core: 0.5.0 + # Use a tight version constraint to ensure that a constraint on matcher + # properly constrains all features it provides. + matcher: '>=0.12.15 <0.12.16' dev_dependencies: fake_async: ^1.0.0 diff --git a/pkgs/test/pubspec_overrides.yaml b/pkgs/test/pubspec_overrides.yaml index d02d71dea..3b714dc34 100644 --- a/pkgs/test/pubspec_overrides.yaml +++ b/pkgs/test/pubspec_overrides.yaml @@ -3,3 +3,7 @@ dependency_overrides: path: ../test_core test_api: path: ../test_api + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3fc9b3c35c3c0465ebef9158971f8203a4fc9416 diff --git a/pkgs/test_api/CHANGELOG.md b/pkgs/test_api/CHANGELOG.md index 0b5dd8474..5c6394329 100644 --- a/pkgs/test_api/CHANGELOG.md +++ b/pkgs/test_api/CHANGELOG.md @@ -8,6 +8,8 @@ to `Runtime`. * Add `package:test_api/hooks_testing.dart` library for writing tests against code that uses `package:test_api/hooks.dart`. +* **BREAKING** Remove the `package:test_api/expect.dart` library and stop + exporting any of the old APIs it had from the main library. ## 0.4.18 diff --git a/pkgs/test_api/lib/expect.dart b/pkgs/test_api/lib/expect.dart deleted file mode 100644 index 30a807158..000000000 --- a/pkgs/test_api/lib/expect.dart +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2021, 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. - -export 'package:matcher/matcher.dart'; - -export 'src/expect/expect.dart' show expect, expectLater, fail; -export 'src/expect/expect_async.dart' - show - Func0, - Func1, - Func2, - Func3, - Func4, - Func5, - Func6, - expectAsync0, - expectAsync1, - expectAsync2, - expectAsync3, - expectAsync4, - expectAsync5, - expectAsync6, - expectAsyncUntil0, - expectAsyncUntil1, - expectAsyncUntil2, - expectAsyncUntil3, - expectAsyncUntil4, - expectAsyncUntil5, - expectAsyncUntil6; -export 'src/expect/future_matchers.dart' - show completes, completion, doesNotComplete; -export 'src/expect/never_called.dart' show neverCalled; -export 'src/expect/prints_matcher.dart' show prints; -export 'src/expect/stream_matcher.dart' show StreamMatcher; -export 'src/expect/stream_matchers.dart' - show - emitsDone, - emits, - emitsError, - mayEmit, - emitsAnyOf, - emitsInOrder, - emitsInAnyOrder, - emitsThrough, - mayEmitMultiple, - neverEmits; -export 'src/expect/throws_matcher.dart' show throwsA; -export 'src/expect/throws_matchers.dart' - show - throwsArgumentError, - throwsConcurrentModificationError, - throwsCyclicInitializationError, - throwsException, - throwsFormatException, - throwsNoSuchMethodError, - throwsNullThrownError, - throwsRangeError, - throwsStateError, - throwsUnimplementedError, - throwsUnsupportedError; diff --git a/pkgs/test_api/lib/src/backend/invoker.dart b/pkgs/test_api/lib/src/backend/invoker.dart index 582d9f96c..d223ec159 100644 --- a/pkgs/test_api/lib/src/backend/invoker.dart +++ b/pkgs/test_api/lib/src/backend/invoker.dart @@ -305,9 +305,9 @@ class Invoker { // Set the state explicitly so we don't get an extra error about the test // failing after being complete. _controller.setState(const State(Status.complete, Result.error)); - throw 'This test was marked as skipped after it had already completed. ' - 'Make sure to use\n' - '[expectAsync] or the [completes] matcher when testing async code.'; + throw 'This test was marked as skipped after it had already completed.\n' + 'Make sure to use a matching library which informs the test runner ' + 'of pending async work.'; } if (message != null) _controller.message(Message.skip(message)); @@ -367,9 +367,9 @@ class Invoker { _handleError( zone, - 'This test failed after it had already completed. Make sure to use ' - '[expectAsync]\n' - 'or the [completes] matcher when testing async code.', + 'This test failed after it had already completed.\n' + 'Make sure to use a matching library which informs the test runner ' + 'of pending async work.', stackTrace); } diff --git a/pkgs/test_api/lib/src/expect/async_matcher.dart b/pkgs/test_api/lib/src/expect/async_matcher.dart deleted file mode 100644 index a3cc96500..000000000 --- a/pkgs/test_api/lib/src/expect/async_matcher.dart +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2017, 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 'package:matcher/matcher.dart'; -import 'package:test_api/hooks.dart'; - -import 'expect.dart'; - -/// A matcher that does asynchronous computation. -/// -/// Rather than implementing [matches], subclasses implement [matchAsync]. -/// [AsyncMatcher.matches] ensures that the test doesn't complete until the -/// returned future completes, and [expect] returns a future that completes when -/// the returned future completes so that tests can wait for it. -abstract class AsyncMatcher extends Matcher { - const AsyncMatcher(); - - /// Returns `null` if this matches [item], or a [String] description of the - /// failure if it doesn't match. - /// - /// This can return a [Future] or a synchronous value. If it returns a - /// [Future], neither [expect] nor the test will complete until that [Future] - /// completes. - /// - /// If this returns a [String] synchronously, [expect] will synchronously - /// throw a [TestFailure] and [matches] will synchronously return `false`. - dynamic /*FutureOr*/ matchAsync(item); - - @override - bool matches(item, Map matchState) { - final result = matchAsync(item); - expect(result, - anyOf([equals(null), TypeMatcher(), TypeMatcher()]), - reason: 'matchAsync() may only return a String, a Future, or null.'); - - if (result is Future) { - final outstandingWork = TestHandle.current.markPending(); - result.then((realResult) { - if (realResult != null) { - fail(formatFailure(this, item, realResult as String)); - } - outstandingWork.complete(); - }); - } else if (result is String) { - matchState[this] = result; - return false; - } - - return true; - } - - @override - Description describeMismatch(item, Description mismatchDescription, - Map matchState, bool verbose) => - StringDescription(matchState[this] as String); -} diff --git a/pkgs/test_api/lib/src/expect/expect.dart b/pkgs/test_api/lib/src/expect/expect.dart deleted file mode 100644 index 377300026..000000000 --- a/pkgs/test_api/lib/src/expect/expect.dart +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2015, 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 'package:matcher/matcher.dart'; -import 'package:test_api/hooks.dart'; - -import 'async_matcher.dart'; -import 'util/pretty_print.dart'; - -/// The type used for functions that can be used to build up error reports -/// upon failures in [expect]. -@Deprecated('Will be removed in 0.13.0.') -typedef ErrorFormatter = String Function(dynamic actual, Matcher matcher, - String? reason, Map matchState, bool verbose); - -/// Assert that [actual] matches [matcher]. -/// -/// This is the main assertion function. [reason] is optional and is typically -/// not supplied, as a reason is generated from [matcher]; if [reason] -/// is included it is appended to the reason generated by the matcher. -/// -/// [matcher] can be a value in which case it will be wrapped in an -/// [equals] matcher. -/// -/// If the assertion fails a [TestFailure] is thrown. -/// -/// If [skip] is a String or `true`, the assertion is skipped. The arguments are -/// still evaluated, but [actual] is not verified to match [matcher]. If -/// [actual] is a [Future], the test won't complete until the future emits a -/// value. -/// -/// If [skip] is a string, it should explain why the assertion is skipped; this -/// reason will be printed when running the test. -/// -/// Certain matchers, like [completion] and [throwsA], either match or fail -/// asynchronously. When you use [expect] with these matchers, it ensures that -/// the test doesn't complete until the matcher has either matched or failed. If -/// you want to wait for the matcher to complete before continuing the test, you -/// can call [expectLater] instead and `await` the result. -void expect(actual, matcher, - {String? reason, - skip, - @Deprecated('Will be removed in 0.13.0.') bool verbose = false, - @Deprecated('Will be removed in 0.13.0.') ErrorFormatter? formatter}) { - _expect(actual, matcher, - reason: reason, skip: skip, verbose: verbose, formatter: formatter); -} - -/// Just like [expect], but returns a [Future] that completes when the matcher -/// has finished matching. -/// -/// For the [completes] and [completion] matchers, as well as [throwsA] and -/// related matchers when they're matched against a [Future], the returned -/// future completes when the matched future completes. For the [prints] -/// matcher, it completes when the future returned by the callback completes. -/// Otherwise, it completes immediately. -/// -/// If the matcher fails asynchronously, that failure is piped to the returned -/// future where it can be handled by user code. -Future expectLater(actual, matcher, {String? reason, skip}) => - _expect(actual, matcher, reason: reason, skip: skip); - -/// The implementation of [expect] and [expectLater]. -Future _expect(actual, matcher, - {String? reason, skip, bool verbose = false, ErrorFormatter? formatter}) { - final test = TestHandle.current; - formatter ??= (actual, matcher, reason, matchState, verbose) { - var mismatchDescription = StringDescription(); - matcher.describeMismatch(actual, mismatchDescription, matchState, verbose); - - return formatFailure(matcher, actual, mismatchDescription.toString(), - reason: reason); - }; - - if (skip != null && skip is! bool && skip is! String) { - throw ArgumentError.value(skip, 'skip', 'must be a bool or a String'); - } - - matcher = wrapMatcher(matcher); - if (skip != null && skip != false) { - String message; - if (skip is String) { - message = 'Skip expect: $skip'; - } else if (reason != null) { - message = 'Skip expect ($reason).'; - } else { - var description = StringDescription().addDescriptionOf(matcher); - message = 'Skip expect ($description).'; - } - - test.markSkipped(message); - return Future.sync(() {}); - } - - if (matcher is AsyncMatcher) { - // Avoid async/await so that expect() throws synchronously when possible. - var result = matcher.matchAsync(actual); - expect(result, - anyOf([equals(null), TypeMatcher(), TypeMatcher()]), - reason: 'matchAsync() may only return a String, a Future, or null.'); - - if (result is String) { - fail(formatFailure(matcher, actual, result, reason: reason)); - } else if (result is Future) { - final outstandingWork = test.markPending(); - return result.then((realResult) { - if (realResult == null) return; - fail(formatFailure(matcher as Matcher, actual, realResult as String, - reason: reason)); - }).whenComplete(() { - // Always remove this, in case the failure is caught and handled - // gracefully. - outstandingWork.complete(); - }); - } - - return Future.sync(() {}); - } - - var matchState = {}; - try { - if ((matcher as Matcher).matches(actual, matchState)) { - return Future.sync(() {}); - } - } catch (e, trace) { - reason ??= '$e at $trace'; - } - fail(formatter(actual, matcher as Matcher, reason, matchState, verbose)); -} - -/// Convenience method for throwing a new [TestFailure] with the provided -/// [message]. -Never fail(String message) => throw TestFailure(message); - -// The default error formatter. -@Deprecated('Will be removed in 0.13.0.') -String formatFailure(Matcher expected, actual, String which, {String? reason}) { - var buffer = StringBuffer(); - buffer.writeln(indent(prettyPrint(expected), first: 'Expected: ')); - buffer.writeln(indent(prettyPrint(actual), first: ' Actual: ')); - if (which.isNotEmpty) buffer.writeln(indent(which, first: ' Which: ')); - if (reason != null) buffer.writeln(reason); - return buffer.toString(); -} diff --git a/pkgs/test_api/lib/src/expect/expect_async.dart b/pkgs/test_api/lib/src/expect/expect_async.dart deleted file mode 100644 index dee7c1e3b..000000000 --- a/pkgs/test_api/lib/src/expect/expect_async.dart +++ /dev/null @@ -1,585 +0,0 @@ -// Copyright (c) 2015, 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 'package:test_api/hooks.dart'; - -import 'util/placeholder.dart'; - -// Function types returned by expectAsync# methods. - -typedef Func0 = T Function(); -typedef Func1 = T Function([A a]); -typedef Func2 = T Function([A a, B b]); -typedef Func3 = T Function([A a, B b, C c]); -typedef Func4 = T Function([A a, B b, C c, D d]); -typedef Func5 = T Function([A a, B b, C c, D d, E e]); -typedef Func6 = T Function([A a, B b, C c, D d, E e, F f]); - -/// A wrapper for a function that ensures that it's called the appropriate -/// number of times. -/// -/// The containing test won't be considered to have completed successfully until -/// this function has been called the appropriate number of times. -/// -/// The wrapper function is accessible via [func]. It supports up to six -/// optional and/or required positional arguments, but no named arguments. -class _ExpectedFunction { - /// The wrapped callback. - final Function _callback; - - /// The minimum number of calls that are expected to be made to the function. - /// - /// If fewer calls than this are made, the test will fail. - final int _minExpectedCalls; - - /// The maximum number of calls that are expected to be made to the function. - /// - /// If more calls than this are made, the test will fail. - final int _maxExpectedCalls; - - /// A callback that should return whether the function is not expected to have - /// any more calls. - /// - /// This will be called after every time the function is run. The test case - /// won't be allowed to terminate until it returns `true`. - /// - /// This may be `null`. If so, the function is considered to be done after - /// it's been run once. - final bool Function()? _isDone; - - /// A descriptive name for the function. - final String _id; - - /// An optional description of why the function is expected to be called. - /// - /// If not passed, this will be an empty string. - final String _reason; - - /// The number of times the function has been called. - int _actualCalls = 0; - - /// The test in which this function was wrapped. - late final TestHandle _test; - - /// Whether this function has been called the requisite number of times. - late bool _complete; - - OutstandingWork? _outstandingWork; - - /// Wraps [callback] in a function that asserts that it's called at least - /// [minExpected] times and no more than [maxExpected] times. - /// - /// If passed, [id] is used as a descriptive name fo the function and [reason] - /// as a reason it's expected to be called. If [isDone] is passed, the test - /// won't be allowed to complete until it returns `true`. - _ExpectedFunction(Function callback, int minExpected, int maxExpected, - {String? id, String? reason, bool Function()? isDone}) - : _callback = callback, - _minExpectedCalls = minExpected, - _maxExpectedCalls = - (maxExpected == 0 && minExpected > 0) ? minExpected : maxExpected, - _isDone = isDone, - _reason = reason == null ? '' : '\n$reason', - _id = _makeCallbackId(id, callback) { - try { - _test = TestHandle.current; - } on OutsideTestException { - throw StateError('`expectAsync` must be called within a test.'); - } - - if (maxExpected > 0 && minExpected > maxExpected) { - throw ArgumentError('max ($maxExpected) may not be less than count ' - '($minExpected).'); - } - - if (isDone != null || minExpected > 0) { - _outstandingWork = _test.markPending(); - _complete = false; - } else { - _complete = true; - } - } - - /// Tries to find a reasonable name for [callback]. - /// - /// If [id] is passed, uses that. Otherwise, tries to determine a name from - /// calling `toString`. If no name can be found, returns the empty string. - static String _makeCallbackId(String? id, Function callback) { - if (id != null) return '$id '; - - // If the callback is not an anonymous closure, try to get the - // name. - var toString = callback.toString(); - var prefix = "Function '"; - var start = toString.indexOf(prefix); - if (start == -1) return ''; - - start += prefix.length; - var end = toString.indexOf("'", start); - if (end == -1) return ''; - return '${toString.substring(start, end)} '; - } - - /// Returns a function that has the same number of positional arguments as the - /// wrapped function (up to a total of 6). - Function get func { - if (_callback is Function(Never, Never, Never, Never, Never, Never)) { - return max6; - } - if (_callback is Function(Never, Never, Never, Never, Never)) return max5; - if (_callback is Function(Never, Never, Never, Never)) return max4; - if (_callback is Function(Never, Never, Never)) return max3; - if (_callback is Function(Never, Never)) return max2; - if (_callback is Function(Never)) return max1; - if (_callback is Function()) return max0; - - _outstandingWork?.complete(); - throw ArgumentError( - 'The wrapped function has more than 6 required arguments'); - } - - // This indirection is critical. It ensures the returned function has an - // argument count of zero. - T max0() => max6(); - - T max1([Object? a0 = placeholder]) => max6(a0); - - T max2([Object? a0 = placeholder, Object? a1 = placeholder]) => max6(a0, a1); - - T max3( - [Object? a0 = placeholder, - Object? a1 = placeholder, - Object? a2 = placeholder]) => - max6(a0, a1, a2); - - T max4( - [Object? a0 = placeholder, - Object? a1 = placeholder, - Object? a2 = placeholder, - Object? a3 = placeholder]) => - max6(a0, a1, a2, a3); - - T max5( - [Object? a0 = placeholder, - Object? a1 = placeholder, - Object? a2 = placeholder, - Object? a3 = placeholder, - Object? a4 = placeholder]) => - max6(a0, a1, a2, a3, a4); - - T max6( - [Object? a0 = placeholder, - Object? a1 = placeholder, - Object? a2 = placeholder, - Object? a3 = placeholder, - Object? a4 = placeholder, - Object? a5 = placeholder]) => - _run([a0, a1, a2, a3, a4, a5].where((a) => a != placeholder)); - - /// Runs the wrapped function with [args] and returns its return value. - T _run(Iterable args) { - // Note that in the old test, this returned `null` if it encountered an - // error, where now it just re-throws that error because Zone machinery will - // pass it to the invoker anyway. - try { - _actualCalls++; - if (_test.shouldBeDone) { - throw 'Callback ${_id}called ($_actualCalls) after test case ' - '${_test.name} had already completed.$_reason'; - } else if (_maxExpectedCalls >= 0 && _actualCalls > _maxExpectedCalls) { - throw TestFailure('Callback ${_id}called more times than expected ' - '($_maxExpectedCalls).$_reason'); - } - - return Function.apply(_callback, args.toList()) as T; - } finally { - _afterRun(); - } - } - - /// After each time the function is run, check to see if it's complete. - void _afterRun() { - if (_complete) return; - if (_minExpectedCalls > 0 && _actualCalls < _minExpectedCalls) return; - if (_isDone != null && !_isDone!()) return; - - // Mark this callback as complete and remove it from the test case's - // outstanding callback count; if that hits zero the test is done. - _complete = true; - _outstandingWork?.complete(); - } -} - -/// This function is deprecated because it doesn't work well with strong mode. -/// Use [expectAsync0], [expectAsync1], -/// [expectAsync2], [expectAsync3], [expectAsync4], [expectAsync5], or -/// [expectAsync6] instead. -@Deprecated('Will be removed in 0.13.0') -Function expectAsync(Function callback, - {int count = 1, int max = 0, String? id, String? reason}) => - _ExpectedFunction(callback, count, max, id: id, reason: reason).func; - -/// Informs the framework that the given [callback] of arity 0 is expected to be -/// called [count] number of times (by default 1). -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// The test framework will wait for the callback to run the [count] times -/// before it considers the current test to be complete. -/// -/// [max] can be used to specify an upper bound on the number of calls; if this -/// is exceeded the test will fail. If [max] is `0` (the default), the callback -/// is expected to be called exactly [count] times. If [max] is `-1`, the -/// callback is allowed to be called any number of times greater than [count]. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with zero arguments. See also -/// [expectAsync1], [expectAsync2], [expectAsync3], [expectAsync4], -/// [expectAsync5], and [expectAsync6] for callbacks with different arity. -Func0 expectAsync0(T Function() callback, - {int count = 1, int max = 0, String? id, String? reason}) => - _ExpectedFunction(callback, count, max, id: id, reason: reason).max0; - -/// Informs the framework that the given [callback] of arity 1 is expected to be -/// called [count] number of times (by default 1). -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// The test framework will wait for the callback to run the [count] times -/// before it considers the current test to be complete. -/// -/// [max] can be used to specify an upper bound on the number of calls; if this -/// is exceeded the test will fail. If [max] is `0` (the default), the callback -/// is expected to be called exactly [count] times. If [max] is `-1`, the -/// callback is allowed to be called any number of times greater than [count]. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with one argument. See also -/// [expectAsync0], [expectAsync2], [expectAsync3], [expectAsync4], -/// [expectAsync5], and [expectAsync6] for callbacks with different arity. -Func1 expectAsync1(T Function(A) callback, - {int count = 1, int max = 0, String? id, String? reason}) => - _ExpectedFunction(callback, count, max, id: id, reason: reason).max1; - -/// Informs the framework that the given [callback] of arity 2 is expected to be -/// called [count] number of times (by default 1). -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// The test framework will wait for the callback to run the [count] times -/// before it considers the current test to be complete. -/// -/// [max] can be used to specify an upper bound on the number of calls; if this -/// is exceeded the test will fail. If [max] is `0` (the default), the callback -/// is expected to be called exactly [count] times. If [max] is `-1`, the -/// callback is allowed to be called any number of times greater than [count]. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with two arguments. See also -/// [expectAsync0], [expectAsync1], [expectAsync3], [expectAsync4], -/// [expectAsync5], and [expectAsync6] for callbacks with different arity. -Func2 expectAsync2(T Function(A, B) callback, - {int count = 1, int max = 0, String? id, String? reason}) => - _ExpectedFunction(callback, count, max, id: id, reason: reason).max2; - -/// Informs the framework that the given [callback] of arity 3 is expected to be -/// called [count] number of times (by default 1). -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// The test framework will wait for the callback to run the [count] times -/// before it considers the current test to be complete. -/// -/// [max] can be used to specify an upper bound on the number of calls; if this -/// is exceeded the test will fail. If [max] is `0` (the default), the callback -/// is expected to be called exactly [count] times. If [max] is `-1`, the -/// callback is allowed to be called any number of times greater than [count]. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with three arguments. See also -/// [expectAsync0], [expectAsync1], [expectAsync2], [expectAsync4], -/// [expectAsync5], and [expectAsync6] for callbacks with different arity. -Func3 expectAsync3(T Function(A, B, C) callback, - {int count = 1, int max = 0, String? id, String? reason}) => - _ExpectedFunction(callback, count, max, id: id, reason: reason).max3; - -/// Informs the framework that the given [callback] of arity 4 is expected to be -/// called [count] number of times (by default 1). -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// The test framework will wait for the callback to run the [count] times -/// before it considers the current test to be complete. -/// -/// [max] can be used to specify an upper bound on the number of calls; if this -/// is exceeded the test will fail. If [max] is `0` (the default), the callback -/// is expected to be called exactly [count] times. If [max] is `-1`, the -/// callback is allowed to be called any number of times greater than [count]. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with four arguments. See also -/// [expectAsync0], [expectAsync1], [expectAsync2], [expectAsync3], -/// [expectAsync5], and [expectAsync6] for callbacks with different arity. -Func4 expectAsync4( - T Function(A, B, C, D) callback, - {int count = 1, - int max = 0, - String? id, - String? reason}) => - _ExpectedFunction(callback, count, max, id: id, reason: reason).max4; - -/// Informs the framework that the given [callback] of arity 5 is expected to be -/// called [count] number of times (by default 1). -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// The test framework will wait for the callback to run the [count] times -/// before it considers the current test to be complete. -/// -/// [max] can be used to specify an upper bound on the number of calls; if this -/// is exceeded the test will fail. If [max] is `0` (the default), the callback -/// is expected to be called exactly [count] times. If [max] is `-1`, the -/// callback is allowed to be called any number of times greater than [count]. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with five arguments. See also -/// [expectAsync0], [expectAsync1], [expectAsync2], [expectAsync3], -/// [expectAsync4], and [expectAsync6] for callbacks with different arity. -Func5 expectAsync5( - T Function(A, B, C, D, E) callback, - {int count = 1, - int max = 0, - String? id, - String? reason}) => - _ExpectedFunction(callback, count, max, id: id, reason: reason).max5; - -/// Informs the framework that the given [callback] of arity 6 is expected to be -/// called [count] number of times (by default 1). -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// The test framework will wait for the callback to run the [count] times -/// before it considers the current test to be complete. -/// -/// [max] can be used to specify an upper bound on the number of calls; if this -/// is exceeded the test will fail. If [max] is `0` (the default), the callback -/// is expected to be called exactly [count] times. If [max] is `-1`, the -/// callback is allowed to be called any number of times greater than [count]. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with six arguments. See also -/// [expectAsync0], [expectAsync1], [expectAsync2], [expectAsync3], -/// [expectAsync4], and [expectAsync5] for callbacks with different arity. -Func6 expectAsync6( - T Function(A, B, C, D, E, F) callback, - {int count = 1, - int max = 0, - String? id, - String? reason}) => - _ExpectedFunction(callback, count, max, id: id, reason: reason).max6; - -/// This function is deprecated because it doesn't work well with strong mode. -/// Use [expectAsyncUntil0], [expectAsyncUntil1], -/// [expectAsyncUntil2], [expectAsyncUntil3], [expectAsyncUntil4], -/// [expectAsyncUntil5], or [expectAsyncUntil6] instead. -@Deprecated('Will be removed in 0.13.0') -Function expectAsyncUntil(Function callback, bool Function() isDone, - {String? id, String? reason}) => - _ExpectedFunction(callback, 0, -1, id: id, reason: reason, isDone: isDone) - .func; - -/// Informs the framework that the given [callback] of arity 0 is expected to be -/// called until [isDone] returns true. -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// [isDone] is called after each time the function is run. Only when it returns -/// true will the callback be considered complete. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with zero arguments. See also -/// [expectAsyncUntil1], [expectAsyncUntil2], [expectAsyncUntil3], -/// [expectAsyncUntil4], [expectAsyncUntil5], and [expectAsyncUntil6] for -/// callbacks with different arity. -Func0 expectAsyncUntil0(T Function() callback, bool Function() isDone, - {String? id, String? reason}) => - _ExpectedFunction(callback, 0, -1, - id: id, reason: reason, isDone: isDone) - .max0; - -/// Informs the framework that the given [callback] of arity 1 is expected to be -/// called until [isDone] returns true. -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// [isDone] is called after each time the function is run. Only when it returns -/// true will the callback be considered complete. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with one argument. See also -/// [expectAsyncUntil0], [expectAsyncUntil2], [expectAsyncUntil3], -/// [expectAsyncUntil4], [expectAsyncUntil5], and [expectAsyncUntil6] for -/// callbacks with different arity. -Func1 expectAsyncUntil1( - T Function(A) callback, bool Function() isDone, - {String? id, String? reason}) => - _ExpectedFunction(callback, 0, -1, - id: id, reason: reason, isDone: isDone) - .max1; - -/// Informs the framework that the given [callback] of arity 2 is expected to be -/// called until [isDone] returns true. -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// [isDone] is called after each time the function is run. Only when it returns -/// true will the callback be considered complete. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with two arguments. See also -/// [expectAsyncUntil0], [expectAsyncUntil1], [expectAsyncUntil3], -/// [expectAsyncUntil4], [expectAsyncUntil5], and [expectAsyncUntil6] for -/// callbacks with different arity. -Func2 expectAsyncUntil2( - T Function(A, B) callback, bool Function() isDone, - {String? id, String? reason}) => - _ExpectedFunction(callback, 0, -1, - id: id, reason: reason, isDone: isDone) - .max2; - -/// Informs the framework that the given [callback] of arity 3 is expected to be -/// called until [isDone] returns true. -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// [isDone] is called after each time the function is run. Only when it returns -/// true will the callback be considered complete. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with three arguments. See also -/// [expectAsyncUntil0], [expectAsyncUntil1], [expectAsyncUntil2], -/// [expectAsyncUntil4], [expectAsyncUntil5], and [expectAsyncUntil6] for -/// callbacks with different arity. -Func3 expectAsyncUntil3( - T Function(A, B, C) callback, bool Function() isDone, - {String? id, String? reason}) => - _ExpectedFunction(callback, 0, -1, - id: id, reason: reason, isDone: isDone) - .max3; - -/// Informs the framework that the given [callback] of arity 4 is expected to be -/// called until [isDone] returns true. -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// [isDone] is called after each time the function is run. Only when it returns -/// true will the callback be considered complete. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with four arguments. See also -/// [expectAsyncUntil0], [expectAsyncUntil1], [expectAsyncUntil2], -/// [expectAsyncUntil3], [expectAsyncUntil5], and [expectAsyncUntil6] for -/// callbacks with different arity. -Func4 expectAsyncUntil4( - T Function(A, B, C, D) callback, bool Function() isDone, - {String? id, String? reason}) => - _ExpectedFunction(callback, 0, -1, - id: id, reason: reason, isDone: isDone) - .max4; - -/// Informs the framework that the given [callback] of arity 5 is expected to be -/// called until [isDone] returns true. -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// [isDone] is called after each time the function is run. Only when it returns -/// true will the callback be considered complete. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with five arguments. See also -/// [expectAsyncUntil0], [expectAsyncUntil1], [expectAsyncUntil2], -/// [expectAsyncUntil3], [expectAsyncUntil4], and [expectAsyncUntil6] for -/// callbacks with different arity. -Func5 expectAsyncUntil5( - T Function(A, B, C, D, E) callback, bool Function() isDone, - {String? id, String? reason}) => - _ExpectedFunction(callback, 0, -1, - id: id, reason: reason, isDone: isDone) - .max5; - -/// Informs the framework that the given [callback] of arity 6 is expected to be -/// called until [isDone] returns true. -/// -/// Returns a wrapped function that should be used as a replacement of the -/// original callback. -/// -/// [isDone] is called after each time the function is run. Only when it returns -/// true will the callback be considered complete. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -/// -/// This method takes callbacks with six arguments. See also -/// [expectAsyncUntil0], [expectAsyncUntil1], [expectAsyncUntil2], -/// [expectAsyncUntil3], [expectAsyncUntil4], and [expectAsyncUntil5] for -/// callbacks with different arity. -Func6 expectAsyncUntil6( - T Function(A, B, C, D, E, F) callback, bool Function() isDone, - {String? id, String? reason}) => - _ExpectedFunction(callback, 0, -1, - id: id, reason: reason, isDone: isDone) - .max6; diff --git a/pkgs/test_api/lib/src/expect/future_matchers.dart b/pkgs/test_api/lib/src/expect/future_matchers.dart deleted file mode 100644 index 9900c9759..000000000 --- a/pkgs/test_api/lib/src/expect/future_matchers.dart +++ /dev/null @@ -1,115 +0,0 @@ -// 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. - -import 'package:matcher/matcher.dart'; -import 'package:test_api/hooks.dart' show pumpEventQueue; - -import 'async_matcher.dart'; -import 'expect.dart'; -import 'util/pretty_print.dart'; - -/// Matches a [Future] that completes successfully with any value. -/// -/// This creates an asynchronous expectation. The call to [expect] will return -/// immediately and execution will continue. Later, when the future completes, -/// the expectation against [matcher] will run. To wait for the future to -/// complete and the expectation to run use [expectLater] and wait on the -/// returned future. -/// -/// To test that a Future completes with an exception, you can use [throws] and -/// [throwsA]. -final Matcher completes = const _Completes(null); - -/// Matches a [Future] that completes successfully with a value that matches -/// [matcher]. -/// -/// This creates an asynchronous expectation. The call to [expect] will return -/// immediately and execution will continue. Later, when the future completes, -/// the expectation against [matcher] will run. To wait for the future to -/// complete and the expectation to run use [expectLater] and wait on the -/// returned future. -/// -/// To test that a Future completes with an exception, you can use [throws] and -/// [throwsA]. -Matcher completion(matcher, - [@Deprecated('this parameter is ignored') String? description]) => - _Completes(wrapMatcher(matcher)); - -class _Completes extends AsyncMatcher { - final Matcher? _matcher; - - const _Completes(this._matcher); - - // Avoid async/await so we synchronously start listening to [item]. - @override - dynamic /*FutureOr*/ matchAsync(item) { - if (item is! Future) return 'was not a Future'; - - return item.then((value) async { - if (_matcher == null) return null; - - String? result; - if (_matcher is AsyncMatcher) { - result = await (_matcher as AsyncMatcher).matchAsync(value) as String?; - if (result == null) return null; - } else { - var matchState = {}; - if (_matcher!.matches(value, matchState)) return null; - result = _matcher! - .describeMismatch(value, StringDescription(), matchState, false) - .toString(); - } - - var buffer = StringBuffer(); - buffer.writeln(indent(prettyPrint(value), first: 'emitted ')); - if (result.isNotEmpty) buffer.writeln(indent(result, first: ' which ')); - return buffer.toString().trimRight(); - }); - } - - @override - Description describe(Description description) { - if (_matcher == null) { - description.add('completes successfully'); - } else { - description.add('completes to a value that ').addDescriptionOf(_matcher); - } - return description; - } -} - -/// Matches a [Future] that does not complete. -/// -/// Note that this creates an asynchronous expectation. The call to -/// `expect()` that includes this will return immediately and execution will -/// continue. -final Matcher doesNotComplete = const _DoesNotComplete(); - -class _DoesNotComplete extends Matcher { - const _DoesNotComplete(); - - @override - Description describe(Description description) { - description.add('does not complete'); - return description; - } - - @override - bool matches(item, Map matchState) { - if (item is! Future) return false; - item.then((value) { - fail('Future was not expected to complete but completed with a value of ' - '$value'); - }); - expect(pumpEventQueue(), completes); - return true; - } - - @override - Description describeMismatch( - item, Description description, Map matchState, bool verbose) { - if (item is! Future) return description.add('$item is not a Future'); - return description; - } -} diff --git a/pkgs/test_api/lib/src/expect/never_called.dart b/pkgs/test_api/lib/src/expect/never_called.dart deleted file mode 100644 index 96c81cb6b..000000000 --- a/pkgs/test_api/lib/src/expect/never_called.dart +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2017, 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:stack_trace/stack_trace.dart'; -import 'package:test_api/hooks.dart'; - -import 'expect.dart'; -import 'future_matchers.dart'; -import 'util/placeholder.dart'; -import 'util/pretty_print.dart'; - -/// Returns a function that causes the test to fail if it's called. -/// -/// This can safely be passed in place of any callback that takes ten or fewer -/// positional parameters. For example: -/// -/// ``` -/// // Asserts that the stream never emits an event. -/// stream.listen(neverCalled); -/// ``` -/// -/// This also ensures that the test doesn't complete until a call to -/// [pumpEventQueue] finishes, so that the callback has a chance to be called. -Null Function( - [Object?, - Object?, - Object?, - Object?, - Object?, - Object?, - Object?, - Object?, - Object?, - Object?]) get neverCalled { - // Make sure the test stays alive long enough to call the function if it's - // going to. - expect(pumpEventQueue(), completes); - - var zone = Zone.current; - return ( - [a1 = placeholder, - a2 = placeholder, - a3 = placeholder, - a4 = placeholder, - a5 = placeholder, - a6 = placeholder, - a7 = placeholder, - a8 = placeholder, - a9 = placeholder, - a10 = placeholder]) { - var arguments = [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10] - .where((argument) => argument != placeholder) - .toList(); - - var argsText = arguments.isEmpty - ? ' no arguments.' - : ':\n${bullet(arguments.map(prettyPrint))}'; - zone.handleUncaughtError( - TestFailure( - 'Callback should never have been called, but it was called with' - '$argsText'), - zone.run(() => Chain.current())); - return null; - }; -} diff --git a/pkgs/test_api/lib/src/expect/prints_matcher.dart b/pkgs/test_api/lib/src/expect/prints_matcher.dart deleted file mode 100644 index 5e9769bfa..000000000 --- a/pkgs/test_api/lib/src/expect/prints_matcher.dart +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2014, 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:matcher/matcher.dart'; - -import 'async_matcher.dart'; -import 'expect.dart'; -import 'util/pretty_print.dart'; - -/// Matches a [Function] that prints text that matches [matcher]. -/// -/// [matcher] may be a String or a [Matcher]. -/// -/// If the function this runs against returns a [Future], all text printed by -/// the function (using [Zone] scoping) until that Future completes is matched. -/// -/// This only tracks text printed using the [print] function. -/// -/// This returns an [AsyncMatcher], so [expect] won't complete until the matched -/// function does. -Matcher prints(matcher) => _Prints(wrapMatcher(matcher)); - -class _Prints extends AsyncMatcher { - final Matcher _matcher; - - _Prints(this._matcher); - - // Avoid async/await so we synchronously fail if the function is - // synchronous. - @override - dynamic /*FutureOr*/ matchAsync(item) { - if (item is! Function()) return 'was not a unary Function'; - - var buffer = StringBuffer(); - var result = runZoned(item, - zoneSpecification: ZoneSpecification(print: (_, __, ____, line) { - buffer.writeln(line); - })); - - return result is Future - ? result.then((_) => _check(buffer.toString())) - : _check(buffer.toString()); - } - - @override - Description describe(Description description) => - description.add('prints ').addDescriptionOf(_matcher); - - /// Verifies that [actual] matches [_matcher] and returns a [String] - /// description of the failure if it doesn't. - String? _check(String actual) { - var matchState = {}; - if (_matcher.matches(actual, matchState)) return null; - - var result = _matcher - .describeMismatch(actual, StringDescription(), matchState, false) - .toString(); - - var buffer = StringBuffer(); - if (actual.isEmpty) { - buffer.writeln('printed nothing'); - } else { - buffer.writeln(indent(prettyPrint(actual), first: 'printed ')); - } - if (result.isNotEmpty) buffer.writeln(indent(result, first: ' which ')); - return buffer.toString().trimRight(); - } -} diff --git a/pkgs/test_api/lib/src/expect/stream_matcher.dart b/pkgs/test_api/lib/src/expect/stream_matcher.dart deleted file mode 100644 index 6d7bddfb8..000000000 --- a/pkgs/test_api/lib/src/expect/stream_matcher.dart +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) 2017, 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 'package:async/async.dart'; -import 'package:matcher/matcher.dart'; -import 'package:test_api/hooks.dart'; - -import 'async_matcher.dart'; -import 'util/pretty_print.dart'; - -/// A matcher that matches events from [Stream]s or [StreamQueue]s. -/// -/// Stream matchers are designed to make it straightforward to create complex -/// expectations for streams, and to interleave expectations with the rest of a -/// test. They can be used on a [Stream] to match all events it emits: -/// -/// ```dart -/// expect(stream, emitsInOrder([ -/// // Values match individual events. -/// "Ready.", -/// -/// // Matchers also run against individual events. -/// startsWith("Loading took"), -/// -/// // Stream matchers can be nested. This asserts that one of two events are -/// // emitted after the "Loading took" line. -/// emitsAnyOf(["Succeeded!", "Failed!"]), -/// -/// // By default, more events are allowed after the matcher finishes -/// // matching. This asserts instead that the stream emits a done event and -/// // nothing else. -/// emitsDone -/// ])); -/// ``` -/// -/// It can also match a [StreamQueue], in which case it consumes the matched -/// events. The call to [expect] returns a [Future] that completes when the -/// matcher is done matching. You can `await` this to consume different events -/// at different times: -/// -/// ```dart -/// var stdout = StreamQueue(stdoutLineStream); -/// -/// // Ignore lines from the process until it's about to emit the URL. -/// await expectLater(stdout, emitsThrough('WebSocket URL:')); -/// -/// // Parse the next line as a URL. -/// var url = Uri.parse(await stdout.next); -/// expect(url.host, equals('localhost')); -/// -/// // You can match against the same StreamQueue multiple times. -/// await expectLater(stdout, emits('Waiting for connection...')); -/// ``` -/// -/// Users can call [StreamMatcher] to create custom matchers. -abstract class StreamMatcher extends Matcher { - /// The description of this matcher. - /// - /// This is in the subjunctive mood, which means it can be used after the word - /// "should". For example, it might be "emit the right events". - String get description; - - /// Creates a new [StreamMatcher] described by [description] that matches - /// events with [matchQueue]. - /// - /// The [matchQueue] callback is used to implement [StreamMatcher.matchQueue], - /// and should follow all the guarantees of that method. In particular: - /// - /// * If it matches successfully, it should return `null` and possibly consume - /// events. - /// * If it fails to match, consume no events and return a description of the - /// failure. - /// * The description should be in past tense. - /// * The description should be grammatically valid when used after "the - /// stream"—"emitted the wrong events", for example. - /// - /// The [matchQueue] callback may return the empty string to indicate a - /// failure if it has no information to add beyond the description of the - /// failure and the events actually emitted by the stream. - /// - /// The [description] should be in the subjunctive mood. This means that it - /// should be grammatically valid when used after the word "should". For - /// example, it might be "emit the right events". - factory StreamMatcher(Future Function(StreamQueue) matchQueue, - String description) = _StreamMatcher; - - /// Tries to match events emitted by [queue]. - /// - /// If this matches successfully, it consumes the matching events from [queue] - /// and returns `null`. - /// - /// If this fails to match, it doesn't consume any events and returns a - /// description of the failure. This description is in the past tense, and - /// could grammatically be used after "the stream". For example, it might - /// return "emitted the wrong events". - /// - /// The description string may also be empty, which indicates that the - /// matcher's description and the events actually emitted by the stream are - /// enough to understand the failure. - /// - /// If the queue emits an error, that error is re-thrown unless otherwise - /// indicated by the matcher. - Future matchQueue(StreamQueue queue); -} - -/// A concrete implementation of [StreamMatcher]. -/// -/// This is separate from the original type to hide the private [AsyncMatcher] -/// interface. -class _StreamMatcher extends AsyncMatcher implements StreamMatcher { - @override - final String description; - - /// The callback used to implement [matchQueue]. - final Future Function(StreamQueue) _matchQueue; - - _StreamMatcher(this._matchQueue, this.description); - - @override - Future matchQueue(StreamQueue queue) => _matchQueue(queue); - - @override - dynamic /*FutureOr*/ matchAsync(item) { - StreamQueue queue; - var shouldCancelQueue = false; - if (item is StreamQueue) { - queue = item; - } else if (item is Stream) { - queue = StreamQueue(item); - shouldCancelQueue = true; - } else { - return 'was not a Stream or a StreamQueue'; - } - - // Avoid async/await in the outer method so that we synchronously error out - // for an invalid argument type. - var transaction = queue.startTransaction(); - var copy = transaction.newQueue(); - return matchQueue(copy).then((result) async { - // Accept the transaction if the result is null, indicating that the match - // succeeded. - if (result == null) { - transaction.commit(copy); - return null; - } - - // Get a list of events emitted by the stream so we can emit them as part - // of the error message. - var replay = transaction.newQueue(); - var events = []; - var subscription = Result.captureStreamTransformer - .bind(replay.rest.cast()) - .listen(events.add, onDone: () => events.add(null)); - - // Wait on a timer tick so all buffered events are emitted. - await Future.delayed(Duration.zero); - _unawaited(subscription.cancel()); - - var eventsString = events.map((event) { - if (event == null) { - return 'x Stream closed.'; - } else if (event.isValue) { - return addBullet(event.asValue!.value.toString()); - } else { - var error = event.asError!; - var chain = TestHandle.current.formatStackTrace(error.stackTrace); - var text = '${error.error}\n$chain'; - return indent(text, first: '! '); - } - }).join('\n'); - if (eventsString.isEmpty) eventsString = 'no events'; - - transaction.reject(); - - var buffer = StringBuffer(); - buffer.writeln(indent(eventsString, first: 'emitted ')); - if (result.isNotEmpty) buffer.writeln(indent(result, first: ' which ')); - return buffer.toString().trimRight(); - }, onError: (Object error) { - transaction.reject(); - throw error; - }).then((result) { - if (shouldCancelQueue) queue.cancel(); - return result; - }); - } - - @override - Description describe(Description description) => - description.add('should ').add(this.description); -} - -void _unawaited(Future f) {} diff --git a/pkgs/test_api/lib/src/expect/stream_matchers.dart b/pkgs/test_api/lib/src/expect/stream_matchers.dart deleted file mode 100644 index df2fbbd01..000000000 --- a/pkgs/test_api/lib/src/expect/stream_matchers.dart +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright (c) 2017, 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 'package:async/async.dart'; -import 'package:matcher/matcher.dart'; - -import 'async_matcher.dart'; -import 'stream_matcher.dart'; -import 'throws_matcher.dart'; -import 'util/pretty_print.dart'; - -/// Returns a [StreamMatcher] that asserts that the stream emits a "done" event. -final emitsDone = StreamMatcher( - (queue) async => (await queue.hasNext) ? '' : null, 'be done'); - -/// Returns a [StreamMatcher] for [matcher]. -/// -/// If [matcher] is already a [StreamMatcher], it's returned as-is. If it's any -/// other [Matcher], this matches a single event that matches that matcher. If -/// it's any other Object, this matches a single event that's equal to that -/// object. -/// -/// This functions like [wrapMatcher] for [StreamMatcher]s: it can convert any -/// matcher-like value into a proper [StreamMatcher]. -StreamMatcher emits(matcher) { - if (matcher is StreamMatcher) return matcher; - var wrapped = wrapMatcher(matcher); - - var matcherDescription = wrapped.describe(StringDescription()); - - return StreamMatcher((queue) async { - if (!await queue.hasNext) return ''; - - var matchState = {}; - var actual = await queue.next; - if (wrapped.matches(actual, matchState)) return null; - - var mismatchDescription = StringDescription(); - wrapped.describeMismatch(actual, mismatchDescription, matchState, false); - - if (mismatchDescription.length == 0) return ''; - return 'emitted an event that $mismatchDescription'; - }, - // TODO(nweiz): add "should" once matcher#42 is fixed. - 'emit an event that $matcherDescription'); -} - -/// Returns a [StreamMatcher] that matches a single error event that matches -/// [matcher]. -StreamMatcher emitsError(matcher) { - var wrapped = wrapMatcher(matcher); - var matcherDescription = wrapped.describe(StringDescription()); - var throwsMatcher = throwsA(wrapped) as AsyncMatcher; - - return StreamMatcher( - (queue) => throwsMatcher.matchAsync(queue.next) as Future, - // TODO(nweiz): add "should" once matcher#42 is fixed. - 'emit an error that $matcherDescription'); -} - -/// Returns a [StreamMatcher] that allows (but doesn't require) [matcher] to -/// match the stream. -/// -/// This matcher always succeeds; if [matcher] doesn't match, this just consumes -/// no events. -StreamMatcher mayEmit(matcher) { - var streamMatcher = emits(matcher); - return StreamMatcher((queue) async { - await queue.withTransaction( - (copy) async => (await streamMatcher.matchQueue(copy)) == null); - return null; - }, 'maybe ${streamMatcher.description}'); -} - -/// Returns a [StreamMatcher] that matches the stream if at least one of -/// [matchers] matches. -/// -/// If multiple matchers match the stream, this chooses the matcher that -/// consumes as many events as possible. -/// -/// If any matchers match the stream, no errors from other matchers are thrown. -/// If no matchers match and multiple matchers threw errors, the first error is -/// re-thrown. -StreamMatcher emitsAnyOf(Iterable matchers) { - var streamMatchers = matchers.map(emits).toList(); - if (streamMatchers.isEmpty) { - throw ArgumentError('matcher may not be empty'); - } - - if (streamMatchers.length == 1) return streamMatchers.first; - var description = 'do one of the following:\n' - '${bullet(streamMatchers.map((matcher) => matcher.description))}'; - - return StreamMatcher((queue) async { - var transaction = queue.startTransaction(); - - // Allocate the failures list ahead of time so that its order matches the - // order of [matchers], and thus the order the matchers will be listed in - // the description. - var failures = List.filled(matchers.length, null); - - // The first error thrown. If no matchers match and this exists, we rethrow - // it. - Object? firstError; - StackTrace? firstStackTrace; - - var futures = []; - StreamQueue? consumedMost; - for (var i = 0; i < matchers.length; i++) { - futures.add(() async { - var copy = transaction.newQueue(); - - String? result; - try { - result = await streamMatchers[i].matchQueue(copy); - } catch (error, stackTrace) { - if (firstError == null) { - firstError = error; - firstStackTrace = stackTrace; - } - return; - } - - if (result != null) { - failures[i] = result; - } else if (consumedMost == null || - consumedMost!.eventsDispatched < copy.eventsDispatched) { - consumedMost = copy; - } - }()); - } - - await Future.wait(futures); - - if (consumedMost == null) { - transaction.reject(); - if (firstError != null) { - await Future.error(firstError!, firstStackTrace); - } - - var failureMessages = []; - for (var i = 0; i < matchers.length; i++) { - var message = 'failed to ${streamMatchers[i].description}'; - if ((failures[i])!.isNotEmpty) { - message += message.contains('\n') ? '\n' : ' '; - message += 'because it ${failures[i]}'; - } - - failureMessages.add(message); - } - - return 'failed all options:\n${bullet(failureMessages)}'; - } else { - transaction.commit(consumedMost!); - return null; - } - }, description); -} - -/// Returns a [StreamMatcher] that matches the stream if each matcher in -/// [matchers] matches, one after another. -/// -/// If any matcher fails to match, this fails and consumes no events. -StreamMatcher emitsInOrder(Iterable matchers) { - var streamMatchers = matchers.map(emits).toList(); - if (streamMatchers.length == 1) return streamMatchers.first; - - var description = 'do the following in order:\n' - '${bullet(streamMatchers.map((matcher) => matcher.description))}'; - - return StreamMatcher((queue) async { - for (var i = 0; i < streamMatchers.length; i++) { - var matcher = streamMatchers[i]; - var result = await matcher.matchQueue(queue); - if (result == null) continue; - - var newResult = "didn't ${matcher.description}"; - if (result.isNotEmpty) { - newResult += newResult.contains('\n') ? '\n' : ' '; - newResult += 'because it $result'; - } - return newResult; - } - return null; - }, description); -} - -/// Returns a [StreamMatcher] that matches any number of events followed by -/// events that match [matcher]. -/// -/// This consumes all events matched by [matcher], as well as all events before. -/// If the stream emits a done event without matching [matcher], this fails and -/// consumes no events. -StreamMatcher emitsThrough(matcher) { - var streamMatcher = emits(matcher); - return StreamMatcher((queue) async { - var failures = []; - - Future tryHere() => queue.withTransaction((copy) async { - var result = await streamMatcher.matchQueue(copy); - if (result == null) return true; - failures.add(result); - return false; - }); - - while (await queue.hasNext) { - if (await tryHere()) return null; - await queue.next; - } - - // Try after the queue is done in case the matcher can match an empty - // stream. - if (await tryHere()) return null; - - var result = 'never did ${streamMatcher.description}'; - - var failureMessages = - bullet(failures.where((failure) => failure.isNotEmpty)); - if (failureMessages.isNotEmpty) { - result += result.contains('\n') ? '\n' : ' '; - result += 'because it:\n$failureMessages'; - } - - return result; - }, 'eventually ${streamMatcher.description}'); -} - -/// Returns a [StreamMatcher] that matches any number of events that match -/// [matcher]. -/// -/// This consumes events until [matcher] no longer matches. It always succeeds; -/// if [matcher] doesn't match, this just consumes no events. It never rethrows -/// errors. -StreamMatcher mayEmitMultiple(matcher) { - var streamMatcher = emits(matcher); - - var description = streamMatcher.description; - description += description.contains('\n') ? '\n' : ' '; - description += 'zero or more times'; - - return StreamMatcher((queue) async { - while (await _tryMatch(queue, streamMatcher)) { - // Do nothing; the matcher presumably already consumed events. - } - return null; - }, description); -} - -/// Returns a [StreamMatcher] that matches a stream that never matches -/// [matcher]. -/// -/// This doesn't complete until the stream emits a done event. It never consumes -/// any events. It never re-throws errors. -StreamMatcher neverEmits(matcher) { - var streamMatcher = emits(matcher); - return StreamMatcher((queue) async { - var events = 0; - var matched = false; - await queue.withTransaction((copy) async { - while (await copy.hasNext) { - matched = await _tryMatch(copy, streamMatcher); - if (matched) return false; - - events++; - - try { - await copy.next; - } catch (_) { - // Ignore errors events. - } - } - - matched = await _tryMatch(copy, streamMatcher); - return false; - }); - - if (!matched) return null; - return "after $events ${pluralize('event', events)} did " - '${streamMatcher.description}'; - }, 'never ${streamMatcher.description}'); -} - -/// Returns whether [matcher] matches [queue] at its current position. -/// -/// This treats errors as failures to match. -Future _tryMatch(StreamQueue queue, StreamMatcher matcher) { - return queue.withTransaction((copy) async { - try { - return (await matcher.matchQueue(copy)) == null; - } catch (_) { - return false; - } - }); -} - -/// Returns a [StreamMatcher] that matches the stream if each matcher in -/// [matchers] matches, in any order. -/// -/// If any matcher fails to match, this fails and consumes no events. If the -/// matchers match in multiple different possible orders, this chooses the order -/// that consumes as many events as possible. -/// -/// If any sequence of matchers matches the stream, no errors from other -/// sequences are thrown. If no sequences match and multiple sequences throw -/// errors, the first error is re-thrown. -/// -/// Note that checking every ordering of [matchers] is O(n!) in the worst case, -/// so this should only be called when there are very few [matchers]. -StreamMatcher emitsInAnyOrder(Iterable matchers) { - var streamMatchers = matchers.map(emits).toSet(); - if (streamMatchers.length == 1) return streamMatchers.first; - var description = 'do the following in any order:\n' - '${bullet(streamMatchers.map((matcher) => matcher.description))}'; - - return StreamMatcher( - (queue) async => await _tryInAnyOrder(queue, streamMatchers) ? null : '', - description); -} - -/// Returns whether [queue] matches [matchers] in any order. -Future _tryInAnyOrder( - StreamQueue queue, Set matchers) async { - if (matchers.length == 1) { - return await matchers.first.matchQueue(queue) == null; - } - - var transaction = queue.startTransaction(); - StreamQueue? consumedMost; - - // The first error thrown. If no matchers match and this exists, we rethrow - // it. - Object? firstError; - StackTrace? firstStackTrace; - - await Future.wait(matchers.map((matcher) async { - var copy = transaction.newQueue(); - try { - if (await matcher.matchQueue(copy) != null) return; - } catch (error, stackTrace) { - if (firstError == null) { - firstError = error; - firstStackTrace = stackTrace; - } - return; - } - - var rest = Set.from(matchers); - rest.remove(matcher); - - try { - if (!await _tryInAnyOrder(copy, rest)) return; - } catch (error, stackTrace) { - if (firstError == null) { - firstError = error; - firstStackTrace = stackTrace; - } - return; - } - - if (consumedMost == null || - consumedMost!.eventsDispatched < copy.eventsDispatched) { - consumedMost = copy; - } - })); - - if (consumedMost == null) { - transaction.reject(); - if (firstError != null) await Future.error(firstError!, firstStackTrace); - return false; - } else { - transaction.commit(consumedMost!); - return true; - } -} diff --git a/pkgs/test_api/lib/src/expect/throws_matcher.dart b/pkgs/test_api/lib/src/expect/throws_matcher.dart deleted file mode 100644 index b4f081e77..000000000 --- a/pkgs/test_api/lib/src/expect/throws_matcher.dart +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2014, 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 'package:matcher/matcher.dart'; -import 'package:test_api/hooks.dart'; - -import 'async_matcher.dart'; -import 'util/pretty_print.dart'; - -/// This function is deprecated. -/// -/// Use [throwsA] instead. We strongly recommend that you add assertions about -/// at least the type of the error, but you can write `throwsA(anything)` to -/// mimic the behavior of this matcher. -@Deprecated('Will be removed in 0.13.0') -const Matcher throws = Throws(); - -/// This can be used to match three kinds of objects: -/// -/// * A [Function] that throws an exception when called. The function cannot -/// take any arguments. If you want to test that a function expecting -/// arguments throws, wrap it in another zero-argument function that calls -/// the one you want to test. -/// -/// * A [Future] that completes with an exception. Note that this creates an -/// asynchronous expectation. The call to `expect()` that includes this will -/// return immediately and execution will continue. Later, when the future -/// completes, the actual expectation will run. -/// -/// * A [Function] that returns a [Future] that completes with an exception. -/// -/// In all three cases, when an exception is thrown, this will test that the -/// exception object matches [matcher]. If [matcher] is not an instance of -/// [Matcher], it will implicitly be treated as `equals(matcher)`. -/// -/// Examples: -/// ```dart -/// void functionThatThrows() => throw SomeException(); -/// -/// void functionWithArgument(bool shouldThrow) { -/// if (shouldThrow) { -/// throw SomeException(); -/// } -/// } -/// -/// Future asyncFunctionThatThrows() async => throw SomeException(); -/// -/// expect(functionThatThrows, throwsA(isA())); -/// -/// expect(() => functionWithArgument(true), throwsA(isA())); -/// -/// var future = asyncFunctionThatThrows(); -/// await expectLater(future, throwsA(isA())); -/// -/// await expectLater( -/// asyncFunctionThatThrows, throwsA(isA())); -/// ``` -Matcher throwsA(matcher) => Throws(wrapMatcher(matcher)); - -/// Use the [throwsA] function instead. -@Deprecated('Will be removed in 0.13.0') -class Throws extends AsyncMatcher { - final Matcher? _matcher; - - const Throws([Matcher? matcher]) : _matcher = matcher; - - // Avoid async/await so we synchronously fail if we match a synchronous - // function. - @override - dynamic /*FutureOr*/ matchAsync(item) { - if (item is! Function && item is! Future) { - return 'was not a Function or Future'; - } - - if (item is Future) { - return _matchFuture(item, 'emitted '); - } - - try { - var value = item(); - if (value is Future) { - return _matchFuture(value, 'returned a Future that emitted '); - } - - return indent(prettyPrint(value), first: 'returned '); - } catch (error, trace) { - return _check(error, trace); - } - } - - /// Matches [future], using try/catch since `onError` doesn't seem to work - /// properly in nnbd. - Future _matchFuture( - Future future, String messagePrefix) async { - try { - var value = await future; - return indent(prettyPrint(value), first: messagePrefix); - } catch (error, trace) { - return _check(error, trace); - } - } - - @override - Description describe(Description description) { - if (_matcher == null) { - return description.add('throws'); - } else { - return description.add('throws ').addDescriptionOf(_matcher); - } - } - - /// Verifies that [error] matches [_matcher] and returns a [String] - /// description of the failure if it doesn't. - String? _check(error, StackTrace? trace) { - if (_matcher == null) return null; - - var matchState = {}; - if (_matcher!.matches(error, matchState)) return null; - - var result = _matcher! - .describeMismatch(error, StringDescription(), matchState, false) - .toString(); - - var buffer = StringBuffer(); - buffer.writeln(indent(prettyPrint(error), first: 'threw ')); - if (trace != null) { - buffer.writeln(indent( - TestHandle.current.formatStackTrace(trace).toString(), - first: 'stack ')); - } - if (result.isNotEmpty) buffer.writeln(indent(result, first: 'which ')); - return buffer.toString().trimRight(); - } -} diff --git a/pkgs/test_api/lib/src/expect/throws_matchers.dart b/pkgs/test_api/lib/src/expect/throws_matchers.dart deleted file mode 100644 index edf777b93..000000000 --- a/pkgs/test_api/lib/src/expect/throws_matchers.dart +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2014, 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 'package:matcher/matcher.dart'; - -import 'throws_matcher.dart'; - -/// A matcher for functions that throw ArgumentError. -/// -/// See [throwsA] for objects that this can be matched against. -const Matcher throwsArgumentError = Throws(isArgumentError); - -/// A matcher for functions that throw ConcurrentModificationError. -/// -/// See [throwsA] for objects that this can be matched against. -const Matcher throwsConcurrentModificationError = - Throws(isConcurrentModificationError); - -/// A matcher for functions that throw CyclicInitializationError. -/// -/// See [throwsA] for objects that this can be matched against. -@Deprecated('throwsCyclicInitializationError has been deprecated, because ' - 'the type will longer exists in Dart 3.0. It will now catch any kind of ' - 'error, not only CyclicInitializationError.') -const Matcher throwsCyclicInitializationError = Throws(TypeMatcher()); - -/// A matcher for functions that throw Exception. -/// -/// See [throwsA] for objects that this can be matched against. -const Matcher throwsException = Throws(isException); - -/// A matcher for functions that throw FormatException. -/// -/// See [throwsA] for objects that this can be matched against. -const Matcher throwsFormatException = Throws(isFormatException); - -/// A matcher for functions that throw NoSuchMethodError. -/// -/// See [throwsA] for objects that this can be matched against. -const Matcher throwsNoSuchMethodError = Throws(isNoSuchMethodError); - -/// A matcher for functions that throw NullThrownError. -/// -/// See [throwsA] for objects that this can be matched against. -@Deprecated('throwsNullThrownError has been deprecated, because ' - 'NullThrownError has been replaced with TypeError. ' - 'Use `throwsA(isA())` instead.') -const Matcher throwsNullThrownError = Throws(TypeMatcher()); - -/// A matcher for functions that throw RangeError. -/// -/// See [throwsA] for objects that this can be matched against. -const Matcher throwsRangeError = Throws(isRangeError); - -/// A matcher for functions that throw StateError. -/// -/// See [throwsA] for objects that this can be matched against. -const Matcher throwsStateError = Throws(isStateError); - -/// A matcher for functions that throw Exception. -/// -/// See [throwsA] for objects that this can be matched against. -const Matcher throwsUnimplementedError = Throws(isUnimplementedError); - -/// A matcher for functions that throw UnsupportedError. -/// -/// See [throwsA] for objects that this can be matched against. -const Matcher throwsUnsupportedError = Throws(isUnsupportedError); diff --git a/pkgs/test_api/lib/src/expect/util/placeholder.dart b/pkgs/test_api/lib/src/expect/util/placeholder.dart deleted file mode 100644 index ee2dc70ac..000000000 --- a/pkgs/test_api/lib/src/expect/util/placeholder.dart +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2017, 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. - -/// A class that's used as a default argument to detect whether an argument was -/// passed. -/// -/// We use a custom class for this rather than just `const Object()` so that -/// callers can't accidentally pass the placeholder value. -class _Placeholder { - const _Placeholder(); -} - -/// A placeholder to use as a default argument value to detect whether an -/// argument was passed. -const placeholder = _Placeholder(); diff --git a/pkgs/test_api/lib/src/expect/util/pretty_print.dart b/pkgs/test_api/lib/src/expect/util/pretty_print.dart deleted file mode 100644 index f21399dc7..000000000 --- a/pkgs/test_api/lib/src/expect/util/pretty_print.dart +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2021, 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 'package:matcher/matcher.dart'; -import 'package:term_glyph/term_glyph.dart' as glyph; - -/// Indent each line in [string] by [first.length] spaces. -/// -/// [first] is used in place of the first line's indentation. -String indent(String text, {required String first}) { - final prefix = ' ' * first.length; - var lines = text.split('\n'); - if (lines.length == 1) return '$first$text'; - - var buffer = StringBuffer('$first${lines.first}\n'); - - // Write out all but the first and last lines with [prefix]. - for (var line in lines.skip(1).take(lines.length - 2)) { - buffer.writeln('$prefix$line'); - } - buffer.write('$prefix${lines.last}'); - return buffer.toString(); -} - -/// Returns a pretty-printed representation of [value]. -/// -/// The matcher package doesn't expose its pretty-print function directly, but -/// we can use it through StringDescription. -String prettyPrint(value) => - StringDescription().addDescriptionOf(value).toString(); - -/// Indents [text], and adds a bullet at the beginning. -String addBullet(String text) => indent(text, first: '${glyph.bullet} '); - -/// Converts [strings] to a bulleted list. -String bullet(Iterable strings) => strings.map(addBullet).join('\n'); - -/// Returns [name] if [number] is 1, or the plural of [name] otherwise. -/// -/// By default, this just adds "s" to the end of [name] to get the plural. If -/// [plural] is passed, that's used instead. -String pluralize(String name, int number, {String? plural}) { - if (number == 1) return name; - if (plural != null) return plural; - return '${name}s'; -} diff --git a/pkgs/test_api/lib/src/frontend/async_matcher.dart b/pkgs/test_api/lib/src/frontend/async_matcher.dart deleted file mode 100644 index a6b800a48..000000000 --- a/pkgs/test_api/lib/src/frontend/async_matcher.dart +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2021, 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. - -@Deprecated('Moved to src/expect/async_matcher.dart') -library test_api.src.frontend.async_matcher; - -// Temporary re-export to reduce churn in flutter_test. -export '../expect/async_matcher.dart'; diff --git a/pkgs/test_api/lib/src/frontend/expect.dart b/pkgs/test_api/lib/src/frontend/expect.dart deleted file mode 100644 index 43540589d..000000000 --- a/pkgs/test_api/lib/src/frontend/expect.dart +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2021, 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. - -@Deprecated('Moved to src/expect/expect.dart') -library test_api.src.frontend.expect; - -// Temporary re-export to avoid churn in analyzer tests. -export '../expect/expect.dart'; diff --git a/pkgs/test_api/lib/test_api.dart b/pkgs/test_api/lib/test_api.dart index 8e037ccc0..7c732f16d 100644 --- a/pkgs/test_api/lib/test_api.dart +++ b/pkgs/test_api/lib/test_api.dart @@ -6,12 +6,7 @@ 'Please use package:test.') library test_api; -export 'expect.dart'; export 'hooks.dart' show TestFailure; export 'scaffolding.dart'; -// Deprecated exports not surfaced through focused libraries. -export 'src/expect/expect.dart' show ErrorFormatter; -export 'src/expect/expect_async.dart' show expectAsync; -export 'src/expect/throws_matcher.dart' show throws, Throws; // Not yet deprecated, but not exposed through focused libraries. export 'src/scaffolding/utils.dart' show registerException; diff --git a/pkgs/test_api/pubspec.yaml b/pkgs/test_api/pubspec.yaml index 124cd69ae..2fe918fa7 100644 --- a/pkgs/test_api/pubspec.yaml +++ b/pkgs/test_api/pubspec.yaml @@ -18,10 +18,6 @@ dependencies: string_scanner: ^1.1.0 term_glyph: ^1.2.0 - # Use a tight version constraint to ensure that a constraint on matcher - # properly constrains all features it provides. - matcher: '>=0.12.11 <0.12.15' - dev_dependencies: analyzer: '>=2.1.0 <6.0.0' fake_async: ^1.2.0 diff --git a/pkgs/test_api/pubspec_overrides.yaml b/pkgs/test_api/pubspec_overrides.yaml index bdb2cf470..77c296ea4 100644 --- a/pkgs/test_api/pubspec_overrides.yaml +++ b/pkgs/test_api/pubspec_overrides.yaml @@ -3,3 +3,7 @@ dependency_overrides: path: ../test test_core: path: ../test_core + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3b48930a01f678e2921cea16563af67460e6f765 diff --git a/pkgs/test_api/test/backend/invoker_test.dart b/pkgs/test_api/test/backend/invoker_test.dart index 490bb023b..d2ba0de80 100644 --- a/pkgs/test_api/test/backend/invoker_test.dart +++ b/pkgs/test_api/test/backend/invoker_test.dart @@ -184,10 +184,9 @@ void main() { expect(lastState, equals(const State(Status.complete, Result.error))); expect( error, - equals( - 'This test failed after it had already completed. Make sure to ' - 'use [expectAsync]\n' - 'or the [completes] matcher when testing async code.')); + equals('This test failed after it had already completed.\n' + 'Make sure to use a matching library which informs the test runner ' + 'of pending async work.')); } ]); diff --git a/pkgs/test_api/test/frontend/expect_async_test.dart b/pkgs/test_api/test/frontend/expect_async_test.dart deleted file mode 100644 index 3752b7b10..000000000 --- a/pkgs/test_api/test/frontend/expect_async_test.dart +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright (c) 2015, 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:fake_async/fake_async.dart'; -import 'package:test/test.dart'; -import 'package:test_api/hooks_testing.dart'; - -import '../utils_new.dart'; - -void main() { - group('supports a function with this many arguments:', () { - test('0', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync0(() { - callbackRun = true; - })(); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test('1', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync1((int arg) { - expect(arg, equals(1)); - callbackRun = true; - })(1); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test('2', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync2((arg1, arg2) { - expect(arg1, equals(1)); - expect(arg2, equals(2)); - callbackRun = true; - })(1, 2); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test('3', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync3((arg1, arg2, arg3) { - expect(arg1, equals(1)); - expect(arg2, equals(2)); - expect(arg3, equals(3)); - callbackRun = true; - })(1, 2, 3); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test('4', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync4((arg1, arg2, arg3, arg4) { - expect(arg1, equals(1)); - expect(arg2, equals(2)); - expect(arg3, equals(3)); - expect(arg4, equals(4)); - callbackRun = true; - })(1, 2, 3, 4); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test('5', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync5((arg1, arg2, arg3, arg4, arg5) { - expect(arg1, equals(1)); - expect(arg2, equals(2)); - expect(arg3, equals(3)); - expect(arg4, equals(4)); - expect(arg5, equals(5)); - callbackRun = true; - })(1, 2, 3, 4, 5); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test('6', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync6((arg1, arg2, arg3, arg4, arg5, arg6) { - expect(arg1, equals(1)); - expect(arg2, equals(2)); - expect(arg3, equals(3)); - expect(arg4, equals(4)); - expect(arg5, equals(5)); - expect(arg6, equals(6)); - callbackRun = true; - })(1, 2, 3, 4, 5, 6); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - }); - - group('with optional arguments', () { - test('allows them to be passed', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync1(([arg = 1]) { - expect(arg, equals(2)); - callbackRun = true; - })(2); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test('allows them not to be passed', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync1(([arg = 1]) { - expect(arg, equals(1)); - callbackRun = true; - })(); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - }); - - group('by default', () { - 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 monitor = await TestCaseMonitor.run(() { - var callback = expectAsync0(() {}); - callback(); - callback(); - }); - - expectTestFailed( - monitor, 'Callback called more times than expected (1).'); - }); - }); - - group('with count', () { - test( - "won't allow the test to complete until it's called at least that " - 'many times', () async { - late void Function() callback; - final monitor = TestCaseMonitor.start(() { - callback = expectAsync0(() {}, count: 3); - }); - - 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 monitor = await TestCaseMonitor.run(() { - var callback = expectAsync0(() {}, count: 3); - callback(); - callback(); - callback(); - callback(); - }); - - expectTestFailed( - monitor, 'Callback called more times than expected (3).'); - }); - - group('0,', () { - test("won't block the test's completion", () { - expectAsync0(() {}, count: 0); - }); - - test("will throw an error if it's ever called", () async { - var monitor = await TestCaseMonitor.run(() { - expectAsync0(() {}, count: 0)(); - }); - - expectTestFailed( - monitor, 'Callback called more times than expected (0).'); - }); - }); - }); - - group('with max', () { - test('will allow the callback to be called that many times', () { - var callback = expectAsync0(() {}, max: 3); - callback(); - callback(); - callback(); - }); - - test('will allow the callback to be called fewer than that many times', () { - var callback = expectAsync0(() {}, max: 3); - callback(); - }); - - test("will throw an error if it's called more than that many times", - () async { - var monitor = await TestCaseMonitor.run(() { - var callback = expectAsync0(() {}, max: 3); - callback(); - callback(); - callback(); - callback(); - }); - - expectTestFailed( - monitor, 'Callback called more times than expected (3).'); - }); - - test('-1, will allow the callback to be called any number of times', () { - var callback = expectAsync0(() {}, max: -1); - for (var i = 0; i < 20; i++) { - callback(); - } - }); - }); - - test('will throw an error if max is less than count', () { - expect(() => expectAsync0(() {}, max: 1, count: 2), throwsArgumentError); - }); - - group('expectAsyncUntil()', () { - test("won't allow the test to complete until isDone returns true", - () async { - late TestCaseMonitor monitor; - late Future future; - monitor = TestCaseMonitor.start(() { - var done = false; - var callback = expectAsyncUntil0(() {}, () => done); - - future = () async { - await pumpEventQueue(); - expect(monitor.state, equals(State.running)); - callback(); - await pumpEventQueue(); - expect(monitor.state, equals(State.running)); - done = true; - callback(); - }(); - }); - await monitor.onDone; - - expectTestPassed(monitor); - // Ensure that the outer test doesn't complete until the inner future - // completes. - await future; - }); - - test("doesn't call isDone until after the callback is called", () { - var callbackRun = false; - expectAsyncUntil0(() => callbackRun = true, () { - expect(callbackRun, isTrue); - return true; - })(); - }); - }); - - test('allows errors', () async { - var monitor = await TestCaseMonitor.run(() { - expect(expectAsync0(() => throw 'oh no'), throwsA('oh no')); - }); - - expectTestPassed(monitor); - }); - - test('may be called in a non-test zone', () async { - var monitor = await TestCaseMonitor.run(() { - var callback = expectAsync0(() {}); - Zone.root.run(callback); - }); - expectTestPassed(monitor); - }); - - test('may be called in a FakeAsync zone that does not run further', () async { - var monitor = await TestCaseMonitor.run(() { - FakeAsync().run((_) { - var callback = expectAsync0(() {}); - callback(); - }); - }); - expectTestPassed(monitor); - }); - - group('old-style expectAsync()', () { - test('works with no arguments', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync(() { - callbackRun = true; - })(); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test('works with dynamic arguments', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync((arg1, arg2) { - callbackRun = true; - })(1, 2); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test('works with non-nullable arguments', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync((int arg1, int arg2) { - callbackRun = true; - })(1, 2); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test('works with 6 arguments', () async { - var callbackRun = false; - var monitor = await TestCaseMonitor.run(() { - expectAsync((arg1, arg2, arg3, arg4, arg5, arg6) { - callbackRun = true; - })(1, 2, 3, 4, 5, 6); - }); - - expectTestPassed(monitor); - expect(callbackRun, isTrue); - }); - - test("doesn't support a function with 7 arguments", () { - // ignore: no_leading_underscores_for_local_identifiers - expect(() => expectAsync((_a, _b, _c, _d, _e, _f, _g) {}), - throwsArgumentError); - }); - }); -} diff --git a/pkgs/test_api/test/frontend/expect_test.dart b/pkgs/test_api/test/frontend/expect_test.dart deleted file mode 100644 index cae351150..000000000 --- a/pkgs/test_api/test/frontend/expect_test.dart +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2017, 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 'package:test/test.dart'; - -import '../utils_new.dart'; - -void main() { - group('returned Future from expectLater()', () { - test('completes immediately for a sync matcher', () { - expect(expectLater(true, isTrue), completes); - }); - - test('contains the expect failure', () { - expect(expectLater(Future.value(true), completion(isFalse)), - throwsA(isTestFailure(anything))); - }); - - test('contains an async error', () { - expect(expectLater(Future.error('oh no'), completion(isFalse)), - throwsA('oh no')); - }); - }); - - group('an async matcher that fails synchronously', () { - test('throws synchronously', () { - expect(() => expect(() {}, throws), throwsA(isTestFailure(anything))); - }); - - test('can be used with synchronous operators', () { - expect(() {}, isNot(throws)); - }); - }); -} diff --git a/pkgs/test_api/test/frontend/matcher/completion_test.dart b/pkgs/test_api/test/frontend/matcher/completion_test.dart deleted file mode 100644 index 4f114af9b..000000000 --- a/pkgs/test_api/test/frontend/matcher/completion_test.dart +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) 2015, 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/test.dart'; -import 'package:test_api/hooks_testing.dart'; - -import '../../utils_new.dart'; - -void main() { - group('[doesNotComplete]', () { - test('fails when provided a non future', () async { - var monitor = await TestCaseMonitor.run(() { - expect(10, doesNotComplete); - }); - - expectTestFailed(monitor, contains('10 is not a Future')); - }); - - test('succeeds when a future does not complete', () { - var completer = Completer(); - expect(completer.future, doesNotComplete); - }); - - test('fails when a future does complete', () async { - var monitor = await TestCaseMonitor.run(() { - var completer = Completer(); - completer.complete(null); - expect(completer.future, doesNotComplete); - }); - - expectTestFailed( - 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 monitor = await TestCaseMonitor.run(() { - var completer = Completer(); - expect(completer.future, doesNotComplete); - completer.complete(null); - }); - - expectTestFailed( - monitor, - 'Future was not expected to complete but completed with a value of' - ' null'); - }); - - test('fails when a future eventually completes', () async { - var monitor = await TestCaseMonitor.run(() { - var completer = Completer(); - expect(completer.future, doesNotComplete); - Future(() async { - await pumpEventQueue(times: 10); - }).then(completer.complete); - }); - - expectTestFailed( - monitor, - 'Future was not expected to complete but completed with a value of' - ' null'); - }); - }); - group('[completes]', () { - test('blocks the test until the Future completes', () async { - final completer = Completer(); - final monitor = TestCaseMonitor.start(() { - expect(completer.future, completes); - }); - await pumpEventQueue(); - expect(monitor.state, State.running); - completer.complete(); - await monitor.onDone; - expectTestPassed(monitor); - }); - - test('with an error', () async { - var monitor = await TestCaseMonitor.run(() { - expect(Future.error('X'), completes); - }); - - expect(monitor.state, equals(State.failed)); - expect(monitor.errors, [isAsyncError(equals('X'))]); - }); - - test('with a failure', () async { - var monitor = await TestCaseMonitor.run(() { - expect(Future.error(TestFailure('oh no')), completes); - }); - - expectTestFailed(monitor, 'oh no'); - }); - - test('with a non-future', () async { - var monitor = await TestCaseMonitor.run(() { - expect(10, completes); - }); - - expectTestFailed( - monitor, - 'Expected: completes successfully\n' - ' Actual: <10>\n' - ' Which: was not a Future\n'); - }); - - test('with a successful future', () { - expect(Future.value('1'), completes); - }); - }); - - group('[completion]', () { - test('blocks the test until the Future completes', () async { - final completer = Completer(); - final monitor = TestCaseMonitor.start(() { - expect(completer.future, completion(isNull)); - }); - await pumpEventQueue(); - expect(monitor.state, State.running); - completer.complete(null); - await monitor.onDone; - expectTestPassed(monitor); - }); - - test('with an error', () async { - var monitor = await TestCaseMonitor.run(() { - expect(Future.error('X'), completion(isNull)); - }); - - expect(monitor.state, equals(State.failed)); - expect(monitor.errors, [isAsyncError(equals('X'))]); - }); - - test('with a failure', () async { - var monitor = await TestCaseMonitor.run(() { - expect(Future.error(TestFailure('oh no')), completion(isNull)); - }); - - expectTestFailed(monitor, 'oh no'); - }); - - test('with a non-future', () async { - var monitor = await TestCaseMonitor.run(() { - expect(10, completion(equals(10))); - }); - - expectTestFailed( - 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 monitor = await TestCaseMonitor.run(() { - expect(Future.value('a'), completion(equals('b'))); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: completes to a value that 'b'\n" - ' Actual: <'), - endsWith('>\n' - " Which: emitted 'a'\n" - ' which is different.\n' - ' Expected: b\n' - ' Actual: a\n' - ' ^\n' - ' Differ at offset 0\n') - ])); - }); - - test("blocks expectLater's Future", () async { - var completer = Completer(); - var fired = false; - unawaited(expectLater(completer.future, completion(equals(1))).then((_) { - fired = true; - })); - - await pumpEventQueue(); - expect(fired, isFalse); - - completer.complete(1); - await pumpEventQueue(); - expect(fired, isTrue); - }); - }); -} diff --git a/pkgs/test_api/test/frontend/matcher/prints_test.dart b/pkgs/test_api/test/frontend/matcher/prints_test.dart deleted file mode 100644 index 337a8124c..000000000 --- a/pkgs/test_api/test/frontend/matcher/prints_test.dart +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) 2014, 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/test.dart'; -import 'package:test_api/hooks_testing.dart'; - -import '../../utils_new.dart'; - -void main() { - group('synchronous', () { - test('passes with an expected print', () { - expect(() => print('Hello, world!'), prints('Hello, world!\n')); - }); - - test('combines multiple prints', () { - expect(() { - print('Hello'); - print('World!'); - }, prints('Hello\nWorld!\n')); - }); - - test('works with a Matcher', () { - expect(() => print('Hello, world!'), prints(contains('Hello'))); - }); - - test('describes a failure nicely', () async { - void local() => print('Hello, world!'); - var monitor = await TestCaseMonitor.run(() { - expect(local, prints('Goodbye, world!\n')); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: prints 'Goodbye, world!\\n'\n" - " ''\n" - ' Actual: <'), - endsWith('>\n' - " Which: printed 'Hello, world!\\n'\n" - " ''\n" - ' which is different.\n' - ' Expected: Goodbye, w ...\n' - ' Actual: Hello, wor ...\n' - ' ^\n' - ' Differ at offset 0\n') - ])); - }); - - test('describes a failure with a non-descriptive Matcher nicely', () async { - void local() => print('Hello, world!'); - var monitor = await TestCaseMonitor.run(() { - expect(local, prints(contains('Goodbye'))); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: prints contains 'Goodbye'\n" - ' Actual: <'), - endsWith('>\n' - " Which: printed 'Hello, world!\\n'\n" - " ''\n") - ])); - }); - - test('describes a failure with no text nicely', () async { - void local() {} - var monitor = await TestCaseMonitor.run(() { - expect(local, prints(contains('Goodbye'))); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: prints contains 'Goodbye'\n" - ' Actual: <'), - endsWith('>\n' - ' Which: printed nothing\n') - ])); - }); - - test('with a non-function', () async { - var monitor = await TestCaseMonitor.run(() { - expect(10, prints(contains('Goodbye'))); - }); - - expectTestFailed( - monitor, - "Expected: prints contains 'Goodbye'\n" - ' Actual: <10>\n' - ' Which: was not a unary Function\n'); - }); - }); - - group('asynchronous', () { - test('passes with an expected print', () { - expect(() => Future(() => print('Hello, world!')), - prints('Hello, world!\n')); - }); - - test('combines multiple prints', () { - expect( - () => Future(() { - print('Hello'); - print('World!'); - }), - prints('Hello\nWorld!\n')); - }); - - test('works with a Matcher', () { - expect(() => Future(() => print('Hello, world!')), - prints(contains('Hello'))); - }); - - test('describes a failure nicely', () async { - void local() => Future(() => print('Hello, world!')); - var monitor = await TestCaseMonitor.run(() { - expect(local, prints('Goodbye, world!\n')); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: prints 'Goodbye, world!\\n'\n" - " ''\n" - ' Actual: <'), - contains('>\n' - " Which: printed 'Hello, world!\\n'\n" - " ''\n" - ' which is different.\n' - ' Expected: Goodbye, w ...\n' - ' Actual: Hello, wor ...\n' - ' ^\n' - ' Differ at offset 0') - ])); - }); - - test('describes a failure with a non-descriptive Matcher nicely', () async { - void local() => Future(() => print('Hello, world!')); - var monitor = await TestCaseMonitor.run(() { - expect(local, prints(contains('Goodbye'))); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: prints contains 'Goodbye'\n" - ' Actual: <'), - contains('>\n' - " Which: printed 'Hello, world!\\n'\n" - " ''") - ])); - }); - - test('describes a failure with no text nicely', () async { - void local() => Future.value(); - var monitor = await TestCaseMonitor.run(() { - expect(local, prints(contains('Goodbye'))); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: prints contains 'Goodbye'\n" - ' Actual: <'), - contains('>\n' - ' Which: printed nothing') - ])); - }); - - test("won't let the test end until the Future completes", () async { - final completer = Completer(); - final monitor = TestCaseMonitor.start(() { - expect(() => completer.future, prints(isEmpty)); - }); - await pumpEventQueue(); - expect(monitor.state, State.running); - completer.complete(); - await monitor.onDone; - expectTestPassed(monitor); - }); - - test("blocks expectLater's Future", () async { - var completer = Completer(); - var fired = false; - - unawaited(expectLater(() { - scheduleMicrotask(() => print('hello!')); - return completer.future; - }, prints('hello!\n')) - .then((_) { - fired = true; - })); - - await pumpEventQueue(); - expect(fired, isFalse); - - completer.complete(); - await pumpEventQueue(); - expect(fired, isTrue); - }); - }); -} diff --git a/pkgs/test_api/test/frontend/matcher/throws_test.dart b/pkgs/test_api/test/frontend/matcher/throws_test.dart deleted file mode 100644 index a9e76af3a..000000000 --- a/pkgs/test_api/test/frontend/matcher/throws_test.dart +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (c) 2015, 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/test.dart'; -import 'package:test_api/hooks_testing.dart'; - -import '../../utils_new.dart'; - -void main() { - group('synchronous', () { - group('[throws]', () { - test('with a function that throws an error', () { - expect(() => throw 'oh no', throws); - }); - - test("with a function that doesn't throw", () async { - void local() {} - var monitor = await TestCaseMonitor.run(() { - expect(local, throws); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith('Expected: throws\n' - ' Actual: <'), - endsWith('>\n' - ' Which: returned \n') - ])); - }); - - test('with a non-function', () async { - var monitor = await TestCaseMonitor.run(() { - expect(10, throws); - }); - - expectTestFailed( - monitor, - 'Expected: throws\n' - ' Actual: <10>\n' - ' Which: was not a Function or Future\n'); - }); - }); - - group('[throwsA]', () { - test('with a function that throws an identical error', () { - expect(() => throw 'oh no', throwsA('oh no')); - }); - - test('with a function that throws a matching error', () { - expect(() => throw FormatException('bad'), throwsA(isFormatException)); - }); - - test("with a function that doesn't throw", () async { - void local() {} - var monitor = await TestCaseMonitor.run(() { - expect(local, throwsA('oh no')); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: throws 'oh no'\n" - ' Actual: <'), - endsWith('>\n' - ' Which: returned \n') - ])); - }); - - test('with a non-function', () async { - var monitor = await TestCaseMonitor.run(() { - expect(10, throwsA('oh no')); - }); - - expectTestFailed( - 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 monitor = await TestCaseMonitor.run(() { - expect(() => throw 'aw dang', throwsA('oh no')); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: throws 'oh no'\n" - ' Actual: <'), - contains('>\n' - " Which: threw 'aw dang'\n" - ' stack'), - endsWith(' which is different.\n' - ' Expected: oh no\n' - ' Actual: aw dang\n' - ' ^\n' - ' Differ at offset 0\n') - ])); - }); - }); - }); - - group('asynchronous', () { - group('[throws]', () { - test('with a Future that throws an error', () { - expect(Future.error('oh no'), throws); - }); - - test("with a Future that doesn't throw", () async { - var monitor = await TestCaseMonitor.run(() { - expect(Future.value(), throws); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith('Expected: throws\n' - ' Actual: <'), - endsWith('>\n' - ' Which: emitted \n') - ])); - }); - - test('with a closure that returns a Future that throws an error', () { - expect(() => Future.error('oh no'), throws); - }); - - test("with a closure that returns a Future that doesn't throw", () async { - var monitor = await TestCaseMonitor.run(() { - expect(() => Future.value(), throws); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith('Expected: throws\n' - ' Actual: <'), - endsWith('>\n' - ' Which: returned a Future that emitted \n') - ])); - }); - - test("won't let the test end until the Future completes", () async { - late void Function() callback; - final monitor = TestCaseMonitor.start(() { - final completer = Completer(); - expect(completer.future, throws); - callback = () => completer.completeError('oh no'); - }); - await pumpEventQueue(); - expect(monitor.state, State.running); - callback(); - await monitor.onDone; - expectTestPassed(monitor); - }); - }); - - group('[throwsA]', () { - test('with a Future that throws an identical error', () { - expect(Future.error('oh no'), throwsA('oh no')); - }); - - test('with a Future that throws a matching error', () { - expect( - Future.error(FormatException('bad')), throwsA(isFormatException)); - }); - - test("with a Future that doesn't throw", () async { - var monitor = await TestCaseMonitor.run(() { - expect(Future.value(), throwsA('oh no')); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: throws 'oh no'\n" - ' Actual: <'), - endsWith('>\n' - ' Which: emitted \n') - ])); - }); - - test('with a Future that throws the wrong error', () async { - var monitor = await TestCaseMonitor.run(() { - expect(Future.error('aw dang'), throwsA('oh no')); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: throws 'oh no'\n" - ' Actual: <'), - contains('>\n' - " Which: threw 'aw dang'\n") - ])); - }); - - test('with a closure that returns a Future that throws a matching error', - () { - expect(() => Future.error(FormatException('bad')), - throwsA(isFormatException)); - }); - - test("with a closure that returns a Future that doesn't throw", () async { - var monitor = await TestCaseMonitor.run(() { - expect(() => Future.value(), throwsA('oh no')); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: throws 'oh no'\n" - ' Actual: <'), - endsWith('>\n' - ' Which: returned a Future that emitted \n') - ])); - }); - - test('with closure that returns a Future that throws the wrong error', - () async { - var monitor = await TestCaseMonitor.run(() { - expect(() => Future.error('aw dang'), throwsA('oh no')); - }); - - expectTestFailed( - monitor, - allOf([ - startsWith("Expected: throws 'oh no'\n" - ' Actual: <'), - contains('>\n' - " Which: threw 'aw dang'\n") - ])); - }); - - test("won't let the test end until the Future completes", () async { - late void Function() callback; - final monitor = TestCaseMonitor.start(() { - final completer = Completer(); - expect(completer.future, throwsA('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 { - var completer = Completer(); - var fired = false; - unawaited(expectLater(completer.future, throwsArgumentError).then((_) { - fired = true; - })); - - await pumpEventQueue(); - expect(fired, isFalse); - - completer.completeError(ArgumentError('oh no')); - await pumpEventQueue(); - expect(fired, isTrue); - }); - }); - }); -} diff --git a/pkgs/test_api/test/frontend/matcher/throws_type_test.dart b/pkgs/test_api/test/frontend/matcher/throws_type_test.dart deleted file mode 100644 index dfad890b9..000000000 --- a/pkgs/test_api/test/frontend/matcher/throws_type_test.dart +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2015, 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 'package:test/test.dart'; -import 'package:test_api/hooks_testing.dart'; - -import '../../utils_new.dart'; - -void main() { - group('[throwsArgumentError]', () { - test('passes when a ArgumentError is thrown', () { - expect(() => throw ArgumentError(''), throwsArgumentError); - }); - - test('fails when a non-ArgumentError is thrown', () async { - var liveTest = await TestCaseMonitor.run(() { - expect(() => throw Exception(), throwsArgumentError); - }); - - expectTestFailed(liveTest, - startsWith("Expected: throws ")); - }); - }); - - group('[throwsConcurrentModificationError]', () { - test('passes when a ConcurrentModificationError is thrown', () { - expect(() => throw ConcurrentModificationError(''), - throwsConcurrentModificationError); - }); - - test('fails when a non-ConcurrentModificationError is thrown', () async { - var liveTest = await TestCaseMonitor.run(() { - expect(() => throw Exception(), throwsConcurrentModificationError); - }); - - expectTestFailed( - liveTest, - startsWith( - "Expected: throws ")); - }); - }); - - group('[throwsCyclicInitializationError]', () { - test('passes when a CyclicInitializationError is thrown', () { - expect( - () => _CyclicInitializationFailure().x, - // ignore: deprecated_member_use - throwsCyclicInitializationError); - }); - - test('fails when a non-CyclicInitializationError is thrown', () async { - var liveTest = await TestCaseMonitor.run(() { - expect(() => throw Exception(), throwsCyclicInitializationError); - }); - - expectTestFailed( - liveTest, startsWith("Expected: throws ")); - }); - }); - - group('[throwsException]', () { - test('passes when a Exception is thrown', () { - expect(() => throw Exception(''), throwsException); - }); - - test('fails when a non-Exception is thrown', () async { - var liveTest = await TestCaseMonitor.run(() { - expect(() => throw 'oh no', throwsException); - }); - - expectTestFailed( - liveTest, startsWith("Expected: throws ")); - }); - }); - - group('[throwsFormatException]', () { - test('passes when a FormatException is thrown', () { - expect(() => throw FormatException(''), throwsFormatException); - }); - - test('fails when a non-FormatException is thrown', () async { - var liveTest = await TestCaseMonitor.run(() { - expect(() => throw Exception(), throwsFormatException); - }); - - expectTestFailed(liveTest, - startsWith("Expected: throws ")); - }); - }); - - group('[throwsNoSuchMethodError]', () { - test('passes when a NoSuchMethodError is thrown', () { - expect(() { - (1 as dynamic).notAMethodOnInt(); - }, throwsNoSuchMethodError); - }); - - test('fails when a non-NoSuchMethodError is thrown', () async { - var liveTest = await TestCaseMonitor.run(() { - expect(() => throw Exception(), throwsNoSuchMethodError); - }); - - expectTestFailed(liveTest, - startsWith("Expected: throws ")); - }); - }); - - group('[throwsRangeError]', () { - test('passes when a RangeError is thrown', () { - expect(() => throw RangeError(''), throwsRangeError); - }); - - test('fails when a non-RangeError is thrown', () async { - var liveTest = await TestCaseMonitor.run(() { - expect(() => throw Exception(), throwsRangeError); - }); - - expectTestFailed( - liveTest, startsWith("Expected: throws ")); - }); - }); - - group('[throwsStateError]', () { - test('passes when a StateError is thrown', () { - expect(() => throw StateError(''), throwsStateError); - }); - - test('fails when a non-StateError is thrown', () async { - var liveTest = await TestCaseMonitor.run(() { - expect(() => throw Exception(), throwsStateError); - }); - - expectTestFailed( - liveTest, startsWith("Expected: throws ")); - }); - }); - - group('[throwsUnimplementedError]', () { - test('passes when a UnimplementedError is thrown', () { - expect(() => throw UnimplementedError(''), throwsUnimplementedError); - }); - - test('fails when a non-UnimplementedError is thrown', () async { - var liveTest = await TestCaseMonitor.run(() { - expect(() => throw Exception(), throwsUnimplementedError); - }); - - expectTestFailed(liveTest, - startsWith("Expected: throws ")); - }); - }); - - group('[throwsUnsupportedError]', () { - test('passes when a UnsupportedError is thrown', () { - expect(() => throw UnsupportedError(''), throwsUnsupportedError); - }); - - test('fails when a non-UnsupportedError is thrown', () async { - var liveTest = await TestCaseMonitor.run(() { - expect(() => throw Exception(), throwsUnsupportedError); - }); - - expectTestFailed(liveTest, - startsWith("Expected: throws ")); - }); - }); -} - -class _CyclicInitializationFailure { - late int x = y; - late int y = x; -} diff --git a/pkgs/test_api/test/frontend/never_called_test.dart b/pkgs/test_api/test/frontend/never_called_test.dart deleted file mode 100644 index f4160a340..000000000 --- a/pkgs/test_api/test/frontend/never_called_test.dart +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2017, 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 'package:term_glyph/term_glyph.dart' as glyph; -import 'package:test/test.dart'; -import 'package:test_api/hooks_testing.dart'; - -import '../utils_new.dart'; - -void main() { - setUpAll(() { - glyph.ascii = true; - }); - - test("doesn't throw if it isn't called", () async { - var monitor = await TestCaseMonitor.run(() { - const Stream.empty().listen(neverCalled); - }); - - expectTestPassed(monitor); - }); - - group("if it's called", () { - test('throws', () async { - var monitor = await TestCaseMonitor.run(() { - neverCalled(); - }); - - expectTestFailed( - monitor, - 'Callback should never have been called, but it was called with no ' - 'arguments.'); - }); - - test('pretty-prints arguments', () async { - var monitor = await TestCaseMonitor.run(() { - neverCalled(1, 'foo\nbar'); - }); - - expectTestFailed( - monitor, - 'Callback should never have been called, but it was called with:\n' - '* <1>\n' - "* 'foo\\n'\n" - " 'bar'"); - }); - - test('keeps the test alive', () async { - var monitor = await TestCaseMonitor.run(() { - pumpEventQueue(times: 10).then(neverCalled); - }); - - expectTestFailed( - monitor, - 'Callback should never have been called, but it was called with:\n' - '* '); - }); - - test("can't be caught", () async { - var monitor = await TestCaseMonitor.run(() { - try { - neverCalled(); - } catch (_) { - // Do nothing. - } - }); - - expectTestFailed( - 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 deleted file mode 100644 index 37d37bf82..000000000 --- a/pkgs/test_api/test/frontend/stream_matcher_test.dart +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright (c) 2017, 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:async/async.dart'; -import 'package:term_glyph/term_glyph.dart' as glyph; -import 'package:test/test.dart'; - -import '../utils_new.dart'; - -void main() { - setUpAll(() { - glyph.ascii = true; - }); - - late Stream stream; - late StreamQueue queue; - late Stream errorStream; - late StreamQueue errorQueue; - setUp(() { - stream = Stream.fromIterable([1, 2, 3, 4, 5]); - queue = StreamQueue(Stream.fromIterable([1, 2, 3, 4, 5])); - errorStream = Stream.fromFuture(Future.error('oh no!', StackTrace.current)); - errorQueue = StreamQueue( - Stream.fromFuture(Future.error('oh no!', StackTrace.current))); - }); - - group('emits()', () { - test('matches the first event of a Stream', () { - expect(stream, emits(1)); - }); - - test('rejects the first event of a Stream', () { - expect( - expectLater(stream, emits(2)), - throwsTestFailure(allOf([ - startsWith('Expected: should emit an event that <2>\n'), - endsWith(' Which: emitted * 1\n' - ' * 2\n' - ' * 3\n' - ' * 4\n' - ' * 5\n' - ' x Stream closed.\n') - ]))); - }); - - test('matches and consumes the next event of a StreamQueue', () { - expect(queue, emits(1)); - expect(queue.next, completion(equals(2))); - expect(queue, emits(3)); - expect(queue.next, completion(equals(4))); - }); - - test('rejects and does not consume the first event of a StreamQueue', () { - expect( - expectLater(queue, emits(2)), - throwsTestFailure(allOf([ - startsWith('Expected: should emit an event that <2>\n'), - endsWith(' Which: emitted * 1\n' - ' * 2\n' - ' * 3\n' - ' * 4\n' - ' * 5\n' - ' x Stream closed.\n') - ]))); - - expect(queue, emits(1)); - }); - - test('rejects an empty stream', () { - expect( - expectLater(Stream.empty(), emits(1)), - throwsTestFailure(allOf([ - startsWith('Expected: should emit an event that <1>\n'), - endsWith(' Which: emitted x Stream closed.\n') - ]))); - }); - - test('forwards a stream error', () { - expect(expectLater(errorStream, emits(1)), throwsA('oh no!')); - }); - - test('wraps a normal matcher', () { - expect(queue, emits(lessThan(5))); - expect(expectLater(queue, emits(greaterThan(5))), - throwsTestFailure(anything)); - }); - - test('returns a StreamMatcher as-is', () { - expect(queue, emits(emitsThrough(4))); - expect(queue, emits(5)); - }); - }); - - group('emitsDone', () { - test('succeeds for an empty stream', () { - expect(Stream.empty(), emitsDone); - }); - - test('fails for a stream with events', () { - expect( - expectLater(stream, emitsDone), - throwsTestFailure(allOf([ - startsWith('Expected: should be done\n'), - endsWith(' Which: emitted * 1\n' - ' * 2\n' - ' * 3\n' - ' * 4\n' - ' * 5\n' - ' x Stream closed.\n') - ]))); - }); - }); - - group('emitsError()', () { - test('consumes a matching error', () { - expect(errorQueue, emitsError('oh no!')); - expect(errorQueue.hasNext, completion(isFalse)); - }); - - test('fails for a non-matching error', () { - expect( - expectLater(errorStream, emitsError('oh heck')), - throwsTestFailure(allOf([ - startsWith("Expected: should emit an error that 'oh heck'\n"), - contains(' Which: emitted ! oh no!\n'), - contains(' x Stream closed.\n' - " which threw 'oh no!'\n" - ' stack '), - endsWith(' which is different.\n' - ' Expected: oh heck\n' - ' Actual: oh no!\n' - ' ^\n' - ' Differ at offset 3\n') - ]))); - }); - - test('fails for a stream with events', () { - expect( - expectLater(stream, emitsDone), - throwsTestFailure(allOf([ - startsWith('Expected: should be done\n'), - endsWith(' Which: emitted * 1\n' - ' * 2\n' - ' * 3\n' - ' * 4\n' - ' * 5\n' - ' x Stream closed.\n') - ]))); - }); - }); - - group('mayEmit()', () { - test('consumes a matching event', () { - expect(queue, mayEmit(1)); - expect(queue, emits(2)); - }); - - test('allows a non-matching event', () { - expect(queue, mayEmit('fish')); - expect(queue, emits(1)); - }); - }); - - group('emitsAnyOf()', () { - test('consumes an event that matches a matcher', () { - expect(queue, emitsAnyOf([2, 1, 3])); - expect(queue, emits(2)); - }); - - test('consumes as many events as possible', () { - expect( - queue, - emitsAnyOf([ - 1, - emitsInOrder([1, 2]), - emitsInOrder([1, 2, 3]) - ])); - - expect(queue, emits(4)); - }); - - test('fails if no matchers match', () { - expect( - expectLater(stream, emitsAnyOf([2, 3, 4])), - throwsTestFailure(allOf([ - startsWith('Expected: should do one of the following:\n' - ' * emit an event that <2>\n' - ' * emit an event that <3>\n' - ' * emit an event that <4>\n'), - endsWith(' Which: emitted * 1\n' - ' * 2\n' - ' * 3\n' - ' * 4\n' - ' * 5\n' - ' x Stream closed.\n' - ' which failed all options:\n' - ' * failed to emit an event that <2>\n' - ' * failed to emit an event that <3>\n' - ' * failed to emit an event that <4>\n') - ]))); - }); - - test('allows an error if any matcher matches', () { - expect(errorStream, emitsAnyOf([1, 2, emitsError('oh no!')])); - }); - - test('rethrows an error if no matcher matches', () { - expect( - expectLater(errorStream, emitsAnyOf([1, 2, 3])), throwsA('oh no!')); - }); - }); - - group('emitsInOrder()', () { - test('consumes matching events', () { - expect(queue, emitsInOrder([1, 2, emitsThrough(4)])); - expect(queue, emits(5)); - }); - - test("fails if the matchers don't match in order", () { - expect( - expectLater(queue, emitsInOrder([1, 3, 2])), - throwsTestFailure(allOf([ - startsWith('Expected: should do the following in order:\n' - ' * emit an event that <1>\n' - ' * emit an event that <3>\n' - ' * emit an event that <2>\n'), - endsWith(' Which: emitted * 1\n' - ' * 2\n' - ' * 3\n' - ' * 4\n' - ' * 5\n' - ' x Stream closed.\n' - " which didn't emit an event that <3>\n") - ]))); - }); - }); - - group('emitsThrough()', () { - test('consumes events including those matching the matcher', () { - expect(queue, emitsThrough(emitsInOrder([3, 4]))); - expect(queue, emits(5)); - }); - - test('consumes the entire queue with emitsDone', () { - expect(queue, emitsThrough(emitsDone)); - expect(queue.hasNext, completion(isFalse)); - }); - - test('fails if the queue never matches the matcher', () { - expect( - expectLater(queue, emitsThrough(6)), - throwsTestFailure(allOf([ - startsWith('Expected: should eventually emit an event that <6>\n'), - endsWith(' Which: emitted * 1\n' - ' * 2\n' - ' * 3\n' - ' * 4\n' - ' * 5\n' - ' x Stream closed.\n' - ' which never did emit an event that <6>\n') - ]))); - }); - }); - - group('mayEmitMultiple()', () { - test('consumes multiple instances of the given matcher', () { - expect(queue, mayEmitMultiple(lessThan(3))); - expect(queue, emits(3)); - }); - - test('consumes zero instances of the given matcher', () { - expect(queue, mayEmitMultiple(6)); - expect(queue, emits(1)); - }); - - test("doesn't rethrow errors", () { - expect(errorQueue, mayEmitMultiple(1)); - expect(errorQueue, emitsError('oh no!')); - }); - }); - - group('neverEmits()', () { - test('succeeds if the event never matches', () { - expect(queue, neverEmits(6)); - expect(queue, emits(1)); - }); - - test('fails if the event matches', () { - expect( - expectLater(stream, neverEmits(4)), - throwsTestFailure(allOf([ - startsWith('Expected: should never emit an event that <4>\n'), - endsWith(' Which: emitted * 1\n' - ' * 2\n' - ' * 3\n' - ' * 4\n' - ' * 5\n' - ' x Stream closed.\n' - ' which after 3 events did emit an event that <4>\n') - ]))); - }); - - test('fails if emitsDone matches', () { - expect(expectLater(stream, neverEmits(emitsDone)), - throwsTestFailure(anything)); - }); - - test("doesn't rethrow errors", () { - expect(errorQueue, neverEmits(6)); - expect(errorQueue, emitsError('oh no!')); - }); - }); - - group('emitsInAnyOrder()', () { - test('consumes events that match in any order', () { - expect(queue, emitsInAnyOrder([3, 1, 2])); - expect(queue, emits(4)); - }); - - test("fails if the events don't match in any order", () { - expect( - expectLater(stream, emitsInAnyOrder([4, 1, 2])), - throwsTestFailure(allOf([ - startsWith('Expected: should do the following in any order:\n' - ' * emit an event that <4>\n' - ' * emit an event that <1>\n' - ' * emit an event that <2>\n'), - endsWith(' Which: emitted * 1\n' - ' * 2\n' - ' * 3\n' - ' * 4\n' - ' * 5\n' - ' x Stream closed.\n') - ]))); - }); - - test("doesn't rethrow if some ordering matches", () { - expect(errorQueue, emitsInAnyOrder([emitsDone, emitsError('oh no!')])); - }); - - test('rethrows if no ordering matches', () { - expect( - expectLater(errorQueue, emitsInAnyOrder([1, emitsError('oh no!')])), - throwsA('oh no!')); - }); - }); - - test('A custom StreamController doesn\'t hang on close', () async { - var controller = StreamController(); - var done = expectLater(controller.stream, emits(null)); - controller.add(null); - await done; - await controller.close(); - }); -} diff --git a/pkgs/test_api/test/utils_new.dart b/pkgs/test_api/test/utils_new.dart deleted file mode 100644 index 5aa69749e..000000000 --- a/pkgs/test_api/test/utils_new.dart +++ /dev/null @@ -1,49 +0,0 @@ -// 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().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() - .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)); diff --git a/pkgs/test_core/CHANGELOG.md b/pkgs/test_core/CHANGELOG.md index 376afec8b..3de41517e 100644 --- a/pkgs/test_core/CHANGELOG.md +++ b/pkgs/test_core/CHANGELOG.md @@ -27,6 +27,7 @@ not statically breaking but it will be confusing for users if it isn't supported. * **BREAKING** Remove `useDataIsolateStrategy` field from `Configuration`. +* **BREAKING** Stop exporting APIs from `package:matcher/expect.dart`. ## 0.4.24 diff --git a/pkgs/test_core/lib/test_core.dart b/pkgs/test_core/lib/test_core.dart index c60358629..55c7a5051 100644 --- a/pkgs/test_core/lib/test_core.dart +++ b/pkgs/test_core/lib/test_core.dart @@ -6,12 +6,8 @@ 'Please use package:test.') library test_core; -export 'package:test_api/expect.dart'; export 'package:test_api/hooks.dart' show TestFailure; // Not yet deprecated, but not exposed through focused libraries. export 'package:test_api/test_api.dart' show registerException; -// Deprecated exports not surfaced through focused libraries. -export 'package:test_api/test_api.dart' - show ErrorFormatter, expectAsync, throws, Throws; export 'scaffolding.dart'; diff --git a/pkgs/test_core/pubspec_overrides.yaml b/pkgs/test_core/pubspec_overrides.yaml index 2a7f631aa..2cf7e0a16 100644 --- a/pkgs/test_core/pubspec_overrides.yaml +++ b/pkgs/test_core/pubspec_overrides.yaml @@ -1,3 +1,7 @@ dependency_overrides: test_api: path: ../test_api + matcher: + git: + url: https://github.com/dart-lang/matcher.git + ref: 3fc9b3c35c3c0465ebef9158971f8203a4fc9416 From 6c134af8bf7401d48163788ec72ec69e58feff8f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 9 Mar 2023 21:06:00 +0000 Subject: [PATCH 2/9] Update test expectations Remove tests for imports in removed libraries. --- .../test/runner/github_reporter_test.dart | 5 +-- pkgs/test/test/runner/json_reporter_test.dart | 6 ++-- pkgs/test/test/runner/skip_expect_test.dart | 12 +++---- pkgs/test_api/lib/src/backend/invoker.dart | 4 +-- pkgs/test_api/test/backend/invoker_test.dart | 4 +-- .../test/import_restrictions_test.dart | 33 ------------------- 6 files changed, 16 insertions(+), 48 deletions(-) diff --git a/pkgs/test/test/runner/github_reporter_test.dart b/pkgs/test/test/runner/github_reporter_test.dart index 96bc86371..c4c75b3bc 100644 --- a/pkgs/test/test/runner/github_reporter_test.dart +++ b/pkgs/test/test/runner/github_reporter_test.dart @@ -154,8 +154,9 @@ void main() { test.dart 8:62 main.. ::endgroup:: ::group::❌ fail after completion (failed after test completion) - This test failed after it had already completed. Make sure to use [expectAsync] - or the [completes] matcher when testing async code. + This test failed after it had already completed. + Make sure to use a matching library which informs the test runner + of pending async work. test.dart 8:62 main.. ::endgroup:: ✅ second test so that the first failure is reported diff --git a/pkgs/test/test/runner/json_reporter_test.dart b/pkgs/test/test/runner/json_reporter_test.dart index 029d55731..6ddcabf1f 100644 --- a/pkgs/test/test/runner/json_reporter_test.dart +++ b/pkgs/test/test/runner/json_reporter_test.dart @@ -172,9 +172,9 @@ void main() { errorJson(3, 'oh no'), errorJson( 3, - 'This test failed after it had already completed. Make sure to ' - 'use [expectAsync]\n' - 'or the [completes] matcher when testing async code.'), + 'This test failed after it had already completed.\n' + 'Make sure to use a matching library which informs the ' + 'test runner\nof pending async work.'), testDoneJson(4), ] ], doneJson(success: false)); diff --git a/pkgs/test/test/runner/skip_expect_test.dart b/pkgs/test/test/runner/skip_expect_test.dart index 97e1330af..41376e3f7 100644 --- a/pkgs/test/test/runner/skip_expect_test.dart +++ b/pkgs/test/test/runner/skip_expect_test.dart @@ -197,9 +197,9 @@ void main() { '+0: skip', '+1: wait', '+0 -1: skip', - 'This test was marked as skipped after it had already completed. ' - 'Make sure to use', - '[expectAsync] or the [completes] matcher when testing async code.', + 'This test was marked as skipped after it had already completed.', + 'Make sure to use a matching library which informs the test runner', + 'of pending async work.', '+1 -1: Some tests failed.' ])); await test.shouldExit(1); @@ -239,9 +239,9 @@ void main() { '+0: skip', '+1: wait', '+0 -1: skip', - 'This test was marked as skipped after it had already completed. ' - 'Make sure to use', - '[expectAsync] or the [completes] matcher when testing async code.', + 'This test was marked as skipped after it had already completed.', + 'Make sure to use a matching library which informs the test runner', + 'of pending async work.', '+1 -1: Some tests failed.' ])); await test.shouldExit(1); diff --git a/pkgs/test_api/lib/src/backend/invoker.dart b/pkgs/test_api/lib/src/backend/invoker.dart index d223ec159..0274f90ac 100644 --- a/pkgs/test_api/lib/src/backend/invoker.dart +++ b/pkgs/test_api/lib/src/backend/invoker.dart @@ -306,7 +306,7 @@ class Invoker { // failing after being complete. _controller.setState(const State(Status.complete, Result.error)); throw 'This test was marked as skipped after it had already completed.\n' - 'Make sure to use a matching library which informs the test runner ' + 'Make sure to use a matching library which informs the test runner\n' 'of pending async work.'; } @@ -368,7 +368,7 @@ class Invoker { _handleError( zone, 'This test failed after it had already completed.\n' - 'Make sure to use a matching library which informs the test runner ' + 'Make sure to use a matching library which informs the test runner\n' 'of pending async work.', stackTrace); } diff --git a/pkgs/test_api/test/backend/invoker_test.dart b/pkgs/test_api/test/backend/invoker_test.dart index d2ba0de80..b2de0ee86 100644 --- a/pkgs/test_api/test/backend/invoker_test.dart +++ b/pkgs/test_api/test/backend/invoker_test.dart @@ -185,8 +185,8 @@ void main() { expect( error, equals('This test failed after it had already completed.\n' - 'Make sure to use a matching library which informs the test runner ' - 'of pending async work.')); + 'Make sure to use a matching library which informs the ' + 'test runner\nof pending async work.')); } ]); diff --git a/pkgs/test_api/test/import_restrictions_test.dart b/pkgs/test_api/test/import_restrictions_test.dart index f07c4c6f3..7dfe3d3fa 100644 --- a/pkgs/test_api/test/import_restrictions_test.dart +++ b/pkgs/test_api/test/import_restrictions_test.dart @@ -38,39 +38,6 @@ void main() { } }); }); - - group('expect', () { - test('must not be imported from any other library', () async { - final entryPoints = [ - _testApiLibrary('hooks.dart'), - _testApiLibrary('scaffolding.dart'), - _testApiLibrary('fake.dart') - ]; - await for (final source - in importCheck.transitiveSamePackageSources(entryPoints)) { - for (final import in source.imports) { - expect(import.path, isNot(contains('test_api.dart')), - reason: 'Invalid import from ${source.uri} : $import.'); - expect(import.path, isNot(contains('expect')), - reason: 'Invalid import from ${source.uri} : $import.'); - } - } - }); - - test('may only import hooks', () async { - final entryPoint = _testApiLibrary('expect.dart'); - await for (final source - in importCheck.transitiveSamePackageSources([entryPoint])) { - // Transitive imports through `hooks.dart` don't follow this restriction - if (!source.uri.path.contains('expect')) continue; - for (final import in source.imports) { - expect(import.path, - anyOf(['test_api/hooks.dart', startsWith('test_api/src/expect')]), - reason: 'Invalid import from ${source.uri} : $import'); - } - } - }); - }); } Uri _testApiLibrary(String path) => Uri.parse('package:test_api/$path'); From 8f1aaaa7e4a46a772f0ecefc5a23f6e37016f75a Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 9 Mar 2023 21:38:15 +0000 Subject: [PATCH 3/9] Another expectation --- pkgs/test_api/test/backend/invoker_test.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pkgs/test_api/test/backend/invoker_test.dart b/pkgs/test_api/test/backend/invoker_test.dart index b2de0ee86..55e697b25 100644 --- a/pkgs/test_api/test/backend/invoker_test.dart +++ b/pkgs/test_api/test/backend/invoker_test.dart @@ -315,10 +315,9 @@ void main() { (error) { expect( error, - equals( - 'This test failed after it had already completed. Make sure to ' - 'use [expectAsync]\n' - 'or the [completes] matcher when testing async code.')); + equals('This test failed after it had already completed.\n' + 'Make sure to use a matching library which informs the ' + 'test runner\nof pending async work.')); } ]); From f4adc0631f302f4a39a9fc54e630dbc7b83e2976 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 10 Mar 2023 23:09:52 +0000 Subject: [PATCH 4/9] Overzealous delete? --- pkgs/test/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pkgs/test/README.md b/pkgs/test/README.md index 35b6296af..9b4aef8e2 100644 --- a/pkgs/test/README.md +++ b/pkgs/test/README.md @@ -91,6 +91,32 @@ void main() { } ``` +You can use the [`setUp()`] and [`tearDown()`] functions to share code between +tests. The `setUp()` callback will run before every test in a group or test +suite, and `tearDown()` will run after. `tearDown()` will run even if a test +fails, to ensure that it has a chance to clean up after itself. + +```dart +import 'package:test/test.dart'; + +void main() { + late HttpServer server; + late Uri url; + setUp(() async { + server = await HttpServer.bind('localhost', 0); + url = Uri.parse('http://${server.address.host}:${server.port}'); + }); + + tearDown(() async { + await server.close(force: true); + server = null; + url = null; + }); + + // ... +} +``` + [`setUp()`]: https://pub.dev/documentation/test_core/latest/test_core.scaffolding/setUp.html [`tearDown()`]: https://pub.dev/documentation/test_core/latest/test_core.scaffolding/tearDown.html From 8a1ccb3f275108519210f89126797868446234bd Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 16 Mar 2023 19:12:41 +0000 Subject: [PATCH 5/9] Restore export from test_api Will make it easier to incrementally roll to flutter --- pkgs/test_api/CHANGELOG.md | 4 ++-- pkgs/test_api/lib/expect.dart | 5 +++++ pkgs/test_api/lib/test_api.dart | 2 ++ pkgs/test_api/pubspec.yaml | 4 ++++ 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 pkgs/test_api/lib/expect.dart diff --git a/pkgs/test_api/CHANGELOG.md b/pkgs/test_api/CHANGELOG.md index 5c6394329..f90dbc203 100644 --- a/pkgs/test_api/CHANGELOG.md +++ b/pkgs/test_api/CHANGELOG.md @@ -8,8 +8,8 @@ to `Runtime`. * Add `package:test_api/hooks_testing.dart` library for writing tests against code that uses `package:test_api/hooks.dart`. -* **BREAKING** Remove the `package:test_api/expect.dart` library and stop - exporting any of the old APIs it had from the main library. +* **BREAKING** Remove `ErrorFormatter`, `expectAsync`, `throws`, and `Throws` + from `package:test_api/test_api.dart`. ## 0.4.18 diff --git a/pkgs/test_api/lib/expect.dart b/pkgs/test_api/lib/expect.dart new file mode 100644 index 000000000..5e62622fe --- /dev/null +++ b/pkgs/test_api/lib/expect.dart @@ -0,0 +1,5 @@ +// Copyright (c) 2021, 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. + +export 'package:matcher/expect.dart'; diff --git a/pkgs/test_api/lib/test_api.dart b/pkgs/test_api/lib/test_api.dart index 7c732f16d..881226e8d 100644 --- a/pkgs/test_api/lib/test_api.dart +++ b/pkgs/test_api/lib/test_api.dart @@ -6,6 +6,8 @@ 'Please use package:test.') library test_api; +export 'package:matcher/expect.dart'; + export 'hooks.dart' show TestFailure; export 'scaffolding.dart'; // Not yet deprecated, but not exposed through focused libraries. diff --git a/pkgs/test_api/pubspec.yaml b/pkgs/test_api/pubspec.yaml index 2fe918fa7..541ad8673 100644 --- a/pkgs/test_api/pubspec.yaml +++ b/pkgs/test_api/pubspec.yaml @@ -18,6 +18,10 @@ dependencies: string_scanner: ^1.1.0 term_glyph: ^1.2.0 + # Use a tight version constraint to ensure that a constraint on matcher + # properly constrains all features it provides. + matcher: '>=0.12.15 <0.12.16' + dev_dependencies: analyzer: '>=2.1.0 <6.0.0' fake_async: ^1.2.0 From f88f40da2a93eef609e77a4f12681abb22c424de Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 16 Mar 2023 19:47:54 +0000 Subject: [PATCH 6/9] Add matcher to the frames folded by default The implementation of `expect` used to be hidden, do the same with `matcher`. --- pkgs/test_api/lib/src/backend/stack_trace_formatter.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/test_api/lib/src/backend/stack_trace_formatter.dart b/pkgs/test_api/lib/src/backend/stack_trace_formatter.dart index d20014309..35d56f1dc 100644 --- a/pkgs/test_api/lib/src/backend/stack_trace_formatter.dart +++ b/pkgs/test_api/lib/src/backend/stack_trace_formatter.dart @@ -23,7 +23,7 @@ class StackTraceFormatter { StackTraceMapper? _mapper; /// The set of packages to fold when producing terse [Chain]s. - var _except = {'test', 'stream_channel', 'test_api'}; + var _except = {'matcher', 'stream_channel', 'test', 'test_api'}; /// If non-empty, all packages not in this list will be folded when producing /// terse [Chain]s. From c6fab49bf8b81cf5795131d0c33d53e5a225dc17 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 16 Mar 2023 20:02:55 +0000 Subject: [PATCH 7/9] Restore an in use src/ import --- pkgs/test_api/lib/src/expect/async_matcher.dart | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 pkgs/test_api/lib/src/expect/async_matcher.dart diff --git a/pkgs/test_api/lib/src/expect/async_matcher.dart b/pkgs/test_api/lib/src/expect/async_matcher.dart new file mode 100644 index 000000000..a2958edf8 --- /dev/null +++ b/pkgs/test_api/lib/src/expect/async_matcher.dart @@ -0,0 +1,6 @@ +// Copyright (c) 2017, 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. + +@Deprecated('Import from package:matcher if use cannot be avoided') +export 'package:matcher/src/expect/async_matcher.dart'; From c711c5756adfbfb3e9bf54c6edc9536697993c2f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 16 Mar 2023 20:12:13 +0000 Subject: [PATCH 8/9] Restore some APIs from the `package:test/test.dart` library --- pkgs/test/lib/test.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkgs/test/lib/test.dart b/pkgs/test/lib/test.dart index 97a62b0ce..d976c56b9 100644 --- a/pkgs/test/lib/test.dart +++ b/pkgs/test/lib/test.dart @@ -3,5 +3,13 @@ // BSD-style license that can be found in the LICENSE file. export 'package:matcher/expect.dart'; +// Deprecated exports not surfaced through focused libraries. +// ignore: deprecated_member_use +export 'package:matcher/src/expect/expect.dart' show ErrorFormatter; +// ignore: deprecated_member_use +export 'package:matcher/src/expect/expect_async.dart' show expectAsync; +// ignore: deprecated_member_use +export 'package:matcher/src/expect/throws_matcher.dart' show throws, Throws; +// The non-deprecated API (through a deprecated import). // ignore: deprecated_member_use export 'package:test_core/test_core.dart'; From 86c5ccee5ecd071fa25a1cf0bcbb5e75c9a3dfeb Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 16 Mar 2023 20:29:32 +0000 Subject: [PATCH 9/9] Don't deprecate the export Some internal targets make this a hard failure :( --- pkgs/test_api/lib/src/expect/async_matcher.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/pkgs/test_api/lib/src/expect/async_matcher.dart b/pkgs/test_api/lib/src/expect/async_matcher.dart index a2958edf8..044c1924c 100644 --- a/pkgs/test_api/lib/src/expect/async_matcher.dart +++ b/pkgs/test_api/lib/src/expect/async_matcher.dart @@ -2,5 +2,4 @@ // 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. -@Deprecated('Import from package:matcher if use cannot be avoided') export 'package:matcher/src/expect/async_matcher.dart';