Home | History | Annotate | Download | only in android
      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 """
      6 Unit tests for decorators.py.
      7 """
      8 
      9 # pylint: disable=W0613
     10 
     11 import time
     12 import traceback
     13 import unittest
     14 
     15 from devil.android import decorators
     16 from devil.android import device_errors
     17 from devil.utils import reraiser_thread
     18 
     19 _DEFAULT_TIMEOUT = 30
     20 _DEFAULT_RETRIES = 3
     21 
     22 
     23 class 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 
    330 if __name__ == '__main__':
    331   unittest.main(verbosity=2)
    332 
    333