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

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

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

Powered by Google App Engine
This is Rietveld 408576698