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