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,
21 class ResponseMap(object):
23 An object that encapsulates return values based on parameters given to the
26 Return Map should be initialized with a list containing tuples all possible
27 arg/kwarg combinations plus the result that should be sent for those args
29 def __init__(self
, map):
32 def __getitem__(self
, key
):
33 for k
, response
in self
.map:
38 class CallProxy(object):
39 """ Proxy for a method that will record calls to it. To use this class
40 monkey patch the original method using an instance of this class
42 setting the enabled flag can enable/disable whether the method is actually
43 executed when it is called, or just recorded.
45 def __init__(self
, func
, enabled
=True, response
=None, **kwargs
):
48 func: function to proxy
49 enabled: whether to call the wrapped function
50 kwargs: kwargs passed to all calls. may be overwritten by kwargs
55 self
.enabled
= enabled
57 self
.response
= response
61 self
.matching_function
= self
.create_matching_function(func
)
63 def assertCalled(self
, testcase
, *args
, **kwargs
):
65 Assertion function for checking if a callproxy was called
73 if args_
==args
and kwargs_
==kwargs
:
75 testcase
.fail("exact call (%s) did not occur: %s" % (f
, calls
))
79 testcase
.assert_(calls
, "%s was not called: %s" % (f
, calls
))
82 def assertNotCalled(self
, testcase
, *args
, **kwargs
):
84 Assertion function for checking if callproxy was not called
92 if args_
==args
and kwargs_
==kwargs
:
93 testcase
.fail("exact call (%s) was made: %s" % (f
, calls
))
96 testcase
.assertFalse(calls
, '%s was called' % f
)
107 def __call__ (self
, *args
, **kwargs
):
113 kwargs_
.update(self
.kwargs
)
114 kwargs_
.update(kwargs
)
115 self
.calls
.append((args
, kwargs_
))
118 response
= self
.func(*args
, **kwargs_
)
120 # call matching call, this ensures the args are checked even when
121 # the real function isn't actually called
123 # pass in the instance if it is set. if this was a bound method
124 # then it will fail without self passed.
125 if self
.func
.im_self
:
126 self
.matching_function(self
.func
.im_self
, *args
, **kwargs
)
128 self
.matching_function(*args
, **kwargs
)
130 # return mandated response. This may be a ResponseMap, so process
131 # according to what type it is.
133 if isinstance(self
.response
, (ResponseMap
,)):
134 return self
.response
[(args
, kwargs
)]
140 def create_matching_function(self
, func
):
142 constructs a function with a method signature that matches the
143 function that is passed in. The resulting function does not actually
144 do anything. It is only used for verifying arguments to the call match.
146 The function is constructed from a combination of properties from an
147 inner function and the function passed in.
151 base_code
= base
.func_code
152 code
= func
.func_code
154 name
= 'MATCHING_PROXY: %s' % func
.__name
__
156 new_code
= types
.CodeType( \
159 base_code
.co_stacksize
, \
162 base_code
.co_consts
, \
163 base_code
.co_names
, \
165 base_code
.co_filename
, \
167 base_code
.co_firstlineno
, \
170 return types
.FunctionType(new_code
, func
.func_globals
, \
171 name
, func
.func_defaults
)
174 def patch(cls
, instance
, name
, *args
, **kwargs
):
176 Helper function for patching a function on an instance. useful since
177 patching an instance requires a bit more work to ensure the proxy works
181 * instance: instance to patch
182 * name: name of function to
184 func
= getattr(instance
, name
)
185 proxy
= CallProxy(func
, *args
, **kwargs
)
186 setattr(instance
, name
, proxy
)