1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4# pylint: disable=W0212 5 6import functools 7import inspect 8import types 9 10 11def Cache(obj): 12 """Decorator for caching read-only properties. 13 14 Example usage (always returns the same Foo instance): 15 @Cache 16 def CreateFoo(): 17 return Foo() 18 19 If CreateFoo() accepts parameters, a separate cached value is maintained 20 for each unique parameter combination. 21 22 Cached methods maintain their cache for the lifetime of the /instance/, while 23 cached functions maintain their cache for the lifetime of the /module/. 24 """ 25 @functools.wraps(obj) 26 def Cacher(*args, **kwargs): 27 cacher = args[0] if inspect.getargspec(obj).args[:1] == ['self'] else obj 28 cacher.__cache = cacher.__cache if hasattr(cacher, '__cache') else {} 29 key = str(obj) + str(args) + str(kwargs) 30 if key not in cacher.__cache: 31 cacher.__cache[key] = obj(*args, **kwargs) 32 return cacher.__cache[key] 33 return Cacher 34 35 36def Disabled(*args): 37 """Decorator for disabling tests/benchmarks. 38 39 May be used without args to unconditionally disable: 40 @Disabled # Unconditionally disabled. 41 42 If args are given, the test will be disabled if ANY of the args match the 43 browser type, OS name or OS version: 44 @Disabled('canary') # Disabled for canary browsers 45 @Disabled('win') # Disabled on Windows. 46 @Disabled('win', 'linux') # Disabled on both Windows and Linux. 47 @Disabled('mavericks') # Disabled on Mac Mavericks (10.9) only. 48 """ 49 def _Disabled(func): 50 if not isinstance(func, types.FunctionType): 51 func._disabled_strings = disabled_strings 52 return func 53 @functools.wraps(func) 54 def wrapper(*args, **kwargs): 55 func(*args, **kwargs) 56 wrapper._disabled_strings = disabled_strings 57 return wrapper 58 if len(args) == 1 and callable(args[0]): 59 disabled_strings = [] 60 return _Disabled(args[0]) 61 disabled_strings = list(args) 62 for disabled_string in disabled_strings: 63 # TODO(tonyg): Validate that these strings are recognized. 64 assert isinstance(disabled_string, str), '@Disabled accepts a list of strs' 65 return _Disabled 66 67 68def Enabled(*args): 69 """Decorator for enabling tests/benchmarks. 70 71 The test will be enabled if ANY of the args match the browser type, OS name 72 or OS version: 73 @Enabled('canary') # Enabled only for canary browsers 74 @Enabled('win') # Enabled only on Windows. 75 @Enabled('win', 'linux') # Enabled only on Windows or Linux. 76 @Enabled('mavericks') # Enabled only on Mac Mavericks (10.9). 77 """ 78 def _Enabled(func): 79 if not isinstance(func, types.FunctionType): 80 func._enabled_strings = enabled_strings 81 return func 82 @functools.wraps(func) 83 def wrapper(*args, **kwargs): 84 func(*args, **kwargs) 85 wrapper._enabled_strings = enabled_strings 86 return wrapper 87 assert args and not callable(args[0]), '@Enabled requires argumentas' 88 enabled_strings = list(args) 89 for enabled_string in enabled_strings: 90 # TODO(tonyg): Validate that these strings are recognized. 91 assert isinstance(enabled_string, str), '@Enabled accepts a list of strs' 92 return _Enabled 93 94 95def IsEnabled(test, possible_browser): 96 """Returns True iff |test| is enabled given the |possible_browser|. 97 98 Use to respect the @Enabled / @Disabled decorators. 99 100 Args: 101 test: A function or class that may contain _disabled_strings and/or 102 _enabled_strings attributes. 103 possible_browser: A PossibleBrowser to check whether |test| may run against. 104 """ 105 platform_attributes = [a.lower() for a in [ 106 possible_browser.browser_type, 107 possible_browser.platform.GetOSName(), 108 possible_browser.platform.GetOSVersionName(), 109 ]] 110 if possible_browser.supports_tab_control: 111 platform_attributes.append('has tabs') 112 113 if hasattr(test, '__name__'): 114 name = test.__name__ 115 elif hasattr(test, '__class__'): 116 name = test.__class__.__name__ 117 else: 118 name = str(test) 119 120 if hasattr(test, '_disabled_strings'): 121 disabled_strings = test._disabled_strings 122 if not disabled_strings: 123 return False # No arguments to @Disabled means always disable. 124 for disabled_string in disabled_strings: 125 if disabled_string in platform_attributes: 126 print ( 127 'Skipping %s because it is disabled for %s. ' 128 'You are running %s.' % (name, 129 ' and '.join(disabled_strings), 130 ' '.join(platform_attributes))) 131 return False 132 133 if hasattr(test, '_enabled_strings'): 134 enabled_strings = test._enabled_strings 135 if not enabled_strings: 136 return True # No arguments to @Enabled means always enable. 137 for enabled_string in enabled_strings: 138 if enabled_string in platform_attributes: 139 return True 140 print ( 141 'Skipping %s because it is only enabled for %s. ' 142 'You are running %s.' % (name, 143 ' or '.join(enabled_strings), 144 ' '.join(platform_attributes))) 145 return False 146 147 return True 148