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

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

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