Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into patch-1
Browse files Browse the repository at this point in the history
natebosch committed Nov 21, 2023
2 parents f277c91 + fcbd361 commit 7823bb2
Showing 13 changed files with 66 additions and 47 deletions.
2 changes: 2 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -8,3 +8,5 @@ updates:
directory: /
schedule:
interval: monthly
labels:
- autosubmit
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -22,8 +22,8 @@ jobs:
matrix:
sdk: [dev]
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d
with:
sdk: ${{ matrix.sdk }}
- id: install
@@ -49,8 +49,8 @@ jobs:
os: [ubuntu-latest]
sdk: [2.18.0, dev]
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d
with:
sdk: ${{ matrix.sdk }}
- id: install
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
## 0.12.15-dev
## 0.12.17-wip

## 0.12.16

* Expand bounds on `test_api` dependency to allow the next breaking release
which will remove the cyclic dependency on this package.

## 0.12.15

* Add `package:matcher/expect.dart` library. Copies the implementation of
`expect` and the asynchronous matchers from `package:test`.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -253,3 +253,14 @@ why the test failed. For instance compare the failures between
Which: has length of <2>
```

### Prefer TypeMatcher to predicate if the match can fail in multiple ways

The `predicate` utility is a convenient shortcut for testing an arbitrary
(synchronous) property of a value, but it discards context and failures are
opaque. Different failure modes cannot be distinguished in the output which is
determined by a single "description" argument. Using `isA<SomeType>()` and the
`TypeMatcher.having` API to extract and test derived properties in a structured
way brings the context of that structure through to failure messages, so
failures for different reasons will have distinguishable and actionable failure
messages.
4 changes: 2 additions & 2 deletions lib/expect.dart
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

export 'matcher.dart';

export 'src/expect/expect.dart' show expect, expectLater, fail;
export 'src/expect/expect.dart' show ErrorFormatter, expect, expectLater, fail;
export 'src/expect/expect_async.dart'
show
Func0,
@@ -48,7 +48,7 @@ export 'src/expect/stream_matchers.dart'
emitsThrough,
mayEmitMultiple,
neverEmits;
export 'src/expect/throws_matcher.dart' show throwsA;
export 'src/expect/throws_matcher.dart' show Throws, throws, throwsA;
export 'src/expect/throws_matchers.dart'
show
throwsArgumentError,
21 changes: 15 additions & 6 deletions lib/src/core_matchers.dart
Original file line number Diff line number Diff line change
@@ -296,20 +296,29 @@ class _In<T> extends FeatureMatcher<T> {
description.add('is in ').addDescriptionOf(_source);
}

/// Returns a matcher that uses an arbitrary function that returns
/// true or false for the actual value.
/// Returns a matcher that uses an arbitrary function that returns whether the
/// value is considered a match.
///
/// For example:
///
/// expect(v, predicate((x) => ((x % 2) == 0), "is even"))
/// expect(actual, predicate<num>((v) => (v % 2) == 0, 'is even'));
///
/// Use this method when a value is checked for one conceptual property
/// described by [description].
///
/// If the value can be rejected for more than one reason prefer using [isA] and
/// the [TypeMatcher.having] API to build up a matcher with output that can
/// distinquish between them.
///
/// Using an explicit generict argument allows a passed function literal to have
/// an inferred argument type of [T], and values of the wrong type will be
/// rejected with an informative message.
Matcher predicate<T>(bool Function(T) f,
[String description = 'satisfies function']) =>
_Predicate(f, description);

typedef _PredicateFunction<T> = bool Function(T value);

class _Predicate<T> extends FeatureMatcher<T> {
final _PredicateFunction<T> _matcher;
final bool Function(T) _matcher;
final String _description;

_Predicate(this._matcher, this._description);
6 changes: 3 additions & 3 deletions lib/src/expect/expect.dart
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ typedef ErrorFormatter = String Function(Object? actual, Matcher matcher,
/// 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(Object? actual, Object? matcher,
void expect(dynamic actual, dynamic matcher,
{String? reason,
Object? /* String|bool */ skip,
@Deprecated('Will be removed in 0.13.0.') bool verbose = false,
@@ -68,12 +68,12 @@ void expect(Object? actual, Object? matcher,
///
/// If the matcher fails asynchronously, that failure is piped to the returned
/// future where it can be handled by user code.
Future expectLater(Object? actual, Object? matcher,
Future expectLater(dynamic actual, dynamic matcher,
{String? reason, Object? /* String|bool */ skip}) =>
_expect(actual, matcher, reason: reason, skip: skip);

/// The implementation of [expect] and [expectLater].
Future _expect(actual, matcher,
Future _expect(Object? actual, Object? matcher,
{String? reason, skip, bool verbose = false, ErrorFormatter? formatter}) {
final test = TestHandle.current;
formatter ??= (actual, matcher, reason, matchState, verbose) {
2 changes: 1 addition & 1 deletion lib/src/expect/never_called.dart
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ import 'util/pretty_print.dart';
/// This can safely be passed in place of any callback that takes ten or fewer
/// positional parameters. For example:
///
/// ```
/// ```dart
/// // Asserts that the stream never emits an event.
/// stream.listen(neverCalled);
/// ```
2 changes: 1 addition & 1 deletion lib/src/type_matcher.dart
Original file line number Diff line number Diff line change
@@ -62,7 +62,7 @@ class TypeMatcher<T> extends Matcher {
const TypeMatcher(
[@Deprecated('Provide a type argument to TypeMatcher and omit the name. '
'This argument will be removed in the next release.')
String? name])
String? name])
: _name =
// ignore: deprecated_member_use_from_same_package
name;
18 changes: 4 additions & 14 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: matcher
version: 0.12.14
version: 0.12.17-wip
description: >-
Support for specifying test expectations via an extensible Matcher class.
Also includes a number of built-in Matcher implementations for common cases.
@@ -13,23 +13,13 @@ dependencies:
meta: ^1.8.0
stack_trace: ^1.10.0
term_glyph: ^1.2.0
test_api: ^0.5.0
test_api: ">=0.5.0 <0.7.0"

dev_dependencies:
fake_async: ^1.3.0
lints: ^2.0.0
test: ^1.23.0

dependency_overrides:
test_api:
git:
url: https://github.com/dart-lang/test
path: pkgs/test_api
test_core:
git:
url: https://github.com/dart-lang/test
path: pkgs/test_core
test:
git:
url: https://github.com/dart-lang/test
path: pkgs/test
test: 1.24.2
test_api: 0.5.2
10 changes: 5 additions & 5 deletions test/expect_async_test.dart
Original file line number Diff line number Diff line change
@@ -336,7 +336,7 @@ void main() {
test('works with no arguments', () async {
var callbackRun = false;
var monitor = await TestCaseMonitor.run(() {
// ignore: deprecated_member_use, avoid_dynamic_calls
// ignore: deprecated_member_use_from_same_package, avoid_dynamic_calls
expectAsync(() {
callbackRun = true;
})();
@@ -349,7 +349,7 @@ void main() {
test('works with dynamic arguments', () async {
var callbackRun = false;
var monitor = await TestCaseMonitor.run(() {
// ignore: deprecated_member_use, avoid_dynamic_calls
// ignore: deprecated_member_use_from_same_package, avoid_dynamic_calls
expectAsync((arg1, arg2) {
callbackRun = true;
})(1, 2);
@@ -362,7 +362,7 @@ void main() {
test('works with non-nullable arguments', () async {
var callbackRun = false;
var monitor = await TestCaseMonitor.run(() {
// ignore: deprecated_member_use, avoid_dynamic_calls
// ignore: deprecated_member_use_from_same_package, avoid_dynamic_calls
expectAsync((int arg1, int arg2) {
callbackRun = true;
})(1, 2);
@@ -375,7 +375,7 @@ void main() {
test('works with 6 arguments', () async {
var callbackRun = false;
var monitor = await TestCaseMonitor.run(() {
// ignore: deprecated_member_use, avoid_dynamic_calls
// ignore: deprecated_member_use_from_same_package, avoid_dynamic_calls
expectAsync((arg1, arg2, arg3, arg4, arg5, arg6) {
callbackRun = true;
})(1, 2, 3, 4, 5, 6);
@@ -386,7 +386,7 @@ void main() {
});

test("doesn't support a function with 7 arguments", () {
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
expect(() => expectAsync((a, b, c, d, e, f, g) {}), throwsArgumentError);
});
});
16 changes: 8 additions & 8 deletions test/matcher/throws_test.dart
Original file line number Diff line number Diff line change
@@ -15,14 +15,14 @@ void main() {
group('synchronous', () {
group('[throws]', () {
test('with a function that throws an error', () {
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
expect(() => throw 'oh no', throws);
});

test("with a function that doesn't throw", () async {
void local() {}
var monitor = await TestCaseMonitor.run(() {
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
expect(local, throws);
});

@@ -38,7 +38,7 @@ void main() {

test('with a non-function', () async {
var monitor = await TestCaseMonitor.run(() {
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
expect(10, throws);
});

@@ -114,13 +114,13 @@ void main() {
group('asynchronous', () {
group('[throws]', () {
test('with a Future that throws an error', () {
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
expect(Future.error('oh no'), throws);
});

test("with a Future that doesn't throw", () async {
var monitor = await TestCaseMonitor.run(() {
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
expect(Future.value(), throws);
});

@@ -135,13 +135,13 @@ void main() {
});

test('with a closure that returns a Future that throws an error', () {
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
expect(() => Future.error('oh no'), throws);
});

test("with a closure that returns a Future that doesn't throw", () async {
var monitor = await TestCaseMonitor.run(() {
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
expect(Future.value, throws);
});

@@ -159,7 +159,7 @@ void main() {
late void Function() callback;
final monitor = TestCaseMonitor.start(() {
final completer = Completer<void>();
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
expect(completer.future, throws);
callback = () => completer.completeError('oh no');
});
4 changes: 2 additions & 2 deletions test/matcher/throws_type_test.dart
Original file line number Diff line number Diff line change
@@ -47,13 +47,13 @@ void main() {
test('passes when a CyclicInitializationError is thrown', () {
expect(
() => _CyclicInitializationFailure().x,
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
throwsCyclicInitializationError);
});

test('fails when a non-CyclicInitializationError is thrown', () async {
var liveTest = await TestCaseMonitor.run(() {
// ignore: deprecated_member_use
// ignore: deprecated_member_use_from_same_package
expect(() => throw Exception(), throwsCyclicInitializationError);
});

0 comments on commit 7823bb2

Please sign in to comment.