Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(491)

Side by Side Diff: pkg/analysis_server/test/typed_mock.dart

Issue 281943002: Add a top-level typed_mock package. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address review comments. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/analysis_server/test/test_all.dart ('k') | pkg/analysis_server/test/typed_mock_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 library typed_mock;
2
3
4 _InvocationMatcher _lastMatcher;
5
6
7 /// Enables stubbing methods.
8 /// Use it when you want the mock to return particular value when particular
9 /// method is called.
10 Behavior when(_ignored) {
11 try {
12 var mock = _lastMatcher._mock;
13 mock._removeLastInvocation();
14 // set behavior
15 var behavior = new Behavior._(_lastMatcher);
16 _lastMatcher._behavior = behavior;
17 return behavior;
18 } finally {
19 // clear to prevent memory leak
20 _lastMatcher = null;
21 }
22 }
23
24 /// Verifies certain behavior happened a specified number of times.
25 Verifier verify(_ignored) {
26 try {
27 var mock = _lastMatcher._mock;
28 mock._removeLastInvocation();
29 // set verifier
30 return new Verifier._(mock, _lastMatcher);
31 } finally {
32 // clear to prevent memory leak
33 _lastMatcher = null;
34 }
35 }
36
37
38 /// Verifies that the given mock doesn't have any unverified interaction.
39 void verifyNoMoreInteractions(TypedMock mock) {
40 var notVerified = mock._computeNotVerifiedInvocations();
41 // OK
42 if (notVerified.isEmpty) {
43 return;
44 }
45 // fail
46 var invocationsString = _getInvocationsString(notVerified);
47 throw new VerifyError('Unexpected interactions:\n$invocationsString');
48 }
49
50
51 /// Verifies that no interactions happened on the given mock.
52 void verifyZeroInteractions(TypedMock mock) {
53 var invocations = mock._invocations;
54 // OK
55 if (invocations.isEmpty) {
56 return;
57 }
58 // fail
59 var invocationsString = _getInvocationsString(invocations);
60 throw new VerifyError('Unexpected interactions:\n$invocationsString');
61 }
62
63
64 /// [VerifyError] is thrown when one of the [verify] checks fails.
65 class VerifyError {
66 final String message;
67 VerifyError(this.message);
68 String toString() => 'VerifyError: $message';
69 }
70
71
72 String _getInvocationsString(Iterable<Invocation> invocations) {
73 var buffer = new StringBuffer();
74 invocations.forEach((invocation) {
75 var member = invocation.memberName;
76 buffer.write(member);
77 buffer.write(' ');
78 buffer.write(invocation.positionalArguments);
79 buffer.write(' ');
80 buffer.write(invocation.namedArguments);
81 buffer.writeln();
82 });
83 return buffer.toString();
84 }
85
86
87 class _InvocationMatcher {
88 final Symbol _member;
89 final TypedMock _mock;
90 final List<ArgumentMatcher> _matchers = [];
91
92 Behavior _behavior;
93
94 _InvocationMatcher(this._mock, this._member, Invocation invocation) {
95 invocation.positionalArguments.forEach((argument) {
96 ArgumentMatcher matcher;
97 if (argument is ArgumentMatcher) {
98 matcher = argument;
99 } else {
100 matcher = equals(argument);
101 }
102 _matchers.add(matcher);
103 });
104 }
105
106 bool match(Invocation invocation) {
107 var arguments = invocation.positionalArguments;
108 if (arguments.length != _matchers.length) {
109 return false;
110 }
111 for (int i = 0; i < _matchers.length; i++) {
112 var matcher = _matchers[i];
113 var argument = arguments[i];
114 if (!matcher.match(argument)) {
115 return false;
116 }
117 }
118 return true;
119 }
120 }
121
122
123 class Behavior {
124 final _InvocationMatcher _matcher;
125
126 Behavior._(this._matcher);
127
128 bool _thenFunctionEnabled = false;
129 Function _thenFunction;
130
131 bool _returnAlwaysEnabled = false;
132 var _returnAlways;
133
134 bool _returnListEnabled = false;
135 List _returnList;
136 int _returnListIndex;
137
138 bool _throwExceptionEnabled = false;
139 var _throwException;
140
141 Behavior thenInvoke(Function function) {
142 _reset();
143 _thenFunctionEnabled = true;
144 _thenFunction = function;
145 return this;
146 }
147
148 Behavior thenReturn(value) {
149 _reset();
150 _returnAlwaysEnabled = true;
151 _returnAlways = value;
152 return this;
153 }
154
155 Behavior thenReturnList(List list) {
156 _reset();
157 _returnListEnabled = true;
158 _returnList = list;
159 _returnListIndex = 0;
160 return this;
161 }
162
163 Behavior thenThrow(exception) {
164 _reset();
165 _throwExceptionEnabled = true;
166 _throwException = exception;
167 return this;
168 }
169
170 _reset() {
171 _thenFunctionEnabled = false;
172 _returnAlwaysEnabled = false;
173 _returnListEnabled = false;
174 _throwExceptionEnabled = false;
175 }
176
177 dynamic _getReturnValue(Invocation invocation) {
178 // function
179 if (_thenFunctionEnabled) {
180 return Function.apply(_thenFunction, invocation.positionalArguments,
181 invocation.namedArguments);
182 }
183 // always
184 if (_returnAlwaysEnabled) {
185 return _returnAlways;
186 }
187 // list
188 if (_returnListEnabled) {
189 if (_returnListIndex >= _returnList.length) {
190 throw new StateError('All ${_returnList.length} elements for '
191 '${_matcher._member} from $_returnList have been exhausted.');
192 }
193 return _returnList[_returnListIndex++];
194 }
195 // exception
196 if (_throwExceptionEnabled) {
197 throw _throwException;
198 }
199 // no value
200 return null;
201 }
202 }
203
204
205 class Verifier {
206 final TypedMock _mock;
207 final _InvocationMatcher _matcher;
208
209 Verifier._(this._mock, this._matcher);
210
211 /// Marks matching interactions as verified and never fails.
212 void any() {
213 // mark as verified, but don't check the actual count
214 _count();
215 }
216
217 /// Verifies that there was no matching interactions.
218 void never() {
219 times(0);
220 }
221
222 /// Verifies that there was excatly one martching interaction.
223 void once() {
224 times(1);
225 }
226
227 /// Verifies that there was the specified number of matching interactions.
228 void times(int expected) {
229 var times = _count();
230 if (times != expected) {
231 var member = _matcher._member;
232 throw new VerifyError('$expected expected, but $times'
233 ' invocations of $member recorded.');
234 }
235 }
236
237 /// Verifies that there was at least the specified number of matching
238 /// interactions.
239 void atLeast(int expected) {
240 var times = _count();
241 if (times < expected) {
242 var member = _matcher._member;
243 throw new VerifyError('At least $expected expected, but only $times'
244 ' invocations of $member recorded.');
245 }
246 }
247
248 /// Verifies that there was at least one matching interaction.
249 void atLeastOnce() {
250 var times = _count();
251 if (times == 0) {
252 var member = _matcher._member;
253 throw new VerifyError('At least one expected, but only zero'
254 ' invocations of $member recorded.');
255 }
256 }
257
258 /// Verifies that there was at most the specified number of matching
259 /// interactions.
260 void atMost(int expected) {
261 var times = _count();
262 if (times > expected) {
263 var member = _matcher._member;
264 throw new VerifyError('At most $expected expected, but $times'
265 ' invocations of $member recorded.');
266 }
267 }
268
269 int _count() {
270 var times = 0;
271 _mock._invocations.forEach((invocation) {
272 if (invocation.memberName != _matcher._member) {
273 return;
274 }
275 if (!_matcher.match(invocation)) {
276 return;
277 }
278 _mock._verifiedInvocations.add(invocation);
279 times++;
280 });
281 return times;
282 }
283 }
284
285
286 class TypedMock {
287 final Map<Symbol, List<_InvocationMatcher>> _matchersMap = {};
288
289 final List<Invocation> _invocations = [];
290 final Set<Invocation> _verifiedInvocations = new Set<Invocation>();
291
292 noSuchMethod(Invocation invocation) {
293 _invocations.add(invocation);
294 var member = invocation.memberName;
295 // prepare invocation matchers
296 var matchers = _matchersMap[member];
297 if (matchers == null) {
298 matchers = [];
299 _matchersMap[member] = matchers;
300 }
301 // check if there is a matcher
302 for (var matcher in matchers) {
303 if (matcher.match(invocation)) {
304 _lastMatcher = matcher;
305 // generate value if there is a behavior
306 if (matcher._behavior != null) {
307 return matcher._behavior._getReturnValue(invocation);
308 }
309 // probably verification
310 return null;
311 }
312 }
313 // add a new matcher
314 var matcher = new _InvocationMatcher(this, member, invocation);
315 matchers.add(matcher);
316 _lastMatcher = matcher;
317 }
318
319 Iterable<Invocation> _computeNotVerifiedInvocations() {
320 notVerified(e) => !_verifiedInvocations.contains(e);
321 return _invocations.where(notVerified);
322 }
323
324 void _removeLastInvocation() {
325 _invocations.removeLast();
326 }
327 }
328
329
330 abstract class ArgumentMatcher {
331 bool match(val);
332 }
333
334
335 class _ArgumentMatcher_equals extends ArgumentMatcher {
336 final expected;
337
338 _ArgumentMatcher_equals(this.expected);
339
340 @override
341 bool match(val) {
342 return val == expected;
343 }
344 }
345
346 equals(expected) {
347 return new _ArgumentMatcher_equals(expected);
348 }
349
350
351 class _ArgumentMatcher_anyBool extends ArgumentMatcher {
352 @override
353 bool match(val) {
354 return val is bool;
355 }
356 }
357
358 final anyBool = new _ArgumentMatcher_anyBool();
359
360
361 class _ArgumentMatcher_anyInt extends ArgumentMatcher {
362 @override
363 bool match(val) {
364 return val is int;
365 }
366 }
367
368 final anyInt = new _ArgumentMatcher_anyInt();
369
370
371 class _ArgumentMatcher_anyObject extends ArgumentMatcher {
372 @override
373 bool match(val) {
374 return true;
375 }
376 }
377
378 final anyObject = new _ArgumentMatcher_anyObject();
379
380
381 class _ArgumentMatcher_anyString extends ArgumentMatcher {
382 @override
383 bool match(val) {
384 return val is String;
385 }
386 }
387
388 final anyString = new _ArgumentMatcher_anyString();
OLDNEW
« no previous file with comments | « pkg/analysis_server/test/test_all.dart ('k') | pkg/analysis_server/test/typed_mock_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698