Line data Source code
1 : // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 : // for details. All rights reserved. Use of this source code is governed by a
3 : // BSD-style license that can be found in the LICENSE file.
4 :
5 : import 'dart:async';
6 :
7 : import 'package:matcher/matcher.dart';
8 :
9 : import '../backend/closed_exception.dart';
10 : import '../backend/invoker.dart';
11 : import '../utils.dart';
12 : import 'async_matcher.dart';
13 :
14 : /// A future that emits `null`.
15 : ///
16 : /// We cache and re-use this value to avoid adding a new microtask hit for each
17 : /// call to `expect()`.
18 : final _emptyFuture = new Future.value();
19 :
20 : /// An exception thrown when a test assertion fails.
21 : class TestFailure {
22 : final String message;
23 :
24 0 : TestFailure(this.message);
25 :
26 0 : String toString() => message;
27 : }
28 :
29 : /// The type used for functions that can be used to build up error reports
30 : /// upon failures in [expect].
31 : @Deprecated("Will be removed in 0.13.0.")
32 : typedef String ErrorFormatter(
33 : actual, Matcher matcher, String reason, Map matchState, bool verbose);
34 :
35 : /// Assert that [actual] matches [matcher].
36 : ///
37 : /// This is the main assertion function. [reason] is optional and is typically
38 : /// not supplied, as a reason is generated from [matcher]; if [reason]
39 : /// is included it is appended to the reason generated by the matcher.
40 : ///
41 : /// [matcher] can be a value in which case it will be wrapped in an
42 : /// [equals] matcher.
43 : ///
44 : /// If the assertion fails a [TestFailure] is thrown.
45 : ///
46 : /// If [skip] is a String or `true`, the assertion is skipped. The arguments are
47 : /// still evaluated, but [actual] is not verified to match [matcher]. If
48 : /// [actual] is a [Future], the test won't complete until the future emits a
49 : /// value.
50 : ///
51 : /// If [skip] is a string, it should explain why the assertion is skipped; this
52 : /// reason will be printed when running the test.
53 : ///
54 : /// In some cases extra diagnostic info can be produced on failure (for
55 : /// example, stack traces on mismatched exceptions). To enable these,
56 : /// [verbose] should be specified as `true`.
57 : ///
58 : /// Certain matchers, like [completion] and [throwsA], either match or fail
59 : /// asynchronously. When you use [expect] with these matchers, it ensures that
60 : /// the test doesn't complete until the matcher has either matched or failed. If
61 : /// you want to wait for the matcher to complete before continuing the test, you
62 : /// can call [expectLater] instead and `await` the result.
63 : void expect(actual, matcher,
64 : {String reason,
65 : skip,
66 : @Deprecated("Will be removed in 0.13.0.") bool verbose: false,
67 : @Deprecated("Will be removed in 0.13.0.") ErrorFormatter formatter}) {
68 5 : _expect(actual, matcher,
69 : reason: reason, skip: skip, verbose: verbose, formatter: formatter);
70 : }
71 :
72 : /// Just like [expect], but returns a [Future] that completes when the matcher
73 : /// has finished matching.
74 : ///
75 : /// For the [completes] and [completion] matchers, as well as [throwsA] and
76 : /// related matchers when they're matched against a [Future], the returned
77 : /// future completes when the matched future completes. For the [prints]
78 : /// matcher, it completes when the future returned by the callback completes.
79 : /// Otherwise, it completes immediately.
80 : ///
81 : /// If the matcher fails asynchronously, that failure is piped to the returned
82 : /// future where it can be handled by user code.
83 : Future expectLater(actual, matcher, {String reason, skip}) =>
84 0 : _expect(actual, matcher, reason: reason, skip: skip);
85 :
86 : /// The implementation of [expect] and [expectLater].
87 : Future _expect(actual, matcher,
88 : {String reason, skip, bool verbose: false, ErrorFormatter formatter}) {
89 : formatter ??= (actual, matcher, reason, matchState, verbose) {
90 0 : var mismatchDescription = new StringDescription();
91 0 : matcher.describeMismatch(actual, mismatchDescription, matchState, verbose);
92 :
93 0 : return formatFailure(matcher, actual, mismatchDescription.toString(),
94 : reason: reason);
95 : };
96 :
97 5 : if (Invoker.current == null) {
98 0 : throw new StateError("expect() may only be called within a test.");
99 : }
100 :
101 10 : if (Invoker.current.closed) throw new ClosedException();
102 :
103 0 : if (skip != null && skip is! bool && skip is! String) {
104 0 : throw new ArgumentError.value(skip, "skip", "must be a bool or a String");
105 : }
106 :
107 5 : matcher = wrapMatcher(matcher);
108 0 : if (skip != null && skip != false) {
109 : String message;
110 0 : if (skip is String) {
111 0 : message = "Skip expect: $skip";
112 : } else if (reason != null) {
113 0 : message = "Skip expect ($reason).";
114 : } else {
115 0 : var description = new StringDescription().addDescriptionOf(matcher);
116 0 : message = "Skip expect ($description).";
117 : }
118 :
119 0 : Invoker.current.skip(message);
120 0 : return _emptyFuture;
121 : }
122 :
123 5 : if (matcher is AsyncMatcher) {
124 : // Avoid async/await so that expect() throws synchronously when possible.
125 1 : var result = matcher.matchAsync(actual);
126 1 : expect(
127 : result,
128 2 : anyOf([
129 1 : equals(null),
130 1 : new isInstanceOf<Future>(),
131 1 : new isInstanceOf<String>()
132 : ]),
133 : reason: "matchAsync() may only return a String, a Future, or null.");
134 :
135 1 : if (result is String) {
136 0 : fail(formatFailure(matcher, actual, result, reason: reason));
137 1 : } else if (result is Future) {
138 0 : Invoker.current.addOutstandingCallback();
139 0 : return result.then((realResult) {
140 : if (realResult == null) return;
141 0 : fail(formatFailure(matcher, actual, realResult, reason: reason));
142 0 : }).whenComplete(() {
143 : // Always remove this, in case the failure is caught and handled
144 : // gracefully.
145 0 : Invoker.current.removeOutstandingCallback();
146 : });
147 : }
148 :
149 1 : return _emptyFuture;
150 : }
151 :
152 5 : var matchState = {};
153 : try {
154 10 : if (matcher.matches(actual, matchState)) return _emptyFuture;
155 : } catch (e, trace) {
156 0 : reason ??= '$e at $trace';
157 : }
158 0 : fail(formatter(actual, matcher, reason, matchState, verbose));
159 0 : return _emptyFuture;
160 : }
161 :
162 : /// Convenience method for throwing a new [TestFailure] with the provided
163 : /// [message].
164 0 : void fail(String message) => throw new TestFailure(message);
165 :
166 : // The default error formatter.
167 : @Deprecated("Will be removed in 0.13.0.")
168 : String formatFailure(Matcher expected, actual, String which, {String reason}) {
169 0 : var buffer = new StringBuffer();
170 0 : buffer.writeln(indent(prettyPrint(expected), first: 'Expected: '));
171 0 : buffer.writeln(indent(prettyPrint(actual), first: ' Actual: '));
172 0 : if (which.isNotEmpty) buffer.writeln(indent(which, first: ' Which: '));
173 0 : if (reason != null) buffer.writeln(reason);
174 0 : return buffer.toString();
175 : }
|