|
1 """ |
|
2 Copyright (c) 2007 Gustavo Niemeyer <gustavo@niemeyer.net> |
|
3 |
|
4 Graceful platform for test doubles in Python (mocks, stubs, fakes, and dummies). |
|
5 """ |
|
6 import __builtin__ |
|
7 import tempfile |
|
8 import unittest |
|
9 import inspect |
|
10 import shutil |
|
11 import types |
|
12 import sys |
|
13 import os |
|
14 import gc |
|
15 |
|
16 |
|
17 if sys.version_info < (2, 4): |
|
18 from sets import Set as set # pragma: nocover |
|
19 |
|
20 |
|
21 __all__ = ["Mocker", "expect", "IS", "CONTAINS", "IN", "MATCH", |
|
22 "ANY", "ARGS", "KWARGS"] |
|
23 |
|
24 |
|
25 __author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" |
|
26 __license__ = "PSF License" |
|
27 __version__ = "0.10.1" |
|
28 |
|
29 |
|
30 ERROR_PREFIX = "[Mocker] " |
|
31 |
|
32 |
|
33 # -------------------------------------------------------------------- |
|
34 # Exceptions |
|
35 |
|
36 class MatchError(AssertionError): |
|
37 """Raised when an unknown expression is seen in playback mode.""" |
|
38 |
|
39 |
|
40 # -------------------------------------------------------------------- |
|
41 # Helper for chained-style calling. |
|
42 |
|
43 class expect(object): |
|
44 """This is a simple helper that allows a different call-style. |
|
45 |
|
46 With this class one can comfortably do chaining of calls to the |
|
47 mocker object responsible by the object being handled. For instance:: |
|
48 |
|
49 expect(obj.attr).result(3).count(1, 2) |
|
50 |
|
51 Is the same as:: |
|
52 |
|
53 obj.attr |
|
54 mocker.result(3) |
|
55 mocker.count(1, 2) |
|
56 |
|
57 """ |
|
58 |
|
59 def __init__(self, mock, attr=None): |
|
60 self._mock = mock |
|
61 self._attr = attr |
|
62 |
|
63 def __getattr__(self, attr): |
|
64 return self.__class__(self._mock, attr) |
|
65 |
|
66 def __call__(self, *args, **kwargs): |
|
67 getattr(self._mock.__mocker__, self._attr)(*args, **kwargs) |
|
68 return self |
|
69 |
|
70 |
|
71 # -------------------------------------------------------------------- |
|
72 # Extensions to Python's unittest. |
|
73 |
|
74 class MockerTestCase(unittest.TestCase): |
|
75 """unittest.TestCase subclass with Mocker support. |
|
76 |
|
77 @ivar mocker: The mocker instance. |
|
78 |
|
79 This is a convenience only. Mocker may easily be used with the |
|
80 standard C{unittest.TestCase} class if wanted. |
|
81 |
|
82 Test methods have a Mocker instance available on C{self.mocker}. |
|
83 At the end of each test method, expectations of the mocker will |
|
84 be verified, and any requested changes made to the environment |
|
85 will be restored. |
|
86 |
|
87 In addition to the integration with Mocker, this class provides |
|
88 a few additional helper methods. |
|
89 """ |
|
90 |
|
91 expect = expect |
|
92 |
|
93 def __init__(self, methodName="runTest"): |
|
94 # So here is the trick: we take the real test method, wrap it on |
|
95 # a function that do the job we have to do, and insert it in the |
|
96 # *instance* dictionary, so that getattr() will return our |
|
97 # replacement rather than the class method. |
|
98 test_method = getattr(self, methodName, None) |
|
99 if test_method is not None: |
|
100 def test_method_wrapper(): |
|
101 try: |
|
102 result = test_method() |
|
103 except: |
|
104 raise |
|
105 else: |
|
106 if (self.mocker.is_recording() and |
|
107 self.mocker.get_events()): |
|
108 raise RuntimeError("Mocker must be put in replay " |
|
109 "mode with self.mocker.replay()") |
|
110 if (hasattr(result, "addCallback") and |
|
111 hasattr(result, "addErrback")): |
|
112 def verify(result): |
|
113 self.mocker.verify() |
|
114 return result |
|
115 result.addCallback(verify) |
|
116 else: |
|
117 self.mocker.verify() |
|
118 return result |
|
119 # Copy all attributes from the original method.. |
|
120 for attr in dir(test_method): |
|
121 # .. unless they're present in our wrapper already. |
|
122 if not hasattr(test_method_wrapper, attr) or attr == "__doc__": |
|
123 setattr(test_method_wrapper, attr, |
|
124 getattr(test_method, attr)) |
|
125 setattr(self, methodName, test_method_wrapper) |
|
126 |
|
127 # We could overload run() normally, but other well-known testing |
|
128 # frameworks do it as well, and some of them won't call the super, |
|
129 # which might mean that cleanup wouldn't happen. With that in mind, |
|
130 # we make integration easier by using the following trick. |
|
131 run_method = self.run |
|
132 def run_wrapper(*args, **kwargs): |
|
133 try: |
|
134 return run_method(*args, **kwargs) |
|
135 finally: |
|
136 self.__cleanup() |
|
137 self.run = run_wrapper |
|
138 |
|
139 self.mocker = Mocker() |
|
140 |
|
141 self.__cleanup_funcs = [] |
|
142 self.__cleanup_paths = [] |
|
143 |
|
144 super(MockerTestCase, self).__init__(methodName) |
|
145 |
|
146 def __cleanup(self): |
|
147 for path in self.__cleanup_paths: |
|
148 if os.path.isfile(path): |
|
149 os.unlink(path) |
|
150 elif os.path.isdir(path): |
|
151 shutil.rmtree(path) |
|
152 self.mocker.restore() |
|
153 for func, args, kwargs in self.__cleanup_funcs: |
|
154 func(*args, **kwargs) |
|
155 |
|
156 def addCleanup(self, func, *args, **kwargs): |
|
157 self.__cleanup_funcs.append((func, args, kwargs)) |
|
158 |
|
159 def makeFile(self, content=None, suffix="", prefix="tmp", basename=None, |
|
160 dirname=None, path=None): |
|
161 """Create a temporary file and return the path to it. |
|
162 |
|
163 @param content: Initial content for the file. |
|
164 @param suffix: Suffix to be given to the file's basename. |
|
165 @param prefix: Prefix to be given to the file's basename. |
|
166 @param basename: Full basename for the file. |
|
167 @param dirname: Put file inside this directory. |
|
168 |
|
169 The file is removed after the test runs. |
|
170 """ |
|
171 if path is not None: |
|
172 self.__cleanup_paths.append(path) |
|
173 elif basename is not None: |
|
174 if dirname is None: |
|
175 dirname = tempfile.mkdtemp() |
|
176 self.__cleanup_paths.append(dirname) |
|
177 path = os.path.join(dirname, basename) |
|
178 else: |
|
179 fd, path = tempfile.mkstemp(suffix, prefix, dirname) |
|
180 self.__cleanup_paths.append(path) |
|
181 os.close(fd) |
|
182 if content is None: |
|
183 os.unlink(path) |
|
184 if content is not None: |
|
185 file = open(path, "w") |
|
186 file.write(content) |
|
187 file.close() |
|
188 return path |
|
189 |
|
190 def makeDir(self, suffix="", prefix="tmp", dirname=None, path=None): |
|
191 """Create a temporary directory and return the path to it. |
|
192 |
|
193 @param suffix: Suffix to be given to the file's basename. |
|
194 @param prefix: Prefix to be given to the file's basename. |
|
195 @param dirname: Put directory inside this parent directory. |
|
196 |
|
197 The directory is removed after the test runs. |
|
198 """ |
|
199 if path is not None: |
|
200 os.makedirs(path) |
|
201 else: |
|
202 path = tempfile.mkdtemp(suffix, prefix, dirname) |
|
203 self.__cleanup_paths.append(path) |
|
204 return path |
|
205 |
|
206 def failUnlessIs(self, first, second, msg=None): |
|
207 """Assert that C{first} is the same object as C{second}.""" |
|
208 if first is not second: |
|
209 raise self.failureException(msg or "%r is not %r" % (first, second)) |
|
210 |
|
211 def failIfIs(self, first, second, msg=None): |
|
212 """Assert that C{first} is not the same object as C{second}.""" |
|
213 if first is second: |
|
214 raise self.failureException(msg or "%r is %r" % (first, second)) |
|
215 |
|
216 def failUnlessIn(self, first, second, msg=None): |
|
217 """Assert that C{first} is contained in C{second}.""" |
|
218 if first not in second: |
|
219 raise self.failureException(msg or "%r not in %r" % (first, second)) |
|
220 |
|
221 def failUnlessStartsWith(self, first, second, msg=None): |
|
222 """Assert that C{first} starts with C{second}.""" |
|
223 if first[:len(second)] != second: |
|
224 raise self.failureException(msg or "%r doesn't start with %r" % |
|
225 (first, second)) |
|
226 |
|
227 def failIfStartsWith(self, first, second, msg=None): |
|
228 """Assert that C{first} doesn't start with C{second}.""" |
|
229 if first[:len(second)] == second: |
|
230 raise self.failureException(msg or "%r starts with %r" % |
|
231 (first, second)) |
|
232 |
|
233 def failUnlessEndsWith(self, first, second, msg=None): |
|
234 """Assert that C{first} starts with C{second}.""" |
|
235 if first[len(first)-len(second):] != second: |
|
236 raise self.failureException(msg or "%r doesn't end with %r" % |
|
237 (first, second)) |
|
238 |
|
239 def failIfEndsWith(self, first, second, msg=None): |
|
240 """Assert that C{first} doesn't start with C{second}.""" |
|
241 if first[len(first)-len(second):] == second: |
|
242 raise self.failureException(msg or "%r ends with %r" % |
|
243 (first, second)) |
|
244 |
|
245 def failIfIn(self, first, second, msg=None): |
|
246 """Assert that C{first} is not contained in C{second}.""" |
|
247 if first in second: |
|
248 raise self.failureException(msg or "%r in %r" % (first, second)) |
|
249 |
|
250 def failUnlessApproximates(self, first, second, tolerance, msg=None): |
|
251 """Assert that C{first} is near C{second} by at most C{tolerance}.""" |
|
252 if abs(first - second) > tolerance: |
|
253 raise self.failureException(msg or "abs(%r - %r) > %r" % |
|
254 (first, second, tolerance)) |
|
255 |
|
256 def failIfApproximates(self, first, second, tolerance, msg=None): |
|
257 """Assert that C{first} is far from C{second} by at least C{tolerance}. |
|
258 """ |
|
259 if abs(first - second) <= tolerance: |
|
260 raise self.failureException(msg or "abs(%r - %r) <= %r" % |
|
261 (first, second, tolerance)) |
|
262 |
|
263 def failUnlessMethodsMatch(self, first, second): |
|
264 """Assert that public methods in C{first} are present in C{second}. |
|
265 |
|
266 This method asserts that all public methods found in C{first} are also |
|
267 present in C{second} and accept the same arguments. C{first} may |
|
268 have its own private methods, though, and may not have all methods |
|
269 found in C{second}. Note that if a private method in C{first} matches |
|
270 the name of one in C{second}, their specification is still compared. |
|
271 |
|
272 This is useful to verify if a fake or stub class have the same API as |
|
273 the real class being simulated. |
|
274 """ |
|
275 first_methods = dict(inspect.getmembers(first, inspect.ismethod)) |
|
276 second_methods = dict(inspect.getmembers(second, inspect.ismethod)) |
|
277 for name, first_method in first_methods.items(): |
|
278 first_argspec = inspect.getargspec(first_method) |
|
279 first_formatted = inspect.formatargspec(*first_argspec) |
|
280 |
|
281 second_method = second_methods.get(name) |
|
282 if second_method is None: |
|
283 if name[:1] == "_": |
|
284 continue # First may have its own private methods. |
|
285 raise self.failureException("%s.%s%s not present in %s" % |
|
286 (first.__name__, name, first_formatted, second.__name__)) |
|
287 |
|
288 second_argspec = inspect.getargspec(second_method) |
|
289 if first_argspec != second_argspec: |
|
290 second_formatted = inspect.formatargspec(*second_argspec) |
|
291 raise self.failureException("%s.%s%s != %s.%s%s" % |
|
292 (first.__name__, name, first_formatted, |
|
293 second.__name__, name, second_formatted)) |
|
294 |
|
295 |
|
296 assertIs = failUnlessIs |
|
297 assertIsNot = failIfIs |
|
298 assertIn = failUnlessIn |
|
299 assertNotIn = failIfIn |
|
300 assertStartsWith = failUnlessStartsWith |
|
301 assertNotStartsWith = failIfStartsWith |
|
302 assertEndsWith = failUnlessEndsWith |
|
303 assertNotEndsWith = failIfEndsWith |
|
304 assertApproximates = failUnlessApproximates |
|
305 assertNotApproximates = failIfApproximates |
|
306 assertMethodsMatch = failUnlessMethodsMatch |
|
307 |
|
308 # The following are missing in Python < 2.4. |
|
309 assertTrue = unittest.TestCase.failUnless |
|
310 assertFalse = unittest.TestCase.failIf |
|
311 |
|
312 # The following is provided for compatibility with Twisted's trial. |
|
313 assertIdentical = assertIs |
|
314 assertNotIdentical = assertIsNot |
|
315 failUnlessIdentical = failUnlessIs |
|
316 failIfIdentical = failIfIs |
|
317 |
|
318 |
|
319 # -------------------------------------------------------------------- |
|
320 # Mocker. |
|
321 |
|
322 class classinstancemethod(object): |
|
323 |
|
324 def __init__(self, method): |
|
325 self.method = method |
|
326 |
|
327 def __get__(self, obj, cls=None): |
|
328 def bound_method(*args, **kwargs): |
|
329 return self.method(cls, obj, *args, **kwargs) |
|
330 return bound_method |
|
331 |
|
332 |
|
333 class MockerBase(object): |
|
334 """Controller of mock objects. |
|
335 |
|
336 A mocker instance is used to command recording and replay of |
|
337 expectations on any number of mock objects. |
|
338 |
|
339 Expectations should be expressed for the mock object while in |
|
340 record mode (the initial one) by using the mock object itself, |
|
341 and using the mocker (and/or C{expect()} as a helper) to define |
|
342 additional behavior for each event. For instance:: |
|
343 |
|
344 mock = mocker.mock() |
|
345 mock.hello() |
|
346 mocker.result("Hi!") |
|
347 mocker.replay() |
|
348 assert mock.hello() == "Hi!" |
|
349 mock.restore() |
|
350 mock.verify() |
|
351 |
|
352 In this short excerpt a mock object is being created, then an |
|
353 expectation of a call to the C{hello()} method was recorded, and |
|
354 when called the method should return the value C{10}. Then, the |
|
355 mocker is put in replay mode, and the expectation is satisfied by |
|
356 calling the C{hello()} method, which indeed returns 10. Finally, |
|
357 a call to the L{restore()} method is performed to undo any needed |
|
358 changes made in the environment, and the L{verify()} method is |
|
359 called to ensure that all defined expectations were met. |
|
360 |
|
361 The same logic can be expressed more elegantly using the |
|
362 C{with mocker:} statement, as follows:: |
|
363 |
|
364 mock = mocker.mock() |
|
365 mock.hello() |
|
366 mocker.result("Hi!") |
|
367 with mocker: |
|
368 assert mock.hello() == "Hi!" |
|
369 |
|
370 Also, the MockerTestCase class, which integrates the mocker on |
|
371 a unittest.TestCase subclass, may be used to reduce the overhead |
|
372 of controlling the mocker. A test could be written as follows:: |
|
373 |
|
374 class SampleTest(MockerTestCase): |
|
375 |
|
376 def test_hello(self): |
|
377 mock = self.mocker.mock() |
|
378 mock.hello() |
|
379 self.mocker.result("Hi!") |
|
380 self.mocker.replay() |
|
381 self.assertEquals(mock.hello(), "Hi!") |
|
382 """ |
|
383 |
|
384 _recorders = [] |
|
385 |
|
386 # For convenience only. |
|
387 on = expect |
|
388 |
|
389 class __metaclass__(type): |
|
390 def __init__(self, name, bases, dict): |
|
391 # Make independent lists on each subclass, inheriting from parent. |
|
392 self._recorders = list(getattr(self, "_recorders", ())) |
|
393 |
|
394 def __init__(self): |
|
395 self._recorders = self._recorders[:] |
|
396 self._events = [] |
|
397 self._recording = True |
|
398 self._ordering = False |
|
399 self._last_orderer = None |
|
400 |
|
401 def is_recording(self): |
|
402 """Return True if in recording mode, False if in replay mode. |
|
403 |
|
404 Recording is the initial state. |
|
405 """ |
|
406 return self._recording |
|
407 |
|
408 def replay(self): |
|
409 """Change to replay mode, where recorded events are reproduced. |
|
410 |
|
411 If already in replay mode, the mocker will be restored, with all |
|
412 expectations reset, and then put again in replay mode. |
|
413 |
|
414 An alternative and more comfortable way to replay changes is |
|
415 using the 'with' statement, as follows:: |
|
416 |
|
417 mocker = Mocker() |
|
418 <record events> |
|
419 with mocker: |
|
420 <reproduce events> |
|
421 |
|
422 The 'with' statement will automatically put mocker in replay |
|
423 mode, and will also verify if all events were correctly reproduced |
|
424 at the end (using L{verify()}), and also restore any changes done |
|
425 in the environment (with L{restore()}). |
|
426 |
|
427 Also check the MockerTestCase class, which integrates the |
|
428 unittest.TestCase class with mocker. |
|
429 """ |
|
430 if not self._recording: |
|
431 for event in self._events: |
|
432 event.restore() |
|
433 else: |
|
434 self._recording = False |
|
435 for event in self._events: |
|
436 event.replay() |
|
437 |
|
438 def restore(self): |
|
439 """Restore changes in the environment, and return to recording mode. |
|
440 |
|
441 This should always be called after the test is complete (succeeding |
|
442 or not). There are ways to call this method automatically on |
|
443 completion (e.g. using a C{with mocker:} statement, or using the |
|
444 L{MockerTestCase} class. |
|
445 """ |
|
446 if not self._recording: |
|
447 self._recording = True |
|
448 for event in self._events: |
|
449 event.restore() |
|
450 |
|
451 def reset(self): |
|
452 """Reset the mocker state. |
|
453 |
|
454 This will restore environment changes, if currently in replay |
|
455 mode, and then remove all events previously recorded. |
|
456 """ |
|
457 if not self._recording: |
|
458 self.restore() |
|
459 self.unorder() |
|
460 del self._events[:] |
|
461 |
|
462 def get_events(self): |
|
463 """Return all recorded events.""" |
|
464 return self._events[:] |
|
465 |
|
466 def add_event(self, event): |
|
467 """Add an event. |
|
468 |
|
469 This method is used internally by the implementation, and |
|
470 shouldn't be needed on normal mocker usage. |
|
471 """ |
|
472 self._events.append(event) |
|
473 if self._ordering: |
|
474 orderer = event.add_task(Orderer(event.path)) |
|
475 if self._last_orderer: |
|
476 orderer.add_dependency(self._last_orderer) |
|
477 self._last_orderer = orderer |
|
478 return event |
|
479 |
|
480 def verify(self): |
|
481 """Check if all expectations were met, and raise AssertionError if not. |
|
482 |
|
483 The exception message will include a nice description of which |
|
484 expectations were not met, and why. |
|
485 """ |
|
486 errors = [] |
|
487 for event in self._events: |
|
488 try: |
|
489 event.verify() |
|
490 except AssertionError, e: |
|
491 error = str(e) |
|
492 if not error: |
|
493 raise RuntimeError("Empty error message from %r" |
|
494 % event) |
|
495 errors.append(error) |
|
496 if errors: |
|
497 message = [ERROR_PREFIX + "Unmet expectations:", ""] |
|
498 for error in errors: |
|
499 lines = error.splitlines() |
|
500 message.append("=> " + lines.pop(0)) |
|
501 message.extend([" " + line for line in lines]) |
|
502 message.append("") |
|
503 raise AssertionError(os.linesep.join(message)) |
|
504 |
|
505 def mock(self, spec_and_type=None, spec=None, type=None, |
|
506 name=None, count=True): |
|
507 """Return a new mock object. |
|
508 |
|
509 @param spec_and_type: Handy positional argument which sets both |
|
510 spec and type. |
|
511 @param spec: Method calls will be checked for correctness against |
|
512 the given class. |
|
513 @param type: If set, the Mock's __class__ attribute will return |
|
514 the given type. This will make C{isinstance()} calls |
|
515 on the object work. |
|
516 @param name: Name for the mock object, used in the representation of |
|
517 expressions. The name is rarely needed, as it's usually |
|
518 guessed correctly from the variable name used. |
|
519 @param count: If set to false, expressions may be executed any number |
|
520 of times, unless an expectation is explicitly set using |
|
521 the L{count()} method. By default, expressions are |
|
522 expected once. |
|
523 """ |
|
524 if spec_and_type is not None: |
|
525 spec = type = spec_and_type |
|
526 return Mock(self, spec=spec, type=type, name=name, count=count) |
|
527 |
|
528 def proxy(self, object, spec=True, type=True, name=None, count=True, |
|
529 passthrough=True): |
|
530 """Return a new mock object which proxies to the given object. |
|
531 |
|
532 Proxies are useful when only part of the behavior of an object |
|
533 is to be mocked. Unknown expressions may be passed through to |
|
534 the real implementation implicitly (if the C{passthrough} argument |
|
535 is True), or explicitly (using the L{passthrough()} method |
|
536 on the event). |
|
537 |
|
538 @param object: Real object to be proxied, and replaced by the mock |
|
539 on replay mode. It may also be an "import path", |
|
540 such as C{"time.time"}, in which case the object |
|
541 will be the C{time} function from the C{time} module. |
|
542 @param spec: Method calls will be checked for correctness against |
|
543 the given object, which may be a class or an instance |
|
544 where attributes will be looked up. Defaults to the |
|
545 the C{object} parameter. May be set to None explicitly, |
|
546 in which case spec checking is disabled. Checks may |
|
547 also be disabled explicitly on a per-event basis with |
|
548 the L{nospec()} method. |
|
549 @param type: If set, the Mock's __class__ attribute will return |
|
550 the given type. This will make C{isinstance()} calls |
|
551 on the object work. Defaults to the type of the |
|
552 C{object} parameter. May be set to None explicitly. |
|
553 @param name: Name for the mock object, used in the representation of |
|
554 expressions. The name is rarely needed, as it's usually |
|
555 guessed correctly from the variable name used. |
|
556 @param count: If set to false, expressions may be executed any number |
|
557 of times, unless an expectation is explicitly set using |
|
558 the L{count()} method. By default, expressions are |
|
559 expected once. |
|
560 @param passthrough: If set to False, passthrough of actions on the |
|
561 proxy to the real object will only happen when |
|
562 explicitly requested via the L{passthrough()} |
|
563 method. |
|
564 """ |
|
565 if isinstance(object, basestring): |
|
566 if name is None: |
|
567 name = object |
|
568 import_stack = object.split(".") |
|
569 attr_stack = [] |
|
570 while import_stack: |
|
571 module_path = ".".join(import_stack) |
|
572 try: |
|
573 object = __import__(module_path, {}, {}, [""]) |
|
574 except ImportError: |
|
575 attr_stack.insert(0, import_stack.pop()) |
|
576 if not import_stack: |
|
577 raise |
|
578 continue |
|
579 else: |
|
580 for attr in attr_stack: |
|
581 object = getattr(object, attr) |
|
582 break |
|
583 if spec is True: |
|
584 spec = object |
|
585 if type is True: |
|
586 type = __builtin__.type(object) |
|
587 return Mock(self, spec=spec, type=type, object=object, |
|
588 name=name, count=count, passthrough=passthrough) |
|
589 |
|
590 def replace(self, object, spec=True, type=True, name=None, count=True, |
|
591 passthrough=True): |
|
592 """Create a proxy, and replace the original object with the mock. |
|
593 |
|
594 On replay, the original object will be replaced by the returned |
|
595 proxy in all dictionaries found in the running interpreter via |
|
596 the garbage collecting system. This should cover module |
|
597 namespaces, class namespaces, instance namespaces, and so on. |
|
598 |
|
599 @param object: Real object to be proxied, and replaced by the mock |
|
600 on replay mode. It may also be an "import path", |
|
601 such as C{"time.time"}, in which case the object |
|
602 will be the C{time} function from the C{time} module. |
|
603 @param spec: Method calls will be checked for correctness against |
|
604 the given object, which may be a class or an instance |
|
605 where attributes will be looked up. Defaults to the |
|
606 the C{object} parameter. May be set to None explicitly, |
|
607 in which case spec checking is disabled. Checks may |
|
608 also be disabled explicitly on a per-event basis with |
|
609 the L{nospec()} method. |
|
610 @param type: If set, the Mock's __class__ attribute will return |
|
611 the given type. This will make C{isinstance()} calls |
|
612 on the object work. Defaults to the type of the |
|
613 C{object} parameter. May be set to None explicitly. |
|
614 @param name: Name for the mock object, used in the representation of |
|
615 expressions. The name is rarely needed, as it's usually |
|
616 guessed correctly from the variable name used. |
|
617 @param passthrough: If set to False, passthrough of actions on the |
|
618 proxy to the real object will only happen when |
|
619 explicitly requested via the L{passthrough()} |
|
620 method. |
|
621 """ |
|
622 mock = self.proxy(object, spec, type, name, count, passthrough) |
|
623 event = self._get_replay_restore_event() |
|
624 event.add_task(ProxyReplacer(mock)) |
|
625 return mock |
|
626 |
|
627 def patch(self, object, spec=True): |
|
628 """Patch an existing object to reproduce recorded events. |
|
629 |
|
630 @param object: Class or instance to be patched. |
|
631 @param spec: Method calls will be checked for correctness against |
|
632 the given object, which may be a class or an instance |
|
633 where attributes will be looked up. Defaults to the |
|
634 the C{object} parameter. May be set to None explicitly, |
|
635 in which case spec checking is disabled. Checks may |
|
636 also be disabled explicitly on a per-event basis with |
|
637 the L{nospec()} method. |
|
638 |
|
639 The result of this method is still a mock object, which can be |
|
640 used like any other mock object to record events. The difference |
|
641 is that when the mocker is put on replay mode, the *real* object |
|
642 will be modified to behave according to recorded expectations. |
|
643 |
|
644 Patching works in individual instances, and also in classes. |
|
645 When an instance is patched, recorded events will only be |
|
646 considered on this specific instance, and other instances should |
|
647 behave normally. When a class is patched, the reproduction of |
|
648 events will be considered on any instance of this class once |
|
649 created (collectively). |
|
650 |
|
651 Observe that, unlike with proxies which catch only events done |
|
652 through the mock object, *all* accesses to recorded expectations |
|
653 will be considered; even these coming from the object itself |
|
654 (e.g. C{self.hello()} is considered if this method was patched). |
|
655 While this is a very powerful feature, and many times the reason |
|
656 to use patches in the first place, it's important to keep this |
|
657 behavior in mind. |
|
658 |
|
659 Patching of the original object only takes place when the mocker |
|
660 is put on replay mode, and the patched object will be restored |
|
661 to its original state once the L{restore()} method is called |
|
662 (explicitly, or implicitly with alternative conventions, such as |
|
663 a C{with mocker:} block, or a MockerTestCase class). |
|
664 """ |
|
665 if spec is True: |
|
666 spec = object |
|
667 patcher = Patcher() |
|
668 event = self._get_replay_restore_event() |
|
669 event.add_task(patcher) |
|
670 mock = Mock(self, object=object, patcher=patcher, |
|
671 passthrough=True, spec=spec) |
|
672 object.__mocker_mock__ = mock |
|
673 return mock |
|
674 |
|
675 def act(self, path): |
|
676 """This is called by mock objects whenever something happens to them. |
|
677 |
|
678 This method is part of the implementation between the mocker |
|
679 and mock objects. |
|
680 """ |
|
681 if self._recording: |
|
682 event = self.add_event(Event(path)) |
|
683 for recorder in self._recorders: |
|
684 recorder(self, event) |
|
685 return Mock(self, path) |
|
686 else: |
|
687 # First run events that may run, then run unsatisfied events, then |
|
688 # ones not previously run. We put the index in the ordering tuple |
|
689 # instead of the actual event because we want a stable sort |
|
690 # (ordering between 2 events is undefined). |
|
691 events = self._events |
|
692 order = [(events[i].satisfied()*2 + events[i].has_run(), i) |
|
693 for i in range(len(events))] |
|
694 order.sort() |
|
695 postponed = None |
|
696 for weight, i in order: |
|
697 event = events[i] |
|
698 if event.matches(path): |
|
699 if event.may_run(path): |
|
700 return event.run(path) |
|
701 elif postponed is None: |
|
702 postponed = event |
|
703 if postponed is not None: |
|
704 return postponed.run(path) |
|
705 raise MatchError(ERROR_PREFIX + "Unexpected expression: %s" % path) |
|
706 |
|
707 def get_recorders(cls, self): |
|
708 """Return recorders associated with this mocker class or instance. |
|
709 |
|
710 This method may be called on mocker instances and also on mocker |
|
711 classes. See the L{add_recorder()} method for more information. |
|
712 """ |
|
713 return (self or cls)._recorders[:] |
|
714 get_recorders = classinstancemethod(get_recorders) |
|
715 |
|
716 def add_recorder(cls, self, recorder): |
|
717 """Add a recorder to this mocker class or instance. |
|
718 |
|
719 @param recorder: Callable accepting C{(mocker, event)} as parameters. |
|
720 |
|
721 This is part of the implementation of mocker. |
|
722 |
|
723 All registered recorders are called for translating events that |
|
724 happen during recording into expectations to be met once the state |
|
725 is switched to replay mode. |
|
726 |
|
727 This method may be called on mocker instances and also on mocker |
|
728 classes. When called on a class, the recorder will be used by |
|
729 all instances, and also inherited on subclassing. When called on |
|
730 instances, the recorder is added only to the given instance. |
|
731 """ |
|
732 (self or cls)._recorders.append(recorder) |
|
733 return recorder |
|
734 add_recorder = classinstancemethod(add_recorder) |
|
735 |
|
736 def remove_recorder(cls, self, recorder): |
|
737 """Remove the given recorder from this mocker class or instance. |
|
738 |
|
739 This method may be called on mocker classes and also on mocker |
|
740 instances. See the L{add_recorder()} method for more information. |
|
741 """ |
|
742 (self or cls)._recorders.remove(recorder) |
|
743 remove_recorder = classinstancemethod(remove_recorder) |
|
744 |
|
745 def result(self, value): |
|
746 """Make the last recorded event return the given value on replay. |
|
747 |
|
748 @param value: Object to be returned when the event is replayed. |
|
749 """ |
|
750 self.call(lambda *args, **kwargs: value) |
|
751 |
|
752 def generate(self, sequence): |
|
753 """Last recorded event will return a generator with the given sequence. |
|
754 |
|
755 @param sequence: Sequence of values to be generated. |
|
756 """ |
|
757 def generate(*args, **kwargs): |
|
758 for value in sequence: |
|
759 yield value |
|
760 self.call(generate) |
|
761 |
|
762 def throw(self, exception): |
|
763 """Make the last recorded event raise the given exception on replay. |
|
764 |
|
765 @param exception: Class or instance of exception to be raised. |
|
766 """ |
|
767 def raise_exception(*args, **kwargs): |
|
768 raise exception |
|
769 self.call(raise_exception) |
|
770 |
|
771 def call(self, func): |
|
772 """Make the last recorded event cause the given function to be called. |
|
773 |
|
774 @param func: Function to be called. |
|
775 |
|
776 The result of the function will be used as the event result. |
|
777 """ |
|
778 self._events[-1].add_task(FunctionRunner(func)) |
|
779 |
|
780 def count(self, min, max=False): |
|
781 """Last recorded event must be replayed between min and max times. |
|
782 |
|
783 @param min: Minimum number of times that the event must happen. |
|
784 @param max: Maximum number of times that the event must happen. If |
|
785 not given, it defaults to the same value of the C{min} |
|
786 parameter. If set to None, there is no upper limit, and |
|
787 the expectation is met as long as it happens at least |
|
788 C{min} times. |
|
789 """ |
|
790 event = self._events[-1] |
|
791 for task in event.get_tasks(): |
|
792 if isinstance(task, RunCounter): |
|
793 event.remove_task(task) |
|
794 event.add_task(RunCounter(min, max)) |
|
795 |
|
796 def is_ordering(self): |
|
797 """Return true if all events are being ordered. |
|
798 |
|
799 See the L{order()} method. |
|
800 """ |
|
801 return self._ordering |
|
802 |
|
803 def unorder(self): |
|
804 """Disable the ordered mode. |
|
805 |
|
806 See the L{order()} method for more information. |
|
807 """ |
|
808 self._ordering = False |
|
809 self._last_orderer = None |
|
810 |
|
811 def order(self, *path_holders): |
|
812 """Create an expectation of order between two or more events. |
|
813 |
|
814 @param path_holders: Objects returned as the result of recorded events. |
|
815 |
|
816 By default, mocker won't force events to happen precisely in |
|
817 the order they were recorded. Calling this method will change |
|
818 this behavior so that events will only match if reproduced in |
|
819 the correct order. |
|
820 |
|
821 There are two ways in which this method may be used. Which one |
|
822 is used in a given occasion depends only on convenience. |
|
823 |
|
824 If no arguments are passed, the mocker will be put in a mode where |
|
825 all the recorded events following the method call will only be met |
|
826 if they happen in order. When that's used, the mocker may be put |
|
827 back in unordered mode by calling the L{unorder()} method, or by |
|
828 using a 'with' block, like so:: |
|
829 |
|
830 with mocker.ordered(): |
|
831 <record events> |
|
832 |
|
833 In this case, only expressions in <record events> will be ordered, |
|
834 and the mocker will be back in unordered mode after the 'with' block. |
|
835 |
|
836 The second way to use it is by specifying precisely which events |
|
837 should be ordered. As an example:: |
|
838 |
|
839 mock = mocker.mock() |
|
840 expr1 = mock.hello() |
|
841 expr2 = mock.world |
|
842 expr3 = mock.x.y.z |
|
843 mocker.order(expr1, expr2, expr3) |
|
844 |
|
845 This method of ordering only works when the expression returns |
|
846 another object. |
|
847 |
|
848 Also check the L{after()} and L{before()} methods, which are |
|
849 alternative ways to perform this. |
|
850 """ |
|
851 if not path_holders: |
|
852 self._ordering = True |
|
853 return OrderedContext(self) |
|
854 |
|
855 last_orderer = None |
|
856 for path_holder in path_holders: |
|
857 if type(path_holder) is Path: |
|
858 path = path_holder |
|
859 else: |
|
860 path = path_holder.__mocker_path__ |
|
861 for event in self._events: |
|
862 if event.path is path: |
|
863 for task in event.get_tasks(): |
|
864 if isinstance(task, Orderer): |
|
865 orderer = task |
|
866 break |
|
867 else: |
|
868 orderer = Orderer(path) |
|
869 event.add_task(orderer) |
|
870 if last_orderer: |
|
871 orderer.add_dependency(last_orderer) |
|
872 last_orderer = orderer |
|
873 break |
|
874 |
|
875 def after(self, *path_holders): |
|
876 """Last recorded event must happen after events referred to. |
|
877 |
|
878 @param path_holders: Objects returned as the result of recorded events |
|
879 which should happen before the last recorded event |
|
880 |
|
881 As an example, the idiom:: |
|
882 |
|
883 expect(mock.x).after(mock.y, mock.z) |
|
884 |
|
885 is an alternative way to say:: |
|
886 |
|
887 expr_x = mock.x |
|
888 expr_y = mock.y |
|
889 expr_z = mock.z |
|
890 mocker.order(expr_y, expr_x) |
|
891 mocker.order(expr_z, expr_x) |
|
892 |
|
893 See L{order()} for more information. |
|
894 """ |
|
895 last_path = self._events[-1].path |
|
896 for path_holder in path_holders: |
|
897 self.order(path_holder, last_path) |
|
898 |
|
899 def before(self, *path_holders): |
|
900 """Last recorded event must happen before events referred to. |
|
901 |
|
902 @param path_holders: Objects returned as the result of recorded events |
|
903 which should happen after the last recorded event |
|
904 |
|
905 As an example, the idiom:: |
|
906 |
|
907 expect(mock.x).before(mock.y, mock.z) |
|
908 |
|
909 is an alternative way to say:: |
|
910 |
|
911 expr_x = mock.x |
|
912 expr_y = mock.y |
|
913 expr_z = mock.z |
|
914 mocker.order(expr_x, expr_y) |
|
915 mocker.order(expr_x, expr_z) |
|
916 |
|
917 See L{order()} for more information. |
|
918 """ |
|
919 last_path = self._events[-1].path |
|
920 for path_holder in path_holders: |
|
921 self.order(last_path, path_holder) |
|
922 |
|
923 def nospec(self): |
|
924 """Don't check method specification of real object on last event. |
|
925 |
|
926 By default, when using a mock created as the result of a call to |
|
927 L{proxy()}, L{replace()}, and C{patch()}, or when passing the spec |
|
928 attribute to the L{mock()} method, method calls on the given object |
|
929 are checked for correctness against the specification of the real |
|
930 object (or the explicitly provided spec). |
|
931 |
|
932 This method will disable that check specifically for the last |
|
933 recorded event. |
|
934 """ |
|
935 event = self._events[-1] |
|
936 for task in event.get_tasks(): |
|
937 if isinstance(task, SpecChecker): |
|
938 event.remove_task(task) |
|
939 |
|
940 def passthrough(self, result_callback=None): |
|
941 """Make the last recorded event run on the real object once seen. |
|
942 |
|
943 @param result_callback: If given, this function will be called with |
|
944 the result of the *real* method call as the only argument. |
|
945 |
|
946 This can only be used on proxies, as returned by the L{proxy()} |
|
947 and L{replace()} methods, or on mocks representing patched objects, |
|
948 as returned by the L{patch()} method. |
|
949 """ |
|
950 event = self._events[-1] |
|
951 if event.path.root_object is None: |
|
952 raise TypeError("Mock object isn't a proxy") |
|
953 event.add_task(PathExecuter(result_callback)) |
|
954 |
|
955 def __enter__(self): |
|
956 """Enter in a 'with' context. This will run replay().""" |
|
957 self.replay() |
|
958 return self |
|
959 |
|
960 def __exit__(self, type, value, traceback): |
|
961 """Exit from a 'with' context. |
|
962 |
|
963 This will run restore() at all times, but will only run verify() |
|
964 if the 'with' block itself hasn't raised an exception. Exceptions |
|
965 in that block are never swallowed. |
|
966 """ |
|
967 self.restore() |
|
968 if type is None: |
|
969 self.verify() |
|
970 return False |
|
971 |
|
972 def _get_replay_restore_event(self): |
|
973 """Return unique L{ReplayRestoreEvent}, creating if needed. |
|
974 |
|
975 Some tasks only want to replay/restore. When that's the case, |
|
976 they shouldn't act on other events during replay. Also, they |
|
977 can all be put in a single event when that's the case. Thus, |
|
978 we add a single L{ReplayRestoreEvent} as the first element of |
|
979 the list. |
|
980 """ |
|
981 if not self._events or type(self._events[0]) != ReplayRestoreEvent: |
|
982 self._events.insert(0, ReplayRestoreEvent()) |
|
983 return self._events[0] |
|
984 |
|
985 |
|
986 class OrderedContext(object): |
|
987 |
|
988 def __init__(self, mocker): |
|
989 self._mocker = mocker |
|
990 |
|
991 def __enter__(self): |
|
992 return None |
|
993 |
|
994 def __exit__(self, type, value, traceback): |
|
995 self._mocker.unorder() |
|
996 |
|
997 |
|
998 class Mocker(MockerBase): |
|
999 __doc__ = MockerBase.__doc__ |
|
1000 |
|
1001 # Decorator to add recorders on the standard Mocker class. |
|
1002 recorder = Mocker.add_recorder |
|
1003 |
|
1004 |
|
1005 # -------------------------------------------------------------------- |
|
1006 # Mock object. |
|
1007 |
|
1008 class Mock(object): |
|
1009 |
|
1010 def __init__(self, mocker, path=None, name=None, spec=None, type=None, |
|
1011 object=None, passthrough=False, patcher=None, count=True): |
|
1012 self.__mocker__ = mocker |
|
1013 self.__mocker_path__ = path or Path(self, object) |
|
1014 self.__mocker_name__ = name |
|
1015 self.__mocker_spec__ = spec |
|
1016 self.__mocker_object__ = object |
|
1017 self.__mocker_passthrough__ = passthrough |
|
1018 self.__mocker_patcher__ = patcher |
|
1019 self.__mocker_replace__ = False |
|
1020 self.__mocker_type__ = type |
|
1021 self.__mocker_count__ = count |
|
1022 |
|
1023 def __mocker_act__(self, kind, args=(), kwargs={}, object=None): |
|
1024 if self.__mocker_name__ is None: |
|
1025 self.__mocker_name__ = find_object_name(self, 2) |
|
1026 action = Action(kind, args, kwargs, self.__mocker_path__) |
|
1027 path = self.__mocker_path__ + action |
|
1028 if object is not None: |
|
1029 path.root_object = object |
|
1030 try: |
|
1031 return self.__mocker__.act(path) |
|
1032 except MatchError, exception: |
|
1033 root_mock = path.root_mock |
|
1034 if (path.root_object is not None and |
|
1035 root_mock.__mocker_passthrough__): |
|
1036 return path.execute(path.root_object) |
|
1037 # Reinstantiate to show raise statement on traceback, and |
|
1038 # also to make the traceback shown shorter. |
|
1039 raise MatchError(str(exception)) |
|
1040 except AssertionError, e: |
|
1041 lines = str(e).splitlines() |
|
1042 message = [ERROR_PREFIX + "Unmet expectation:", ""] |
|
1043 message.append("=> " + lines.pop(0)) |
|
1044 message.extend([" " + line for line in lines]) |
|
1045 message.append("") |
|
1046 raise AssertionError(os.linesep.join(message)) |
|
1047 |
|
1048 def __getattribute__(self, name): |
|
1049 if name.startswith("__mocker_"): |
|
1050 return super(Mock, self).__getattribute__(name) |
|
1051 if name == "__class__": |
|
1052 if self.__mocker__.is_recording() or self.__mocker_type__ is None: |
|
1053 return type(self) |
|
1054 return self.__mocker_type__ |
|
1055 return self.__mocker_act__("getattr", (name,)) |
|
1056 |
|
1057 def __setattr__(self, name, value): |
|
1058 if name.startswith("__mocker_"): |
|
1059 return super(Mock, self).__setattr__(name, value) |
|
1060 return self.__mocker_act__("setattr", (name, value)) |
|
1061 |
|
1062 def __delattr__(self, name): |
|
1063 return self.__mocker_act__("delattr", (name,)) |
|
1064 |
|
1065 def __call__(self, *args, **kwargs): |
|
1066 return self.__mocker_act__("call", args, kwargs) |
|
1067 |
|
1068 def __contains__(self, value): |
|
1069 return self.__mocker_act__("contains", (value,)) |
|
1070 |
|
1071 def __getitem__(self, key): |
|
1072 return self.__mocker_act__("getitem", (key,)) |
|
1073 |
|
1074 def __setitem__(self, key, value): |
|
1075 return self.__mocker_act__("setitem", (key, value)) |
|
1076 |
|
1077 def __delitem__(self, key): |
|
1078 return self.__mocker_act__("delitem", (key,)) |
|
1079 |
|
1080 def __len__(self): |
|
1081 # MatchError is turned on an AttributeError so that list() and |
|
1082 # friends act properly when trying to get length hints on |
|
1083 # something that doesn't offer them. |
|
1084 try: |
|
1085 result = self.__mocker_act__("len") |
|
1086 except MatchError, e: |
|
1087 raise AttributeError(str(e)) |
|
1088 if type(result) is Mock: |
|
1089 return 0 |
|
1090 return result |
|
1091 |
|
1092 def __nonzero__(self): |
|
1093 try: |
|
1094 return self.__mocker_act__("nonzero") |
|
1095 except MatchError, e: |
|
1096 return True |
|
1097 |
|
1098 def __iter__(self): |
|
1099 # XXX On py3k, when next() becomes __next__(), we'll be able |
|
1100 # to return the mock itself because it will be considered |
|
1101 # an iterator (we'll be mocking __next__ as well, which we |
|
1102 # can't now). |
|
1103 result = self.__mocker_act__("iter") |
|
1104 if type(result) is Mock: |
|
1105 return iter([]) |
|
1106 return result |
|
1107 |
|
1108 # When adding a new action kind here, also add support for it on |
|
1109 # Action.execute() and Path.__str__(). |
|
1110 |
|
1111 |
|
1112 def find_object_name(obj, depth=0): |
|
1113 """Try to detect how the object is named on a previous scope.""" |
|
1114 try: |
|
1115 frame = sys._getframe(depth+1) |
|
1116 except: |
|
1117 return None |
|
1118 for name, frame_obj in frame.f_locals.iteritems(): |
|
1119 if frame_obj is obj: |
|
1120 return name |
|
1121 self = frame.f_locals.get("self") |
|
1122 if self is not None: |
|
1123 try: |
|
1124 items = list(self.__dict__.iteritems()) |
|
1125 except: |
|
1126 pass |
|
1127 else: |
|
1128 for name, self_obj in items: |
|
1129 if self_obj is obj: |
|
1130 return name |
|
1131 return None |
|
1132 |
|
1133 |
|
1134 # -------------------------------------------------------------------- |
|
1135 # Action and path. |
|
1136 |
|
1137 class Action(object): |
|
1138 |
|
1139 def __init__(self, kind, args, kwargs, path=None): |
|
1140 self.kind = kind |
|
1141 self.args = args |
|
1142 self.kwargs = kwargs |
|
1143 self.path = path |
|
1144 self._execute_cache = {} |
|
1145 |
|
1146 def __repr__(self): |
|
1147 if self.path is None: |
|
1148 return "Action(%r, %r, %r)" % (self.kind, self.args, self.kwargs) |
|
1149 return "Action(%r, %r, %r, %r)" % \ |
|
1150 (self.kind, self.args, self.kwargs, self.path) |
|
1151 |
|
1152 def __eq__(self, other): |
|
1153 return (self.kind == other.kind and |
|
1154 self.args == other.args and |
|
1155 self.kwargs == other.kwargs) |
|
1156 |
|
1157 def __ne__(self, other): |
|
1158 return not self.__eq__(other) |
|
1159 |
|
1160 def matches(self, other): |
|
1161 return (self.kind == other.kind and |
|
1162 match_params(self.args, self.kwargs, other.args, other.kwargs)) |
|
1163 |
|
1164 def execute(self, object): |
|
1165 # This caching scheme may fail if the object gets deallocated before |
|
1166 # the action, as the id might get reused. It's somewhat easy to fix |
|
1167 # that with a weakref callback. For our uses, though, the object |
|
1168 # should never get deallocated before the action itself, so we'll |
|
1169 # just keep it simple. |
|
1170 if id(object) in self._execute_cache: |
|
1171 return self._execute_cache[id(object)] |
|
1172 execute = getattr(object, "__mocker_execute__", None) |
|
1173 if execute is not None: |
|
1174 result = execute(self, object) |
|
1175 else: |
|
1176 kind = self.kind |
|
1177 if kind == "getattr": |
|
1178 result = getattr(object, self.args[0]) |
|
1179 elif kind == "setattr": |
|
1180 result = setattr(object, self.args[0], self.args[1]) |
|
1181 elif kind == "delattr": |
|
1182 result = delattr(object, self.args[0]) |
|
1183 elif kind == "call": |
|
1184 result = object(*self.args, **self.kwargs) |
|
1185 elif kind == "contains": |
|
1186 result = self.args[0] in object |
|
1187 elif kind == "getitem": |
|
1188 result = object[self.args[0]] |
|
1189 elif kind == "setitem": |
|
1190 result = object[self.args[0]] = self.args[1] |
|
1191 elif kind == "delitem": |
|
1192 del object[self.args[0]] |
|
1193 result = None |
|
1194 elif kind == "len": |
|
1195 result = len(object) |
|
1196 elif kind == "nonzero": |
|
1197 result = bool(object) |
|
1198 elif kind == "iter": |
|
1199 result = iter(object) |
|
1200 else: |
|
1201 raise RuntimeError("Don't know how to execute %r kind." % kind) |
|
1202 self._execute_cache[id(object)] = result |
|
1203 return result |
|
1204 |
|
1205 |
|
1206 class Path(object): |
|
1207 |
|
1208 def __init__(self, root_mock, root_object=None, actions=()): |
|
1209 self.root_mock = root_mock |
|
1210 self.root_object = root_object |
|
1211 self.actions = tuple(actions) |
|
1212 self.__mocker_replace__ = False |
|
1213 |
|
1214 def parent_path(self): |
|
1215 if not self.actions: |
|
1216 return None |
|
1217 return self.actions[-1].path |
|
1218 parent_path = property(parent_path) |
|
1219 |
|
1220 def __add__(self, action): |
|
1221 """Return a new path which includes the given action at the end.""" |
|
1222 return self.__class__(self.root_mock, self.root_object, |
|
1223 self.actions + (action,)) |
|
1224 |
|
1225 def __eq__(self, other): |
|
1226 """Verify if the two paths are equal. |
|
1227 |
|
1228 Two paths are equal if they refer to the same mock object, and |
|
1229 have the actions with equal kind, args and kwargs. |
|
1230 """ |
|
1231 if (self.root_mock is not other.root_mock or |
|
1232 self.root_object is not other.root_object or |
|
1233 len(self.actions) != len(other.actions)): |
|
1234 return False |
|
1235 for action, other_action in zip(self.actions, other.actions): |
|
1236 if action != other_action: |
|
1237 return False |
|
1238 return True |
|
1239 |
|
1240 def matches(self, other): |
|
1241 """Verify if the two paths are equivalent. |
|
1242 |
|
1243 Two paths are equal if they refer to the same mock object, and |
|
1244 have the same actions performed on them. |
|
1245 """ |
|
1246 if (self.root_mock is not other.root_mock or |
|
1247 len(self.actions) != len(other.actions)): |
|
1248 return False |
|
1249 for action, other_action in zip(self.actions, other.actions): |
|
1250 if not action.matches(other_action): |
|
1251 return False |
|
1252 return True |
|
1253 |
|
1254 def execute(self, object): |
|
1255 """Execute all actions sequentially on object, and return result. |
|
1256 """ |
|
1257 for action in self.actions: |
|
1258 object = action.execute(object) |
|
1259 return object |
|
1260 |
|
1261 def __str__(self): |
|
1262 """Transform the path into a nice string such as obj.x.y('z').""" |
|
1263 result = self.root_mock.__mocker_name__ or "<mock>" |
|
1264 for action in self.actions: |
|
1265 if action.kind == "getattr": |
|
1266 result = "%s.%s" % (result, action.args[0]) |
|
1267 elif action.kind == "setattr": |
|
1268 result = "%s.%s = %r" % (result, action.args[0], action.args[1]) |
|
1269 elif action.kind == "delattr": |
|
1270 result = "del %s.%s" % (result, action.args[0]) |
|
1271 elif action.kind == "call": |
|
1272 args = [repr(x) for x in action.args] |
|
1273 items = list(action.kwargs.iteritems()) |
|
1274 items.sort() |
|
1275 for pair in items: |
|
1276 args.append("%s=%r" % pair) |
|
1277 result = "%s(%s)" % (result, ", ".join(args)) |
|
1278 elif action.kind == "contains": |
|
1279 result = "%r in %s" % (action.args[0], result) |
|
1280 elif action.kind == "getitem": |
|
1281 result = "%s[%r]" % (result, action.args[0]) |
|
1282 elif action.kind == "setitem": |
|
1283 result = "%s[%r] = %r" % (result, action.args[0], |
|
1284 action.args[1]) |
|
1285 elif action.kind == "delitem": |
|
1286 result = "del %s[%r]" % (result, action.args[0]) |
|
1287 elif action.kind == "len": |
|
1288 result = "len(%s)" % result |
|
1289 elif action.kind == "nonzero": |
|
1290 result = "bool(%s)" % result |
|
1291 elif action.kind == "iter": |
|
1292 result = "iter(%s)" % result |
|
1293 else: |
|
1294 raise RuntimeError("Don't know how to format kind %r" % |
|
1295 action.kind) |
|
1296 return result |
|
1297 |
|
1298 |
|
1299 class SpecialArgument(object): |
|
1300 """Base for special arguments for matching parameters.""" |
|
1301 |
|
1302 def __init__(self, object=None): |
|
1303 self.object = object |
|
1304 |
|
1305 def __repr__(self): |
|
1306 if self.object is None: |
|
1307 return self.__class__.__name__ |
|
1308 else: |
|
1309 return "%s(%r)" % (self.__class__.__name__, self.object) |
|
1310 |
|
1311 def matches(self, other): |
|
1312 return True |
|
1313 |
|
1314 def __eq__(self, other): |
|
1315 return type(other) == type(self) and self.object == other.object |
|
1316 |
|
1317 |
|
1318 class ANY(SpecialArgument): |
|
1319 """Matches any single argument.""" |
|
1320 |
|
1321 ANY = ANY() |
|
1322 |
|
1323 |
|
1324 class ARGS(SpecialArgument): |
|
1325 """Matches zero or more positional arguments.""" |
|
1326 |
|
1327 ARGS = ARGS() |
|
1328 |
|
1329 |
|
1330 class KWARGS(SpecialArgument): |
|
1331 """Matches zero or more keyword arguments.""" |
|
1332 |
|
1333 KWARGS = KWARGS() |
|
1334 |
|
1335 |
|
1336 class IS(SpecialArgument): |
|
1337 |
|
1338 def matches(self, other): |
|
1339 return self.object is other |
|
1340 |
|
1341 def __eq__(self, other): |
|
1342 return type(other) == type(self) and self.object is other.object |
|
1343 |
|
1344 |
|
1345 class CONTAINS(SpecialArgument): |
|
1346 |
|
1347 def matches(self, other): |
|
1348 try: |
|
1349 other.__contains__ |
|
1350 except AttributeError: |
|
1351 try: |
|
1352 iter(other) |
|
1353 except TypeError: |
|
1354 # If an object can't be iterated, and has no __contains__ |
|
1355 # hook, it'd blow up on the test below. We test this in |
|
1356 # advance to prevent catching more errors than we really |
|
1357 # want. |
|
1358 return False |
|
1359 return self.object in other |
|
1360 |
|
1361 |
|
1362 class IN(SpecialArgument): |
|
1363 |
|
1364 def matches(self, other): |
|
1365 return other in self.object |
|
1366 |
|
1367 |
|
1368 class MATCH(SpecialArgument): |
|
1369 |
|
1370 def matches(self, other): |
|
1371 return bool(self.object(other)) |
|
1372 |
|
1373 def __eq__(self, other): |
|
1374 return type(other) == type(self) and self.object is other.object |
|
1375 |
|
1376 |
|
1377 def match_params(args1, kwargs1, args2, kwargs2): |
|
1378 """Match the two sets of parameters, considering special parameters.""" |
|
1379 |
|
1380 has_args = ARGS in args1 |
|
1381 has_kwargs = KWARGS in args1 |
|
1382 |
|
1383 if has_kwargs: |
|
1384 args1 = [arg1 for arg1 in args1 if arg1 is not KWARGS] |
|
1385 elif len(kwargs1) != len(kwargs2): |
|
1386 return False |
|
1387 |
|
1388 if not has_args and len(args1) != len(args2): |
|
1389 return False |
|
1390 |
|
1391 # Either we have the same number of kwargs, or unknown keywords are |
|
1392 # accepted (KWARGS was used), so check just the ones in kwargs1. |
|
1393 for key, arg1 in kwargs1.iteritems(): |
|
1394 if key not in kwargs2: |
|
1395 return False |
|
1396 arg2 = kwargs2[key] |
|
1397 if isinstance(arg1, SpecialArgument): |
|
1398 if not arg1.matches(arg2): |
|
1399 return False |
|
1400 elif arg1 != arg2: |
|
1401 return False |
|
1402 |
|
1403 # Keywords match. Now either we have the same number of |
|
1404 # arguments, or ARGS was used. If ARGS wasn't used, arguments |
|
1405 # must match one-on-one necessarily. |
|
1406 if not has_args: |
|
1407 for arg1, arg2 in zip(args1, args2): |
|
1408 if isinstance(arg1, SpecialArgument): |
|
1409 if not arg1.matches(arg2): |
|
1410 return False |
|
1411 elif arg1 != arg2: |
|
1412 return False |
|
1413 return True |
|
1414 |
|
1415 # Easy choice. Keywords are matching, and anything on args is accepted. |
|
1416 if (ARGS,) == args1: |
|
1417 return True |
|
1418 |
|
1419 # We have something different there. If we don't have positional |
|
1420 # arguments on the original call, it can't match. |
|
1421 if not args2: |
|
1422 # Unless we have just several ARGS (which is bizarre, but..). |
|
1423 for arg1 in args1: |
|
1424 if arg1 is not ARGS: |
|
1425 return False |
|
1426 return True |
|
1427 |
|
1428 # Ok, all bets are lost. We have to actually do the more expensive |
|
1429 # matching. This is an algorithm based on the idea of the Levenshtein |
|
1430 # Distance between two strings, but heavily hacked for this purpose. |
|
1431 args2l = len(args2) |
|
1432 if args1[0] is ARGS: |
|
1433 args1 = args1[1:] |
|
1434 array = [0]*args2l |
|
1435 else: |
|
1436 array = [1]*args2l |
|
1437 for i in range(len(args1)): |
|
1438 last = array[0] |
|
1439 if args1[i] is ARGS: |
|
1440 for j in range(1, args2l): |
|
1441 last, array[j] = array[j], min(array[j-1], array[j], last) |
|
1442 else: |
|
1443 array[0] = i or int(args1[i] != args2[0]) |
|
1444 for j in range(1, args2l): |
|
1445 last, array[j] = array[j], last or int(args1[i] != args2[j]) |
|
1446 if 0 not in array: |
|
1447 return False |
|
1448 if array[-1] != 0: |
|
1449 return False |
|
1450 return True |
|
1451 |
|
1452 |
|
1453 # -------------------------------------------------------------------- |
|
1454 # Event and task base. |
|
1455 |
|
1456 class Event(object): |
|
1457 """Aggregation of tasks that keep track of a recorded action. |
|
1458 |
|
1459 An event represents something that may or may not happen while the |
|
1460 mocked environment is running, such as an attribute access, or a |
|
1461 method call. The event is composed of several tasks that are |
|
1462 orchestrated together to create a composed meaning for the event, |
|
1463 including for which actions it should be run, what happens when it |
|
1464 runs, and what's the expectations about the actions run. |
|
1465 """ |
|
1466 |
|
1467 def __init__(self, path=None): |
|
1468 self.path = path |
|
1469 self._tasks = [] |
|
1470 self._has_run = False |
|
1471 |
|
1472 def add_task(self, task): |
|
1473 """Add a new task to this taks.""" |
|
1474 self._tasks.append(task) |
|
1475 return task |
|
1476 |
|
1477 def remove_task(self, task): |
|
1478 self._tasks.remove(task) |
|
1479 |
|
1480 def get_tasks(self): |
|
1481 return self._tasks[:] |
|
1482 |
|
1483 def matches(self, path): |
|
1484 """Return true if *all* tasks match the given path.""" |
|
1485 for task in self._tasks: |
|
1486 if not task.matches(path): |
|
1487 return False |
|
1488 return bool(self._tasks) |
|
1489 |
|
1490 def has_run(self): |
|
1491 return self._has_run |
|
1492 |
|
1493 def may_run(self, path): |
|
1494 """Verify if any task would certainly raise an error if run. |
|
1495 |
|
1496 This will call the C{may_run()} method on each task and return |
|
1497 false if any of them returns false. |
|
1498 """ |
|
1499 for task in self._tasks: |
|
1500 if not task.may_run(path): |
|
1501 return False |
|
1502 return True |
|
1503 |
|
1504 def run(self, path): |
|
1505 """Run all tasks with the given action. |
|
1506 |
|
1507 @param path: The path of the expression run. |
|
1508 |
|
1509 Running an event means running all of its tasks individually and in |
|
1510 order. An event should only ever be run if all of its tasks claim to |
|
1511 match the given action. |
|
1512 |
|
1513 The result of this method will be the last result of a task |
|
1514 which isn't None, or None if they're all None. |
|
1515 """ |
|
1516 self._has_run = True |
|
1517 result = None |
|
1518 errors = [] |
|
1519 for task in self._tasks: |
|
1520 try: |
|
1521 task_result = task.run(path) |
|
1522 except AssertionError, e: |
|
1523 error = str(e) |
|
1524 if not error: |
|
1525 raise RuntimeError("Empty error message from %r" % task) |
|
1526 errors.append(error) |
|
1527 else: |
|
1528 if task_result is not None: |
|
1529 result = task_result |
|
1530 if errors: |
|
1531 message = [str(self.path)] |
|
1532 if str(path) != message[0]: |
|
1533 message.append("- Run: %s" % path) |
|
1534 for error in errors: |
|
1535 lines = error.splitlines() |
|
1536 message.append("- " + lines.pop(0)) |
|
1537 message.extend([" " + line for line in lines]) |
|
1538 raise AssertionError(os.linesep.join(message)) |
|
1539 return result |
|
1540 |
|
1541 def satisfied(self): |
|
1542 """Return true if all tasks are satisfied. |
|
1543 |
|
1544 Being satisfied means that there are no unmet expectations. |
|
1545 """ |
|
1546 for task in self._tasks: |
|
1547 try: |
|
1548 task.verify() |
|
1549 except AssertionError: |
|
1550 return False |
|
1551 return True |
|
1552 |
|
1553 def verify(self): |
|
1554 """Run verify on all tasks. |
|
1555 |
|
1556 The verify method is supposed to raise an AssertionError if the |
|
1557 task has unmet expectations, with a one-line explanation about |
|
1558 why this item is unmet. This method should be safe to be called |
|
1559 multiple times without side effects. |
|
1560 """ |
|
1561 errors = [] |
|
1562 for task in self._tasks: |
|
1563 try: |
|
1564 task.verify() |
|
1565 except AssertionError, e: |
|
1566 error = str(e) |
|
1567 if not error: |
|
1568 raise RuntimeError("Empty error message from %r" % task) |
|
1569 errors.append(error) |
|
1570 if errors: |
|
1571 message = [str(self.path)] |
|
1572 for error in errors: |
|
1573 lines = error.splitlines() |
|
1574 message.append("- " + lines.pop(0)) |
|
1575 message.extend([" " + line for line in lines]) |
|
1576 raise AssertionError(os.linesep.join(message)) |
|
1577 |
|
1578 def replay(self): |
|
1579 """Put all tasks in replay mode.""" |
|
1580 self._has_run = False |
|
1581 for task in self._tasks: |
|
1582 task.replay() |
|
1583 |
|
1584 def restore(self): |
|
1585 """Restore the state of all tasks.""" |
|
1586 for task in self._tasks: |
|
1587 task.restore() |
|
1588 |
|
1589 |
|
1590 class ReplayRestoreEvent(Event): |
|
1591 """Helper event for tasks which need replay/restore but shouldn't match.""" |
|
1592 |
|
1593 def matches(self, path): |
|
1594 return False |
|
1595 |
|
1596 |
|
1597 class Task(object): |
|
1598 """Element used to track one specific aspect on an event. |
|
1599 |
|
1600 A task is responsible for adding any kind of logic to an event. |
|
1601 Examples of that are counting the number of times the event was |
|
1602 made, verifying parameters if any, and so on. |
|
1603 """ |
|
1604 |
|
1605 def matches(self, path): |
|
1606 """Return true if the task is supposed to be run for the given path. |
|
1607 """ |
|
1608 return True |
|
1609 |
|
1610 def may_run(self, path): |
|
1611 """Return false if running this task would certainly raise an error.""" |
|
1612 return True |
|
1613 |
|
1614 def run(self, path): |
|
1615 """Perform the task item, considering that the given action happened. |
|
1616 """ |
|
1617 |
|
1618 def verify(self): |
|
1619 """Raise AssertionError if expectations for this item are unmet. |
|
1620 |
|
1621 The verify method is supposed to raise an AssertionError if the |
|
1622 task has unmet expectations, with a one-line explanation about |
|
1623 why this item is unmet. This method should be safe to be called |
|
1624 multiple times without side effects. |
|
1625 """ |
|
1626 |
|
1627 def replay(self): |
|
1628 """Put the task in replay mode. |
|
1629 |
|
1630 Any expectations of the task should be reset. |
|
1631 """ |
|
1632 |
|
1633 def restore(self): |
|
1634 """Restore any environmental changes made by the task. |
|
1635 |
|
1636 Verify should continue to work after this is called. |
|
1637 """ |
|
1638 |
|
1639 |
|
1640 # -------------------------------------------------------------------- |
|
1641 # Task implementations. |
|
1642 |
|
1643 class OnRestoreCaller(Task): |
|
1644 """Call a given callback when restoring.""" |
|
1645 |
|
1646 def __init__(self, callback): |
|
1647 self._callback = callback |
|
1648 |
|
1649 def restore(self): |
|
1650 self._callback() |
|
1651 |
|
1652 |
|
1653 class PathMatcher(Task): |
|
1654 """Match the action path against a given path.""" |
|
1655 |
|
1656 def __init__(self, path): |
|
1657 self.path = path |
|
1658 |
|
1659 def matches(self, path): |
|
1660 return self.path.matches(path) |
|
1661 |
|
1662 def path_matcher_recorder(mocker, event): |
|
1663 event.add_task(PathMatcher(event.path)) |
|
1664 |
|
1665 Mocker.add_recorder(path_matcher_recorder) |
|
1666 |
|
1667 |
|
1668 class RunCounter(Task): |
|
1669 """Task which verifies if the number of runs are within given boundaries. |
|
1670 """ |
|
1671 |
|
1672 def __init__(self, min, max=False): |
|
1673 self.min = min |
|
1674 if max is None: |
|
1675 self.max = sys.maxint |
|
1676 elif max is False: |
|
1677 self.max = min |
|
1678 else: |
|
1679 self.max = max |
|
1680 self._runs = 0 |
|
1681 |
|
1682 def replay(self): |
|
1683 self._runs = 0 |
|
1684 |
|
1685 def may_run(self, path): |
|
1686 return self._runs < self.max |
|
1687 |
|
1688 def run(self, path): |
|
1689 self._runs += 1 |
|
1690 if self._runs > self.max: |
|
1691 self.verify() |
|
1692 |
|
1693 def verify(self): |
|
1694 if not self.min <= self._runs <= self.max: |
|
1695 if self._runs < self.min: |
|
1696 raise AssertionError("Performed fewer times than expected.") |
|
1697 raise AssertionError("Performed more times than expected.") |
|
1698 |
|
1699 |
|
1700 class ImplicitRunCounter(RunCounter): |
|
1701 """RunCounter inserted by default on any event. |
|
1702 |
|
1703 This is a way to differentiate explicitly added counters and |
|
1704 implicit ones. |
|
1705 """ |
|
1706 |
|
1707 def run_counter_recorder(mocker, event): |
|
1708 """Any event may be repeated once, unless disabled by default.""" |
|
1709 if event.path.root_mock.__mocker_count__: |
|
1710 event.add_task(ImplicitRunCounter(1)) |
|
1711 |
|
1712 Mocker.add_recorder(run_counter_recorder) |
|
1713 |
|
1714 def run_counter_removal_recorder(mocker, event): |
|
1715 """ |
|
1716 Events created by getattr actions which lead to other events |
|
1717 may be repeated any number of times. For that, we remove implicit |
|
1718 run counters of any getattr actions leading to the current one. |
|
1719 """ |
|
1720 parent_path = event.path.parent_path |
|
1721 for event in mocker.get_events()[::-1]: |
|
1722 if (event.path is parent_path and |
|
1723 event.path.actions[-1].kind == "getattr"): |
|
1724 for task in event.get_tasks(): |
|
1725 if type(task) is ImplicitRunCounter: |
|
1726 event.remove_task(task) |
|
1727 |
|
1728 Mocker.add_recorder(run_counter_removal_recorder) |
|
1729 |
|
1730 |
|
1731 class MockReturner(Task): |
|
1732 """Return a mock based on the action path.""" |
|
1733 |
|
1734 def __init__(self, mocker): |
|
1735 self.mocker = mocker |
|
1736 |
|
1737 def run(self, path): |
|
1738 return Mock(self.mocker, path) |
|
1739 |
|
1740 def mock_returner_recorder(mocker, event): |
|
1741 """Events that lead to other events must return mock objects.""" |
|
1742 parent_path = event.path.parent_path |
|
1743 for event in mocker.get_events(): |
|
1744 if event.path is parent_path: |
|
1745 for task in event.get_tasks(): |
|
1746 if isinstance(task, MockReturner): |
|
1747 break |
|
1748 else: |
|
1749 event.add_task(MockReturner(mocker)) |
|
1750 break |
|
1751 |
|
1752 Mocker.add_recorder(mock_returner_recorder) |
|
1753 |
|
1754 |
|
1755 class FunctionRunner(Task): |
|
1756 """Task that runs a function everything it's run. |
|
1757 |
|
1758 Arguments of the last action in the path are passed to the function, |
|
1759 and the function result is also returned. |
|
1760 """ |
|
1761 |
|
1762 def __init__(self, func): |
|
1763 self._func = func |
|
1764 |
|
1765 def run(self, path): |
|
1766 action = path.actions[-1] |
|
1767 return self._func(*action.args, **action.kwargs) |
|
1768 |
|
1769 |
|
1770 class PathExecuter(Task): |
|
1771 """Task that executes a path in the real object, and returns the result.""" |
|
1772 |
|
1773 def __init__(self, result_callback=None): |
|
1774 self._result_callback = result_callback |
|
1775 |
|
1776 def get_result_callback(self): |
|
1777 return self._result_callback |
|
1778 |
|
1779 def run(self, path): |
|
1780 result = path.execute(path.root_object) |
|
1781 if self._result_callback is not None: |
|
1782 self._result_callback(result) |
|
1783 return result |
|
1784 |
|
1785 |
|
1786 class Orderer(Task): |
|
1787 """Task to establish an order relation between two events. |
|
1788 |
|
1789 An orderer task will only match once all its dependencies have |
|
1790 been run. |
|
1791 """ |
|
1792 |
|
1793 def __init__(self, path): |
|
1794 self.path = path |
|
1795 self._run = False |
|
1796 self._dependencies = [] |
|
1797 |
|
1798 def replay(self): |
|
1799 self._run = False |
|
1800 |
|
1801 def has_run(self): |
|
1802 return self._run |
|
1803 |
|
1804 def may_run(self, path): |
|
1805 for dependency in self._dependencies: |
|
1806 if not dependency.has_run(): |
|
1807 return False |
|
1808 return True |
|
1809 |
|
1810 def run(self, path): |
|
1811 for dependency in self._dependencies: |
|
1812 if not dependency.has_run(): |
|
1813 raise AssertionError("Should be after: %s" % dependency.path) |
|
1814 self._run = True |
|
1815 |
|
1816 def add_dependency(self, orderer): |
|
1817 self._dependencies.append(orderer) |
|
1818 |
|
1819 def get_dependencies(self): |
|
1820 return self._dependencies |
|
1821 |
|
1822 |
|
1823 class SpecChecker(Task): |
|
1824 """Task to check if arguments of the last action conform to a real method. |
|
1825 """ |
|
1826 |
|
1827 def __init__(self, method): |
|
1828 self._method = method |
|
1829 self._unsupported = False |
|
1830 |
|
1831 if method: |
|
1832 try: |
|
1833 self._args, self._varargs, self._varkwargs, self._defaults = \ |
|
1834 inspect.getargspec(method) |
|
1835 except TypeError: |
|
1836 self._unsupported = True |
|
1837 else: |
|
1838 if self._defaults is None: |
|
1839 self._defaults = () |
|
1840 if type(method) is type(self.run): |
|
1841 self._args = self._args[1:] |
|
1842 |
|
1843 def get_method(self): |
|
1844 return self._method |
|
1845 |
|
1846 def _raise(self, message): |
|
1847 spec = inspect.formatargspec(self._args, self._varargs, |
|
1848 self._varkwargs, self._defaults) |
|
1849 raise AssertionError("Specification is %s%s: %s" % |
|
1850 (self._method.__name__, spec, message)) |
|
1851 |
|
1852 def verify(self): |
|
1853 if not self._method: |
|
1854 raise AssertionError("Method not found in real specification") |
|
1855 |
|
1856 def may_run(self, path): |
|
1857 try: |
|
1858 self.run(path) |
|
1859 except AssertionError: |
|
1860 return False |
|
1861 return True |
|
1862 |
|
1863 def run(self, path): |
|
1864 if not self._method: |
|
1865 raise AssertionError("Method not found in real specification") |
|
1866 if self._unsupported: |
|
1867 return # Can't check it. Happens with builtin functions. :-( |
|
1868 action = path.actions[-1] |
|
1869 obtained_len = len(action.args) |
|
1870 obtained_kwargs = action.kwargs.copy() |
|
1871 nodefaults_len = len(self._args) - len(self._defaults) |
|
1872 for i, name in enumerate(self._args): |
|
1873 if i < obtained_len and name in action.kwargs: |
|
1874 self._raise("%r provided twice" % name) |
|
1875 if (i >= obtained_len and i < nodefaults_len and |
|
1876 name not in action.kwargs): |
|
1877 self._raise("%r not provided" % name) |
|
1878 obtained_kwargs.pop(name, None) |
|
1879 if obtained_len > len(self._args) and not self._varargs: |
|
1880 self._raise("too many args provided") |
|
1881 if obtained_kwargs and not self._varkwargs: |
|
1882 self._raise("unknown kwargs: %s" % ", ".join(obtained_kwargs)) |
|
1883 |
|
1884 def spec_checker_recorder(mocker, event): |
|
1885 spec = event.path.root_mock.__mocker_spec__ |
|
1886 if spec: |
|
1887 actions = event.path.actions |
|
1888 if len(actions) == 1: |
|
1889 if actions[0].kind == "call": |
|
1890 method = getattr(spec, "__call__", None) |
|
1891 event.add_task(SpecChecker(method)) |
|
1892 elif len(actions) == 2: |
|
1893 if actions[0].kind == "getattr" and actions[1].kind == "call": |
|
1894 method = getattr(spec, actions[0].args[0], None) |
|
1895 event.add_task(SpecChecker(method)) |
|
1896 |
|
1897 Mocker.add_recorder(spec_checker_recorder) |
|
1898 |
|
1899 |
|
1900 class ProxyReplacer(Task): |
|
1901 """Task which installs and deinstalls proxy mocks. |
|
1902 |
|
1903 This task will replace a real object by a mock in all dictionaries |
|
1904 found in the running interpreter via the garbage collecting system. |
|
1905 """ |
|
1906 |
|
1907 def __init__(self, mock): |
|
1908 self.mock = mock |
|
1909 self.__mocker_replace__ = False |
|
1910 |
|
1911 def replay(self): |
|
1912 global_replace(self.mock.__mocker_object__, self.mock) |
|
1913 |
|
1914 def restore(self): |
|
1915 global_replace(self.mock, self.mock.__mocker_object__) |
|
1916 |
|
1917 |
|
1918 def global_replace(remove, install): |
|
1919 """Replace object 'remove' with object 'install' on all dictionaries.""" |
|
1920 for referrer in gc.get_referrers(remove): |
|
1921 if (type(referrer) is dict and |
|
1922 referrer.get("__mocker_replace__", True)): |
|
1923 for key, value in referrer.items(): |
|
1924 if value is remove: |
|
1925 referrer[key] = install |
|
1926 |
|
1927 |
|
1928 class Undefined(object): |
|
1929 |
|
1930 def __repr__(self): |
|
1931 return "Undefined" |
|
1932 |
|
1933 Undefined = Undefined() |
|
1934 |
|
1935 |
|
1936 class Patcher(Task): |
|
1937 |
|
1938 def __init__(self): |
|
1939 super(Patcher, self).__init__() |
|
1940 self._monitored = {} # {kind: {id(object): object}} |
|
1941 self._patched = {} |
|
1942 |
|
1943 def is_monitoring(self, obj, kind): |
|
1944 monitored = self._monitored.get(kind) |
|
1945 if monitored: |
|
1946 if id(obj) in monitored: |
|
1947 return True |
|
1948 cls = type(obj) |
|
1949 if issubclass(cls, type): |
|
1950 cls = obj |
|
1951 bases = set([id(base) for base in cls.__mro__]) |
|
1952 bases.intersection_update(monitored) |
|
1953 return bool(bases) |
|
1954 return False |
|
1955 |
|
1956 def monitor(self, obj, kind): |
|
1957 if kind not in self._monitored: |
|
1958 self._monitored[kind] = {} |
|
1959 self._monitored[kind][id(obj)] = obj |
|
1960 |
|
1961 def patch_attr(self, obj, attr, value): |
|
1962 original = obj.__dict__.get(attr, Undefined) |
|
1963 self._patched[id(obj), attr] = obj, attr, original |
|
1964 setattr(obj, attr, value) |
|
1965 |
|
1966 def get_unpatched_attr(self, obj, attr): |
|
1967 cls = type(obj) |
|
1968 if issubclass(cls, type): |
|
1969 cls = obj |
|
1970 result = Undefined |
|
1971 for mro_cls in cls.__mro__: |
|
1972 key = (id(mro_cls), attr) |
|
1973 if key in self._patched: |
|
1974 result = self._patched[key][2] |
|
1975 if result is not Undefined: |
|
1976 break |
|
1977 elif attr in mro_cls.__dict__: |
|
1978 result = mro_cls.__dict__.get(attr, Undefined) |
|
1979 break |
|
1980 if isinstance(result, object) and hasattr(type(result), "__get__"): |
|
1981 if cls is obj: |
|
1982 obj = None |
|
1983 return result.__get__(obj, cls) |
|
1984 return result |
|
1985 |
|
1986 def _get_kind_attr(self, kind): |
|
1987 if kind == "getattr": |
|
1988 return "__getattribute__" |
|
1989 return "__%s__" % kind |
|
1990 |
|
1991 def replay(self): |
|
1992 for kind in self._monitored: |
|
1993 attr = self._get_kind_attr(kind) |
|
1994 seen = set() |
|
1995 for obj in self._monitored[kind].itervalues(): |
|
1996 cls = type(obj) |
|
1997 if issubclass(cls, type): |
|
1998 cls = obj |
|
1999 if cls not in seen: |
|
2000 seen.add(cls) |
|
2001 unpatched = getattr(cls, attr, Undefined) |
|
2002 self.patch_attr(cls, attr, |
|
2003 PatchedMethod(kind, unpatched, |
|
2004 self.is_monitoring)) |
|
2005 self.patch_attr(cls, "__mocker_execute__", |
|
2006 self.execute) |
|
2007 |
|
2008 def restore(self): |
|
2009 for obj, attr, original in self._patched.itervalues(): |
|
2010 if original is Undefined: |
|
2011 delattr(obj, attr) |
|
2012 else: |
|
2013 setattr(obj, attr, original) |
|
2014 self._patched.clear() |
|
2015 |
|
2016 def execute(self, action, object): |
|
2017 attr = self._get_kind_attr(action.kind) |
|
2018 unpatched = self.get_unpatched_attr(object, attr) |
|
2019 try: |
|
2020 return unpatched(*action.args, **action.kwargs) |
|
2021 except AttributeError: |
|
2022 if action.kind == "getattr": |
|
2023 # The normal behavior of Python is to try __getattribute__, |
|
2024 # and if it raises AttributeError, try __getattr__. We've |
|
2025 # tried the unpatched __getattribute__ above, and we'll now |
|
2026 # try __getattr__. |
|
2027 try: |
|
2028 __getattr__ = unpatched("__getattr__") |
|
2029 except AttributeError: |
|
2030 pass |
|
2031 else: |
|
2032 return __getattr__(*action.args, **action.kwargs) |
|
2033 raise |
|
2034 |
|
2035 |
|
2036 class PatchedMethod(object): |
|
2037 |
|
2038 def __init__(self, kind, unpatched, is_monitoring): |
|
2039 self._kind = kind |
|
2040 self._unpatched = unpatched |
|
2041 self._is_monitoring = is_monitoring |
|
2042 |
|
2043 def __get__(self, obj, cls=None): |
|
2044 object = obj or cls |
|
2045 if not self._is_monitoring(object, self._kind): |
|
2046 return self._unpatched.__get__(obj, cls) |
|
2047 def method(*args, **kwargs): |
|
2048 if self._kind == "getattr" and args[0].startswith("__mocker_"): |
|
2049 return self._unpatched.__get__(obj, cls)(args[0]) |
|
2050 mock = object.__mocker_mock__ |
|
2051 return mock.__mocker_act__(self._kind, args, kwargs, object) |
|
2052 return method |
|
2053 |
|
2054 def __call__(self, obj, *args, **kwargs): |
|
2055 # At least with __getattribute__, Python seems to use *both* the |
|
2056 # descriptor API and also call the class attribute directly. It |
|
2057 # looks like an interpreter bug, or at least an undocumented |
|
2058 # inconsistency. |
|
2059 return self.__get__(obj)(*args, **kwargs) |
|
2060 |
|
2061 |
|
2062 def patcher_recorder(mocker, event): |
|
2063 mock = event.path.root_mock |
|
2064 if mock.__mocker_patcher__ and len(event.path.actions) == 1: |
|
2065 patcher = mock.__mocker_patcher__ |
|
2066 patcher.monitor(mock.__mocker_object__, event.path.actions[0].kind) |
|
2067 |
|
2068 Mocker.add_recorder(patcher_recorder) |