1from acts import signals 2 3 4class Binding(object): 5 """Creates a binding for a test method with a decorator. 6 7 Python stores all functions as a variable bound to an object. When that 8 object is called it will execute the function logic. It is possible to 9 create a wrapper object around the real function object to perform custom 10 logic and store additional meta-data. 11 12 This object acts as a wrapper for test functions. It allows binding 13 additional test logic to a test. 14 """ 15 16 def __init__(self, inner, arg_modifier=None, before=None, after=None, 17 signal_modifier=None, instance_args=None): 18 """ 19 Args: 20 inner: The inner method or other binding being bound to. 21 arg_modifier: A function of 22 (*args, **kwargs) => args kwargs that will modify the 23 arguments to pass to the bound target 24 before: A function of (*args, **kwargs) => None that will 25 be called before the bound target. 26 after: A function of (result, *args, **kwargs) => None 27 that will be called after the bound target. 28 signal_modifier: A function of 29 (signal, *args, **kwargs) => signal that will be 30 called before the signal is sent to modify the signal to send. 31 """ 32 self.instance_args = instance_args or [] 33 self.arg_modifier = arg_modifier 34 self.signal_modifier = signal_modifier 35 self.after = after 36 self.before = before 37 self.inner = inner 38 self.__name__ = inner.__name__ 39 40 def __get__(self, instance, owner): 41 """Called when a new isntance of the test class is created. 42 43 When a new instance of a class is created all method bindings must 44 be bound as instance bindings. This transforms the function call 45 signature to be func(self, *args, **kwargs) to func(*args, **kwargs). 46 The newly created binding handles inserting the self variable so the 47 caller does not have to. 48 49 This binding needs to do similar logic by creating a new binding for 50 the instance that memorizes the instance as a passed in arg. 51 """ 52 return Binding(self.inner, 53 arg_modifier=self.arg_modifier, 54 before=self.before, 55 after=self.after, 56 signal_modifier=self.signal_modifier, 57 instance_args=[instance] + self.instance_args) 58 59 def __call__(self, *args, **kwargs): 60 """Called when the test is executed.""" 61 full_args = self.instance_args + list(args) 62 63 try: 64 if self.arg_modifier: 65 full_args, kwargs = self.arg_modifier(self.inner, *full_args, 66 **kwargs) 67 68 if self.before: 69 self.before(self.inner, *full_args, **kwargs) 70 71 result = 'UNKNOWN ERROR' 72 try: 73 result = self.inner(*full_args, **kwargs) 74 finally: 75 if self.after: 76 self.after(self.inner, result, *full_args, **kwargs) 77 78 if result or result is None: 79 new_signal = signals.TestPass('') 80 else: 81 new_signal = signals.TestFailure('') 82 except signals.TestSignal as signal: 83 new_signal = signal 84 85 if self.signal_modifier: 86 new_signal = self.signal_modifier(self.inner, new_signal, 87 *full_args, 88 **kwargs) 89 90 raise new_signal 91 92 def __getattr__(self, item): 93 """A simple pass through for any variable we do not known about.""" 94 return getattr(self.inner, item) 95