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

Side by Side Diff: build/android/pylib/utils/mock_calls.py

Issue 723343002: Update from https://crrev.com/304121 (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 6 years, 1 month 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
« no previous file with comments | « build/android/pylib/utils/isolator.py ('k') | build/android/pylib/utils/mock_calls_test.py » ('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 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """
7 A test facility to assert call sequences while mocking their behavior.
8 """
9
10 import os
11 import sys
12 import unittest
13
14 from pylib import constants
15
16 sys.path.append(os.path.join(
17 constants.DIR_SOURCE_ROOT, 'third_party', 'pymock'))
18 import mock # pylint: disable=F0401
19
20
21 class TestCase(unittest.TestCase):
22 """Adds assertCalls to TestCase objects."""
23 class _AssertCalls(object):
24 def __init__(self, test_case, expected_calls, watched):
25 def call_action(pair):
26 if isinstance(pair, type(mock.call)):
27 return (pair, None)
28 else:
29 return pair
30
31 def do_check(call):
32 def side_effect(*args, **kwargs):
33 received_call = call(*args, **kwargs)
34 self._test_case.assertTrue(
35 self._expected_calls,
36 msg=('Unexpected call: %s' % str(received_call)))
37 expected_call, action = self._expected_calls.pop(0)
38 self._test_case.assertTrue(
39 received_call == expected_call,
40 msg=('Expected call missmatch:\n'
41 ' expected: %s\n'
42 ' received: %s\n'
43 % (str(expected_call), str(received_call))))
44 if callable(action):
45 return action(*args, **kwargs)
46 else:
47 return action
48 return side_effect
49
50 self._test_case = test_case
51 self._expected_calls = [call_action(pair) for pair in expected_calls]
52 watched = watched.copy() # do not pollute the caller's dict
53 watched.update((call.parent.name, call.parent)
54 for call, _ in self._expected_calls)
55 self._patched = [test_case.patch_call(call, side_effect=do_check(call))
56 for call in watched.itervalues()]
57
58 def __enter__(self):
59 for patch in self._patched:
60 patch.__enter__()
61 return self
62
63 def __exit__(self, exc_type, exc_val, exc_tb):
64 for patch in self._patched:
65 patch.__exit__(exc_type, exc_val, exc_tb)
66 if exc_type is None:
67 missing = ''.join(' expected: %s\n' % str(call)
68 for call, _ in self._expected_calls)
69 self._test_case.assertFalse(
70 missing,
71 msg='Expected calls not found:\n' + missing)
72
73 def __init__(self, *args, **kwargs):
74 super(TestCase, self).__init__(*args, **kwargs)
75 self.call = mock.call.self
76 self._watched = {}
77
78 def call_target(self, call):
79 """Resolve a self.call instance to the target it represents.
80
81 Args:
82 call: a self.call instance, e.g. self.call.adb.Shell
83
84 Returns:
85 The target object represented by the call, e.g. self.adb.Shell
86
87 Raises:
88 ValueError if the path of the call does not start with "self", i.e. the
89 target of the call is external to the self object.
90 AttributeError if the path of the call does not specify a valid
91 chain of attributes (without any calls) starting from "self".
92 """
93 path = call.name.split('.')
94 if path.pop(0) != 'self':
95 raise ValueError("Target %r outside of 'self' object" % call.name)
96 target = self
97 for attr in path:
98 target = getattr(target, attr)
99 return target
100
101 def patch_call(self, call, **kwargs):
102 """Patch the target of a mock.call instance.
103
104 Args:
105 call: a mock.call instance identifying a target to patch
106 Extra keyword arguments are processed by mock.patch
107
108 Returns:
109 A context manager to mock/unmock the target of the call
110 """
111 if call.name.startswith('self.'):
112 target = self.call_target(call.parent)
113 _, attribute = call.name.rsplit('.', 1)
114 return mock.patch.object(target, attribute, **kwargs)
115 else:
116 return mock.patch(call.name, **kwargs)
117
118 def watchCalls(self, calls):
119 """Add calls to the set of watched calls.
120
121 Args:
122 calls: a sequence of mock.call instances identifying targets to watch
123 """
124 self._watched.update((call.name, call) for call in calls)
125
126 def watchMethodCalls(self, call):
127 """Watch all public methods of the target identified by a self.call.
128
129 Args:
130 call: a self.call instance indetifying an object
131 """
132 target = self.call_target(call)
133 self.watchCalls(getattr(call, method)
134 for method in dir(target.__class__)
135 if not method.startswith('_'))
136
137 def clearWatched(self):
138 """Clear the set of watched calls."""
139 self._watched = {}
140
141 def assertCalls(self, *calls):
142 """A context manager to assert that a sequence of calls is made.
143
144 During the assertion, a number of functions and methods will be "watched",
145 and any calls made to them is expected to appear---in the exact same order,
146 and with the exact same arguments---as specified by the argument |calls|.
147
148 By default, the targets of all expected calls are watched. Further targets
149 to watch may be added using watchCalls and watchMethodCalls.
150
151 Optionaly, each call may be accompanied by an action. If the action is a
152 (non-callable) value, this value will be used as the return value given to
153 the caller when the matching call is found. Alternatively, if the action is
154 a callable, the action will be then called with the same arguments as the
155 intercepted call, so that it can provide a return value or perform other
156 side effects. If the action is missing, a return value of None is assumed.
157
158 Note that mock.Mock objects are often convenient to use as a callable
159 action, e.g. to raise exceptions or return other objects which are
160 themselves callable.
161
162 Args:
163 calls: each argument is either a pair (expected_call, action) or just an
164 expected_call, where expected_call is a mock.call instance.
165
166 Raises:
167 AssertionError if the watched targets do not receive the exact sequence
168 of calls specified. Missing calls, extra calls, and calls with
169 mismatching arguments, all cause the assertion to fail.
170 """
171 return self._AssertCalls(self, calls, self._watched)
172
173 def assertCall(self, call, action=None):
174 return self.assertCalls((call, action))
175
OLDNEW
« no previous file with comments | « build/android/pylib/utils/isolator.py ('k') | build/android/pylib/utils/mock_calls_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698