OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library unittest.expected_function; | 5 library unittest.expect_async; |
6 | 6 |
7 import '../unittest.dart'; | 7 import 'expect.dart'; |
8 | 8 import 'invoker.dart'; |
9 import 'internal_test_case.dart'; | 9 import 'state.dart'; |
10 | 10 |
11 /// An object used to detect unpassed arguments. | 11 /// An object used to detect unpassed arguments. |
12 const _PLACEHOLDER = const Object(); | 12 const _PLACEHOLDER = const Object(); |
13 | 13 |
14 // Functions used to check how many arguments a callback takes. | 14 // Functions used to check how many arguments a callback takes. |
15 typedef _Func0(); | 15 typedef _Func0(); |
16 typedef _Func1(a); | 16 typedef _Func1(a); |
17 typedef _Func2(a, b); | 17 typedef _Func2(a, b); |
18 typedef _Func3(a, b, c); | 18 typedef _Func3(a, b, c); |
19 typedef _Func4(a, b, c, d); | 19 typedef _Func4(a, b, c, d); |
20 typedef _Func5(a, b, c, d, e); | 20 typedef _Func5(a, b, c, d, e); |
21 typedef _Func6(a, b, c, d, e, f); | 21 typedef _Func6(a, b, c, d, e, f); |
22 | 22 |
23 typedef bool _IsDoneCallback(); | 23 typedef bool _IsDoneCallback(); |
24 | 24 |
25 /// A wrapper for a function that ensures that it's called the appropriate | 25 /// A wrapper for a function that ensures that it's called the appropriate |
26 /// number of times. | 26 /// number of times. |
27 /// | 27 /// |
28 /// The containing test won't be considered to have completed successfully until | 28 /// The containing test won't be considered to have completed successfully until |
29 /// this function has been called the appropriate number of times. | 29 /// this function has been called the appropriate number of times. |
30 /// | 30 /// |
31 /// The wrapper function is accessible via [func]. It supports up to six | 31 /// The wrapper function is accessible via [func]. It supports up to six |
32 /// optional and/or required positional arguments, but no named arguments. | 32 /// optional and/or required positional arguments, but no named arguments. |
33 class ExpectedFunction { | 33 class _ExpectedFunction { |
34 /// The wrapped callback. | 34 /// The wrapped callback. |
35 final Function _callback; | 35 final Function _callback; |
36 | 36 |
37 /// The minimum number of calls that are expected to be made to the function. | 37 /// The minimum number of calls that are expected to be made to the function. |
38 /// | 38 /// |
39 /// If fewer calls than this are made, the test will fail. | 39 /// If fewer calls than this are made, the test will fail. |
40 final int _minExpectedCalls; | 40 final int _minExpectedCalls; |
41 | 41 |
42 /// The maximum number of calls that are expected to be made to the function. | 42 /// The maximum number of calls that are expected to be made to the function. |
43 /// | 43 /// |
(...skipping 14 matching lines...) Expand all Loading... |
58 final String _id; | 58 final String _id; |
59 | 59 |
60 /// An optional description of why the function is expected to be called. | 60 /// An optional description of why the function is expected to be called. |
61 /// | 61 /// |
62 /// If not passed, this will be an empty string. | 62 /// If not passed, this will be an empty string. |
63 final String _reason; | 63 final String _reason; |
64 | 64 |
65 /// The number of times the function has been called. | 65 /// The number of times the function has been called. |
66 int _actualCalls = 0; | 66 int _actualCalls = 0; |
67 | 67 |
68 /// The test case in which this function was wrapped. | 68 /// The test invoker in which this function was wrapped. |
69 final InternalTestCase _testCase; | 69 final Invoker _invoker; |
70 | 70 |
71 /// Whether this function has been called the requisite number of times. | 71 /// Whether this function has been called the requisite number of times. |
72 bool _complete; | 72 bool _complete; |
73 | 73 |
74 /// Wraps [callback] in a function that asserts that it's called at least | 74 /// Wraps [callback] in a function that asserts that it's called at least |
75 /// [minExpected] times and no more than [maxExpected] times. | 75 /// [minExpected] times and no more than [maxExpected] times. |
76 /// | 76 /// |
77 /// If passed, [id] is used as a descriptive name fo the function and [reason] | 77 /// If passed, [id] is used as a descriptive name fo the function and [reason] |
78 /// as a reason it's expected to be called. If [isDone] is passed, the test | 78 /// as a reason it's expected to be called. If [isDone] is passed, the test |
79 /// won't be allowed to complete until it returns `true`. | 79 /// won't be allowed to complete until it returns `true`. |
80 ExpectedFunction(Function callback, int minExpected, int maxExpected, | 80 _ExpectedFunction(Function callback, int minExpected, int maxExpected, |
81 {String id, String reason, bool isDone()}) | 81 {String id, String reason, bool isDone()}) |
82 : this._callback = callback, | 82 : this._callback = callback, |
83 _minExpectedCalls = minExpected, | 83 _minExpectedCalls = minExpected, |
84 _maxExpectedCalls = (maxExpected == 0 && minExpected > 0) | 84 _maxExpectedCalls = (maxExpected == 0 && minExpected > 0) |
85 ? minExpected | 85 ? minExpected |
86 : maxExpected, | 86 : maxExpected, |
87 this._isDone = isDone, | 87 this._isDone = isDone, |
88 this._reason = reason == null ? '' : '\n$reason', | 88 this._reason = reason == null ? '' : '\n$reason', |
89 this._testCase = currentTestCase as InternalTestCase, | 89 this._invoker = Invoker.current, |
90 this._id = _makeCallbackId(id, callback) { | 90 this._id = _makeCallbackId(id, callback) { |
91 ensureInitialized(); | 91 if (_invoker == null) { |
92 if (_testCase == null) { | 92 throw new StateError("[expectAsync] was called outside of a test."); |
93 throw new StateError("No valid test. Did you forget to run your test " | 93 } else if (maxExpected > 0 && minExpected > maxExpected) { |
94 "inside a call to test()?"); | 94 throw new ArgumentError("max ($maxExpected) may not be less than count " |
| 95 "($minExpected)."); |
95 } | 96 } |
96 | 97 |
97 if (isDone != null || minExpected > 0) { | 98 if (isDone != null || minExpected > 0) { |
98 _testCase.callbackFunctionsOutstanding++; | 99 _invoker.addOutstandingCallback(); |
99 _complete = false; | 100 _complete = false; |
100 } else { | 101 } else { |
101 _complete = true; | 102 _complete = true; |
102 } | 103 } |
103 } | 104 } |
104 | 105 |
105 /// Tries to find a reasonable name for [callback]. | 106 /// Tries to find a reasonable name for [callback]. |
106 /// | 107 /// |
107 /// If [id] is passed, uses that. Otherwise, tries to determine a name from | 108 /// If [id] is passed, uses that. Otherwise, tries to determine a name from |
108 /// calling `toString`. If no name can be found, returns the empty string. | 109 /// calling `toString`. If no name can be found, returns the empty string. |
(...skipping 17 matching lines...) Expand all Loading... |
126 /// wrapped function (up to a total of 6). | 127 /// wrapped function (up to a total of 6). |
127 Function get func { | 128 Function get func { |
128 if (_callback is _Func6) return _max6; | 129 if (_callback is _Func6) return _max6; |
129 if (_callback is _Func5) return _max5; | 130 if (_callback is _Func5) return _max5; |
130 if (_callback is _Func4) return _max4; | 131 if (_callback is _Func4) return _max4; |
131 if (_callback is _Func3) return _max3; | 132 if (_callback is _Func3) return _max3; |
132 if (_callback is _Func2) return _max2; | 133 if (_callback is _Func2) return _max2; |
133 if (_callback is _Func1) return _max1; | 134 if (_callback is _Func1) return _max1; |
134 if (_callback is _Func0) return _max0; | 135 if (_callback is _Func0) return _max0; |
135 | 136 |
| 137 _invoker.removeOutstandingCallback(); |
136 throw new ArgumentError( | 138 throw new ArgumentError( |
137 'The wrapped function has more than 6 required arguments'); | 139 'The wrapped function has more than 6 required arguments'); |
138 } | 140 } |
139 | 141 |
140 // This indirection is critical. It ensures the returned function has an | 142 // This indirection is critical. It ensures the returned function has an |
141 // argument count of zero. | 143 // argument count of zero. |
142 _max0() => _max6(); | 144 _max0() => _max6(); |
143 | 145 |
144 _max1([a0 = _PLACEHOLDER]) => _max6(a0); | 146 _max1([a0 = _PLACEHOLDER]) => _max6(a0); |
145 | 147 |
146 _max2([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER]) => _max6(a0, a1); | 148 _max2([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER]) => _max6(a0, a1); |
147 | 149 |
148 _max3([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER]) => | 150 _max3([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER]) => |
149 _max6(a0, a1, a2); | 151 _max6(a0, a1, a2); |
150 | 152 |
151 _max4([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER, | 153 _max4([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER, |
152 a3 = _PLACEHOLDER]) => _max6(a0, a1, a2, a3); | 154 a3 = _PLACEHOLDER]) => _max6(a0, a1, a2, a3); |
153 | 155 |
154 _max5([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER, | 156 _max5([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER, |
155 a3 = _PLACEHOLDER, a4 = _PLACEHOLDER]) => _max6(a0, a1, a2, a3, a4); | 157 a3 = _PLACEHOLDER, a4 = _PLACEHOLDER]) => _max6(a0, a1, a2, a3, a4); |
156 | 158 |
157 _max6([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER, | 159 _max6([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER, |
158 a3 = _PLACEHOLDER, a4 = _PLACEHOLDER, a5 = _PLACEHOLDER]) => | 160 a3 = _PLACEHOLDER, a4 = _PLACEHOLDER, a5 = _PLACEHOLDER]) => |
159 _run([a0, a1, a2, a3, a4, a5].where((a) => a != _PLACEHOLDER)); | 161 _run([a0, a1, a2, a3, a4, a5].where((a) => a != _PLACEHOLDER)); |
160 | 162 |
161 /// Runs the wrapped function with [args] and returns its return value. | 163 /// Runs the wrapped function with [args] and returns its return value. |
162 /// | |
163 /// This will pass any errors on to [_testCase] and return `null`. | |
164 _run(Iterable args) { | 164 _run(Iterable args) { |
| 165 // Note that in the old unittest, this returned `null` if it encountered an |
| 166 // error, where now it just re-throws that error because Zone machinery will |
| 167 // pass it to the invoker anyway. |
165 try { | 168 try { |
166 _actualCalls++; | 169 _actualCalls++; |
167 if (_testCase.isComplete) { | 170 if (_invoker.liveTest.isComplete && |
168 // Don't run the callback if the test is done. We don't throw here as | 171 _invoker.liveTest.state.result == Result.success) { |
169 // this is not the current test, but we do mark the old test as having | 172 throw 'Callback ${_id}called ($_actualCalls) after test case ' |
170 // an error if it previously passed. | 173 '${_invoker.liveTest.test.name} had already completed.$_reason'; |
171 if (_testCase.result == PASS) { | |
172 _testCase.error( | |
173 'Callback ${_id}called ($_actualCalls) after test case ' | |
174 '${_testCase.description} had already been marked as ' | |
175 '${_testCase.result}.$_reason'); | |
176 } | |
177 return null; | |
178 } else if (_maxExpectedCalls >= 0 && _actualCalls > _maxExpectedCalls) { | 174 } else if (_maxExpectedCalls >= 0 && _actualCalls > _maxExpectedCalls) { |
179 throw new TestFailure('Callback ${_id}called more times than expected ' | 175 throw new TestFailure('Callback ${_id}called more times than expected ' |
180 '($_maxExpectedCalls).$_reason'); | 176 '($_maxExpectedCalls).$_reason'); |
181 } | 177 } |
182 | 178 |
183 return Function.apply(_callback, args.toList()); | 179 return Function.apply(_callback, args.toList()); |
184 } catch (error, stackTrace) { | 180 } catch (error, stackTrace) { |
185 _testCase.registerException(error, stackTrace); | 181 _invoker.handleError(error, stackTrace); |
186 return null; | 182 return null; |
187 } finally { | 183 } finally { |
188 _afterRun(); | 184 _afterRun(); |
189 } | 185 } |
190 } | 186 } |
191 | 187 |
192 /// After each time the function is run, check to see if it's complete. | 188 /// After each time the function is run, check to see if it's complete. |
193 void _afterRun() { | 189 void _afterRun() { |
194 if (_complete) return; | 190 if (_complete) return; |
195 if (_minExpectedCalls > 0 && _actualCalls < _minExpectedCalls) return; | 191 if (_minExpectedCalls > 0 && _actualCalls < _minExpectedCalls) return; |
196 if (_isDone != null && !_isDone()) return; | 192 if (_isDone != null && !_isDone()) return; |
197 | 193 |
198 // Mark this callback as complete and remove it from the test case's | 194 // Mark this callback as complete and remove it from the test case's |
199 // oustanding callback count; if that hits zero the test is done. | 195 // oustanding callback count; if that hits zero the test is done. |
200 _complete = true; | 196 _complete = true; |
201 _testCase.markCallbackComplete(); | 197 _invoker.removeOutstandingCallback(); |
202 } | 198 } |
203 } | 199 } |
| 200 |
| 201 /// Indicate that [callback] is expected to be called [count] number of times |
| 202 /// (by default 1). |
| 203 /// |
| 204 /// The unittest framework will wait for the callback to run the [count] times |
| 205 /// before it considers the current test to be complete. [callback] may take up |
| 206 /// to six optional or required positional arguments; named arguments are not |
| 207 /// supported. |
| 208 /// |
| 209 /// [max] can be used to specify an upper bound on the number of calls; if this |
| 210 /// is exceeded the test will fail. If [max] is `0` (the default), the callback |
| 211 /// is expected to be called exactly [count] times. If [max] is `-1`, the |
| 212 /// callback is allowed to be called any number of times greater than [count]. |
| 213 /// |
| 214 /// Both [id] and [reason] are optional and provide extra information about the |
| 215 /// callback when debugging. [id] should be the name of the callback, while |
| 216 /// [reason] should be the reason the callback is expected to be called. |
| 217 Function expectAsync(Function callback, |
| 218 {int count: 1, int max: 0, String id, String reason}) => |
| 219 new _ExpectedFunction(callback, count, max, id: id, reason: reason).func; |
| 220 |
| 221 /// Indicate that [callback] is expected to be called until [isDone] returns |
| 222 /// true. |
| 223 /// |
| 224 /// [isDone] is called after each time the function is run. Only when it returns |
| 225 /// true will the callback be considered complete. [callback] may take up to six |
| 226 /// optional or required positional arguments; named arguments are not |
| 227 /// supported. |
| 228 /// |
| 229 /// Both [id] and [reason] are optional and provide extra information about the |
| 230 /// callback when debugging. [id] should be the name of the callback, while |
| 231 /// [reason] should be the reason the callback is expected to be called. |
| 232 Function expectAsyncUntil(Function callback, bool isDone(), |
| 233 {String id, String reason}) => new _ExpectedFunction(callback, 0, -1, |
| 234 id: id, reason: reason, isDone: isDone).func; |
OLD | NEW |