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 5""" 6Unit tests for decorators.py. 7""" 8 9# pylint: disable=W0613 10 11import time 12import traceback 13import unittest 14 15from devil.android import decorators 16from devil.android import device_errors 17from devil.utils import reraiser_thread 18 19_DEFAULT_TIMEOUT = 30 20_DEFAULT_RETRIES = 3 21 22 23class DecoratorsTest(unittest.TestCase): 24 _decorated_function_called_count = 0 25 26 def testFunctionDecoratorDoesTimeouts(self): 27 """Tests that the base decorator handles the timeout logic.""" 28 DecoratorsTest._decorated_function_called_count = 0 29 30 @decorators.WithTimeoutAndRetries 31 def alwaysTimesOut(timeout=None, retries=None): 32 DecoratorsTest._decorated_function_called_count += 1 33 time.sleep(100) 34 35 start_time = time.time() 36 with self.assertRaises(device_errors.CommandTimeoutError): 37 alwaysTimesOut(timeout=1, retries=0) 38 elapsed_time = time.time() - start_time 39 self.assertTrue(elapsed_time >= 1) 40 self.assertEquals(1, DecoratorsTest._decorated_function_called_count) 41 42 def testFunctionDecoratorDoesRetries(self): 43 """Tests that the base decorator handles the retries logic.""" 44 DecoratorsTest._decorated_function_called_count = 0 45 46 @decorators.WithTimeoutAndRetries 47 def alwaysRaisesCommandFailedError(timeout=None, retries=None): 48 DecoratorsTest._decorated_function_called_count += 1 49 raise device_errors.CommandFailedError('testCommand failed') 50 51 with self.assertRaises(device_errors.CommandFailedError): 52 alwaysRaisesCommandFailedError(timeout=30, retries=10) 53 self.assertEquals(11, DecoratorsTest._decorated_function_called_count) 54 55 def testFunctionDecoratorRequiresParams(self): 56 """Tests that the base decorator requires timeout and retries params.""" 57 @decorators.WithTimeoutAndRetries 58 def requiresExplicitTimeoutAndRetries(timeout=None, retries=None): 59 return (timeout, retries) 60 61 with self.assertRaises(KeyError): 62 requiresExplicitTimeoutAndRetries() 63 with self.assertRaises(KeyError): 64 requiresExplicitTimeoutAndRetries(timeout=10) 65 with self.assertRaises(KeyError): 66 requiresExplicitTimeoutAndRetries(retries=0) 67 expected_timeout = 10 68 expected_retries = 1 69 (actual_timeout, actual_retries) = ( 70 requiresExplicitTimeoutAndRetries(timeout=expected_timeout, 71 retries=expected_retries)) 72 self.assertEquals(expected_timeout, actual_timeout) 73 self.assertEquals(expected_retries, actual_retries) 74 75 def testFunctionDecoratorTranslatesReraiserExceptions(self): 76 """Tests that the explicit decorator translates reraiser exceptions.""" 77 @decorators.WithTimeoutAndRetries 78 def alwaysRaisesProvidedException(exception, timeout=None, retries=None): 79 raise exception 80 81 exception_desc = 'Reraiser thread timeout error' 82 with self.assertRaises(device_errors.CommandTimeoutError) as e: 83 alwaysRaisesProvidedException( 84 reraiser_thread.TimeoutError(exception_desc), 85 timeout=10, retries=1) 86 self.assertEquals(exception_desc, str(e.exception)) 87 88 def testConditionalRetriesDecoratorRetries(self): 89 def do_not_retry_no_adb_error(exc): 90 return not isinstance(exc, device_errors.NoAdbError) 91 92 actual_tries = [0] 93 94 @decorators.WithTimeoutAndConditionalRetries(do_not_retry_no_adb_error) 95 def alwaysRaisesCommandFailedError(timeout=None, retries=None): 96 actual_tries[0] += 1 97 raise device_errors.CommandFailedError('Command failed :(') 98 99 with self.assertRaises(device_errors.CommandFailedError): 100 alwaysRaisesCommandFailedError(timeout=10, retries=10) 101 self.assertEquals(11, actual_tries[0]) 102 103 def testConditionalRetriesDecoratorDoesntRetry(self): 104 def do_not_retry_no_adb_error(exc): 105 return not isinstance(exc, device_errors.NoAdbError) 106 107 actual_tries = [0] 108 109 @decorators.WithTimeoutAndConditionalRetries(do_not_retry_no_adb_error) 110 def alwaysRaisesNoAdbError(timeout=None, retries=None): 111 actual_tries[0] += 1 112 raise device_errors.NoAdbError() 113 114 with self.assertRaises(device_errors.NoAdbError): 115 alwaysRaisesNoAdbError(timeout=10, retries=10) 116 self.assertEquals(1, actual_tries[0]) 117 118 def testDefaultsFunctionDecoratorDoesTimeouts(self): 119 """Tests that the defaults decorator handles timeout logic.""" 120 DecoratorsTest._decorated_function_called_count = 0 121 122 @decorators.WithTimeoutAndRetriesDefaults(1, 0) 123 def alwaysTimesOut(timeout=None, retries=None): 124 DecoratorsTest._decorated_function_called_count += 1 125 time.sleep(100) 126 127 start_time = time.time() 128 with self.assertRaises(device_errors.CommandTimeoutError): 129 alwaysTimesOut() 130 elapsed_time = time.time() - start_time 131 self.assertTrue(elapsed_time >= 1) 132 self.assertEquals(1, DecoratorsTest._decorated_function_called_count) 133 134 DecoratorsTest._decorated_function_called_count = 0 135 with self.assertRaises(device_errors.CommandTimeoutError): 136 alwaysTimesOut(timeout=2) 137 elapsed_time = time.time() - start_time 138 self.assertTrue(elapsed_time >= 2) 139 self.assertEquals(1, DecoratorsTest._decorated_function_called_count) 140 141 def testDefaultsFunctionDecoratorDoesRetries(self): 142 """Tests that the defaults decorator handles retries logic.""" 143 DecoratorsTest._decorated_function_called_count = 0 144 145 @decorators.WithTimeoutAndRetriesDefaults(30, 10) 146 def alwaysRaisesCommandFailedError(timeout=None, retries=None): 147 DecoratorsTest._decorated_function_called_count += 1 148 raise device_errors.CommandFailedError('testCommand failed') 149 150 with self.assertRaises(device_errors.CommandFailedError): 151 alwaysRaisesCommandFailedError() 152 self.assertEquals(11, DecoratorsTest._decorated_function_called_count) 153 154 DecoratorsTest._decorated_function_called_count = 0 155 with self.assertRaises(device_errors.CommandFailedError): 156 alwaysRaisesCommandFailedError(retries=5) 157 self.assertEquals(6, DecoratorsTest._decorated_function_called_count) 158 159 def testDefaultsFunctionDecoratorPassesValues(self): 160 """Tests that the defaults decorator passes timeout and retries kwargs.""" 161 @decorators.WithTimeoutAndRetriesDefaults(30, 10) 162 def alwaysReturnsTimeouts(timeout=None, retries=None): 163 return timeout 164 165 self.assertEquals(30, alwaysReturnsTimeouts()) 166 self.assertEquals(120, alwaysReturnsTimeouts(timeout=120)) 167 168 @decorators.WithTimeoutAndRetriesDefaults(30, 10) 169 def alwaysReturnsRetries(timeout=None, retries=None): 170 return retries 171 172 self.assertEquals(10, alwaysReturnsRetries()) 173 self.assertEquals(1, alwaysReturnsRetries(retries=1)) 174 175 def testDefaultsFunctionDecoratorTranslatesReraiserExceptions(self): 176 """Tests that the explicit decorator translates reraiser exceptions.""" 177 @decorators.WithTimeoutAndRetriesDefaults(30, 10) 178 def alwaysRaisesProvidedException(exception, timeout=None, retries=None): 179 raise exception 180 181 exception_desc = 'Reraiser thread timeout error' 182 with self.assertRaises(device_errors.CommandTimeoutError) as e: 183 alwaysRaisesProvidedException( 184 reraiser_thread.TimeoutError(exception_desc)) 185 self.assertEquals(exception_desc, str(e.exception)) 186 187 def testExplicitFunctionDecoratorDoesTimeouts(self): 188 """Tests that the explicit decorator handles timeout logic.""" 189 DecoratorsTest._decorated_function_called_count = 0 190 191 @decorators.WithExplicitTimeoutAndRetries(1, 0) 192 def alwaysTimesOut(): 193 DecoratorsTest._decorated_function_called_count += 1 194 time.sleep(100) 195 196 start_time = time.time() 197 with self.assertRaises(device_errors.CommandTimeoutError): 198 alwaysTimesOut() 199 elapsed_time = time.time() - start_time 200 self.assertTrue(elapsed_time >= 1) 201 self.assertEquals(1, DecoratorsTest._decorated_function_called_count) 202 203 def testExplicitFunctionDecoratorDoesRetries(self): 204 """Tests that the explicit decorator handles retries logic.""" 205 DecoratorsTest._decorated_function_called_count = 0 206 207 @decorators.WithExplicitTimeoutAndRetries(30, 10) 208 def alwaysRaisesCommandFailedError(): 209 DecoratorsTest._decorated_function_called_count += 1 210 raise device_errors.CommandFailedError('testCommand failed') 211 212 with self.assertRaises(device_errors.CommandFailedError): 213 alwaysRaisesCommandFailedError() 214 self.assertEquals(11, DecoratorsTest._decorated_function_called_count) 215 216 def testExplicitDecoratorTranslatesReraiserExceptions(self): 217 """Tests that the explicit decorator translates reraiser exceptions.""" 218 @decorators.WithExplicitTimeoutAndRetries(30, 10) 219 def alwaysRaisesProvidedException(exception): 220 raise exception 221 222 exception_desc = 'Reraiser thread timeout error' 223 with self.assertRaises(device_errors.CommandTimeoutError) as e: 224 alwaysRaisesProvidedException( 225 reraiser_thread.TimeoutError(exception_desc)) 226 self.assertEquals(exception_desc, str(e.exception)) 227 228 class _MethodDecoratorTestObject(object): 229 """An object suitable for testing the method decorator.""" 230 231 def __init__(self, test_case, default_timeout=_DEFAULT_TIMEOUT, 232 default_retries=_DEFAULT_RETRIES): 233 self._test_case = test_case 234 self.default_timeout = default_timeout 235 self.default_retries = default_retries 236 self.function_call_counters = { 237 'alwaysRaisesCommandFailedError': 0, 238 'alwaysTimesOut': 0, 239 'requiresExplicitTimeoutAndRetries': 0, 240 } 241 242 @decorators.WithTimeoutAndRetriesFromInstance( 243 'default_timeout', 'default_retries') 244 def alwaysTimesOut(self, timeout=None, retries=None): 245 self.function_call_counters['alwaysTimesOut'] += 1 246 time.sleep(100) 247 self._test_case.assertFalse(True, msg='Failed to time out?') 248 249 @decorators.WithTimeoutAndRetriesFromInstance( 250 'default_timeout', 'default_retries') 251 def alwaysRaisesCommandFailedError(self, timeout=None, retries=None): 252 self.function_call_counters['alwaysRaisesCommandFailedError'] += 1 253 raise device_errors.CommandFailedError('testCommand failed') 254 255 # pylint: disable=no-self-use 256 257 @decorators.WithTimeoutAndRetriesFromInstance( 258 'default_timeout', 'default_retries') 259 def alwaysReturnsTimeout(self, timeout=None, retries=None): 260 return timeout 261 262 @decorators.WithTimeoutAndRetriesFromInstance( 263 'default_timeout', 'default_retries', min_default_timeout=100) 264 def alwaysReturnsTimeoutWithMin(self, timeout=None, retries=None): 265 return timeout 266 267 @decorators.WithTimeoutAndRetriesFromInstance( 268 'default_timeout', 'default_retries') 269 def alwaysReturnsRetries(self, timeout=None, retries=None): 270 return retries 271 272 @decorators.WithTimeoutAndRetriesFromInstance( 273 'default_timeout', 'default_retries') 274 def alwaysRaisesProvidedException(self, exception, timeout=None, 275 retries=None): 276 raise exception 277 278 # pylint: enable=no-self-use 279 280 def testMethodDecoratorDoesTimeout(self): 281 """Tests that the method decorator handles timeout logic.""" 282 test_obj = self._MethodDecoratorTestObject(self) 283 start_time = time.time() 284 with self.assertRaises(device_errors.CommandTimeoutError): 285 try: 286 test_obj.alwaysTimesOut(timeout=1, retries=0) 287 except: 288 traceback.print_exc() 289 raise 290 elapsed_time = time.time() - start_time 291 self.assertTrue(elapsed_time >= 1) 292 self.assertEquals(1, test_obj.function_call_counters['alwaysTimesOut']) 293 294 def testMethodDecoratorDoesRetries(self): 295 """Tests that the method decorator handles retries logic.""" 296 test_obj = self._MethodDecoratorTestObject(self) 297 with self.assertRaises(device_errors.CommandFailedError): 298 try: 299 test_obj.alwaysRaisesCommandFailedError(retries=10) 300 except: 301 traceback.print_exc() 302 raise 303 self.assertEquals( 304 11, test_obj.function_call_counters['alwaysRaisesCommandFailedError']) 305 306 def testMethodDecoratorPassesValues(self): 307 """Tests that the method decorator passes timeout and retries kwargs.""" 308 test_obj = self._MethodDecoratorTestObject( 309 self, default_timeout=42, default_retries=31) 310 self.assertEquals(42, test_obj.alwaysReturnsTimeout()) 311 self.assertEquals(41, test_obj.alwaysReturnsTimeout(timeout=41)) 312 self.assertEquals(31, test_obj.alwaysReturnsRetries()) 313 self.assertEquals(32, test_obj.alwaysReturnsRetries(retries=32)) 314 315 def testMethodDecoratorUsesMiniumumTimeout(self): 316 test_obj = self._MethodDecoratorTestObject( 317 self, default_timeout=42, default_retries=31) 318 self.assertEquals(100, test_obj.alwaysReturnsTimeoutWithMin()) 319 self.assertEquals(41, test_obj.alwaysReturnsTimeoutWithMin(timeout=41)) 320 321 def testMethodDecoratorTranslatesReraiserExceptions(self): 322 test_obj = self._MethodDecoratorTestObject(self) 323 324 exception_desc = 'Reraiser thread timeout error' 325 with self.assertRaises(device_errors.CommandTimeoutError) as e: 326 test_obj.alwaysRaisesProvidedException( 327 reraiser_thread.TimeoutError(exception_desc)) 328 self.assertEquals(exception_desc, str(e.exception)) 329 330if __name__ == '__main__': 331 unittest.main(verbosity=2) 332 333