• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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