1 # Copyright (C) 2010 Oregon State University et al.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 from response_map
import ResponseMap
23 class CallProxy(object):
24 """ Proxy for a method that will record calls to it. To use this class
25 monkey patch the original method using an instance of this class
27 setting the enabled flag can enable/disable whether the method is actually
28 executed when it is called, or just recorded.
30 def __init__(self
, func
, enabled
=True, response
=None, **kwargs
):
33 func: function to proxy
34 enabled: whether to call the wrapped function
35 kwargs: kwargs passed to all calls. may be overwritten by kwargs
40 self
.enabled
= enabled
42 self
.response
= response
46 self
.matching_function
= self
.create_matching_function(func
)
48 def assertCalled(self
, testcase
, *args
, **kwargs
):
50 Assertion function for checking if a callproxy was called
58 if args_
== args
and kwargs_
== kwargs
:
60 testcase
.fail("exact call (%s) did not occur: %s" % (f
, calls
))
64 testcase
.assert_(calls
, "%s was not called: %s" % (f
, calls
))
67 def assertNotCalled(self
, testcase
, *args
, **kwargs
):
69 Assertion function for checking if callproxy was not called
77 if args_
== args
and kwargs_
== kwargs
:
78 testcase
.fail("exact call (%s) was made: %s" % (f
, calls
))
81 testcase
.assertFalse(calls
, '%s was called' % f
)
92 def __call__(self
, *args
, **kwargs
):
98 kwargs_
.update(self
.kwargs
)
99 kwargs_
.update(kwargs
)
100 self
.calls
.append((args
, kwargs_
))
103 response
= self
.func(*args
, **kwargs_
)
105 # call matching call, this ensures the args are checked even when
106 # the real function isn't actually called
108 # pass in the instance if it is set. if this was a bound method
109 # then it will fail without self passed.
110 if self
.func
.im_self
:
111 self
.matching_function(self
.func
.im_self
, *args
, **kwargs
)
113 self
.matching_function(*args
, **kwargs
)
115 # return mandated response. This may be a ResponseMap, so process
116 # according to what type it is.
118 if isinstance(self
.response
, (ResponseMap
,)):
119 return self
.response
[(args
, kwargs
)]
124 def create_matching_function(self
, func
):
126 constructs a function with a method signature that matches the
127 function that is passed in. The resulting function does not actually
128 do anything. It is only used for verifying arguments
131 The function is constructed from a combination of properties from an
132 inner function and the function passed in.
137 base_code
= base
.func_code
138 code
= func
.func_code
140 name
= 'MATCHING_PROXY: %s' % func
.__name
__
142 new_code
= types
.CodeType(
145 base_code
.co_stacksize
,
151 base_code
.co_filename
,
153 base_code
.co_firstlineno
,
156 return types
.FunctionType(new_code
, func
.func_globals
,
157 name
, func
.func_defaults
)
160 def patch(cls
, instance
, name
, *args
, **kwargs
):
162 Helper function for patching a function on an instance. useful since
163 patching an instance requires a bit more work to ensure the proxy works
167 * instance: instance to patch
168 * name: name of function to
170 func
= getattr(instance
, name
)
171 proxy
= CallProxy(func
, *args
, **kwargs
)
172 setattr(instance
, name
, proxy
)