1# Copyright (C) 2016 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from __future__ import print_function, absolute_import
16
17import functools
18import warnings
19import inspect
20
21
22class skip_conditional(object):
23    '''
24    Test method decorator that marks a test method as ignorable if the given
25    arguments evaluate as Truthy. If the argument is callable, then it is called
26    and the return value is used as the predicate.
27
28    >>> class MyTestClass(TestBase):
29    ...     def test_something(self):
30    ...         pass
31    ...
32    ...     @skip_conditional(not sys.platform.startswith("linux"))
33    ...     def test_some_linux_behaviour(self):
34    ...         assert "vmlinuz" in open("/proc/cmdline").read()
35    ...
36    ...     @skip_conditional(lambda : True):
37    ...     def test_that_never_runs(self):
38    ...         pass
39    '''
40    def __init__(self, skip_condition, message="skipped"):
41        self._skip_condition = skip_condition
42        self._message = message
43
44    def __call__(self, func):
45        @functools.wraps(func)
46        def inner(*args, **kwargs):
47            skip_condition = self._skip_condition
48            if callable(skip_condition):
49                # args[0] is ``self``
50                skip_condition = skip_condition(args[0])
51
52            if skip_condition:
53                print("skipping %r - %s" % (func, self._message))
54                return True
55            return func(args[0])
56
57        return inner
58
59
60class skip_test(skip_conditional):
61    ''''
62    Unconditionally skip a test
63    '''
64    def __init__(self, skip_condition, *args, **kwargs):
65        super(skip_test, self).__init__(True, *args, **kwargs)
66
67
68java_only_test = lambda: skip_conditional(lambda self: not self.app_type == 'java')
69
70cpp_only_test = lambda: skip_conditional(lambda self: not self.app_type == 'cpp')
71
72jni_only_test = lambda: skip_conditional(lambda self: not self.app_type == 'jni')
73
74
75def wimpy(func):
76    '''
77    Mark a test as 'wimpy' that is - a function specifically known to be quick-running.
78    This implementation simply adds the `.wimpy` attribute to the decorated function
79    and returns it, otherwise unmodified
80    '''
81    func.wimpy = True
82
83    return func
84
85
86class ordered_test(object):
87    '''Set the ordered attribute on function'''
88    def __init__(self, order):
89        self._order = order
90
91    def __call__(self, func):
92        func.test_order = self._order
93        return func
94
95
96class deprecated(object):
97    """
98    method or function decorator used to warn of pending feature removal:
99
100    >>> @deprecated()
101    ... def myfunc():
102    ...     return 'hello'
103    ...
104    >>> myfunc()
105        DeprecationWarning: `__main__.myfunc()` is deprecated and will be removed soon.
106    'hello'
107    >>> class MyClass(object):
108    ... @deprecated(alternative_feature='print')
109    ... def myprint(self, *args, **kwargs):
110    ...     print(*args, **kwargs)
111    ...
112    >>> obj = MyClass()
113    >>> obj.myprint("hello")
114    DeprecationWarning: `__main__.MyClass.myfunc()` is deprecated and will be removed soon. Use 'print' instead.
115    hello
116    """
117
118    def __init__(
119            self,
120            alternative_feature=None,
121            removal_date='soon',
122            exception=UserWarning
123        ):
124        self.alternative_feature_message = (
125            alternative_feature and 'use %r instead' % alternative_feature or ''
126        )
127        self.exception = exception
128        self.removal_date = removal_date
129
130    def __call__(self, func):
131        class_name = ''
132        if getattr(func, 'im_class', None):
133            class_name = '%s.' % func.im_class.__name__
134
135        if getattr(func, 'im_func', None):
136            func_name = func.im_func.func_name
137        else:
138            func_name = func.func_name
139
140        module_name = getattr(func, '__module__')
141
142        warning = "`%s.%s%s()` is deprecated and will be removed %s. %s" % (
143            module_name,
144            class_name,
145            func_name,
146            self.removal_date,
147            self.alternative_feature_message
148        )
149
150        @functools.wraps(func)
151        def inner(*args, **kwargs):
152            if not getattr(func, 'deprecation_warned', False):
153                warnings.warn(warning, self.exception, 2)
154                func.deprecation_warned = True
155            return func(*args, **kwargs)
156
157        return inner
158