Home | History | Annotate | Download | only in acts
      1 #!/usr/bin/env python3.4
      2 #
      3 # Copyright 2016 - The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #     http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 import re
     18 import unittest
     19 
     20 from acts import signals
     21 
     22 
     23 # Have an instance of unittest.TestCase so we could reuse some logic from
     24 # python's own unittest.
     25 # _ProxyTest is required because py2 does not allow instantiating
     26 # unittest.TestCase directly.
     27 class _ProxyTest(unittest.TestCase):
     28     def runTest(self):
     29         pass
     30 
     31 
     32 _pyunit_proxy = _ProxyTest()
     33 
     34 
     35 def assert_equal(first, second, msg=None, extras=None):
     36     """Assert an expression evaluates to true, otherwise fail the test.
     37 
     38     Error message is "first != second" by default. Additional explanation can
     39     be supplied in the message.
     40 
     41     Args:
     42         expr: The expression that is evaluated.
     43         msg: A string that adds additional info about the failure.
     44         extras: An optional field for extra information to be included in
     45                 test result.
     46     """
     47     my_msg = None
     48     try:
     49         _pyunit_proxy.assertEqual(first, second)
     50     except Exception as e:
     51         # We have to catch all here for py2/py3 compatibility.
     52         # In py2, assertEqual throws exceptions.AssertionError, which does not
     53         # exist in py3. In py3, it throws unittest.case.failureException, which
     54         # does not exist in py2. To accommodate using explicit catch complicates
     55         # the code like hell, so I opted to catch all instead.
     56         my_msg = str(e)
     57         if msg:
     58             my_msg = "%s %s" % (my_msg, msg)
     59     # This is a hack to remove the stacktrace produced by the above exception.
     60     if my_msg is not None:
     61         fail(my_msg, extras=extras)
     62 
     63 
     64 def assert_almost_equal(first,
     65                         second,
     66                         places=7,
     67                         msg=None,
     68                         delta=None,
     69                         extras=None):
     70     """
     71     Assert FIRST to be within +/- DELTA to SECOND, otherwise fail the
     72     test.
     73     :param first: The first argument, LHS
     74     :param second: The second argument, RHS
     75     :param places: For floating points, how many decimal places to look into
     76     :param msg: Message to display on failure
     77     :param delta: The +/- first and second could be apart from each other
     78     :param extras: Extra object passed to test failure handler
     79     :return:
     80     """
     81     my_msg = None
     82     try:
     83         if delta:
     84             _pyunit_proxy.assertAlmostEqual(
     85                 first, second, msg=msg, delta=delta)
     86         else:
     87             _pyunit_proxy.assertAlmostEqual(
     88                 first, second, places=places, msg=msg)
     89     except Exception as e:
     90         my_msg = str(e)
     91         if msg:
     92             my_msg = "%s %s" % (my_msg, msg)
     93     # This is a hack to remove the stacktrace produced by the above exception.
     94     if my_msg is not None:
     95         fail(my_msg, extras=extras)
     96 
     97 
     98 def assert_raises(expected_exception, extras=None, *args, **kwargs):
     99     """Assert that an exception is raised when a function is called.
    100 
    101     If no exception is raised, test fail. If an exception is raised but not
    102     of the expected type, the exception is let through.
    103 
    104     This should only be used as a context manager:
    105         with assert_raises(Exception):
    106             func()
    107 
    108     Args:
    109         expected_exception: An exception class that is expected to be
    110                             raised.
    111         extras: An optional field for extra information to be included in
    112                 test result.
    113     """
    114     context = _AssertRaisesContext(expected_exception, extras=extras)
    115     return context
    116 
    117 
    118 def assert_raises_regex(expected_exception,
    119                         expected_regex,
    120                         extras=None,
    121                         *args,
    122                         **kwargs):
    123     """Assert that an exception is raised when a function is called.
    124 
    125     If no exception is raised, test fail. If an exception is raised but not
    126     of the expected type, the exception is let through. If an exception of the
    127     expected type is raised but the error message does not match the
    128     expected_regex, test fail.
    129 
    130     This should only be used as a context manager:
    131         with assert_raises(Exception):
    132             func()
    133 
    134     Args:
    135         expected_exception: An exception class that is expected to be
    136                             raised.
    137         extras: An optional field for extra information to be included in
    138                 test result.
    139     """
    140     context = _AssertRaisesContext(
    141         expected_exception, expected_regex, extras=extras)
    142     return context
    143 
    144 
    145 def assert_true(expr, msg, extras=None):
    146     """Assert an expression evaluates to true, otherwise fail the test.
    147 
    148     Args:
    149         expr: The expression that is evaluated.
    150         msg: A string explaining the details in case of failure.
    151         extras: An optional field for extra information to be included in
    152                 test result.
    153     """
    154     if not expr:
    155         fail(msg, extras)
    156 
    157 
    158 def assert_false(expr, msg, extras=None):
    159     """Assert an expression evaluates to false, otherwise fail the test.
    160 
    161     Args:
    162         expr: The expression that is evaluated.
    163         msg: A string explaining the details in case of failure.
    164         extras: An optional field for extra information to be included in
    165                 test result.
    166     """
    167     if expr:
    168         fail(msg, extras)
    169 
    170 
    171 def skip(reason, extras=None):
    172     """Skip a test case.
    173 
    174     Args:
    175         reason: The reason this test is skipped.
    176         extras: An optional field for extra information to be included in
    177                 test result.
    178 
    179     Raises:
    180         signals.TestSkip is raised to mark a test case as skipped.
    181     """
    182     raise signals.TestSkip(reason, extras)
    183 
    184 
    185 def skip_if(expr, reason, extras=None):
    186     """Skip a test case if expression evaluates to True.
    187 
    188     Args:
    189         expr: The expression that is evaluated.
    190         reason: The reason this test is skipped.
    191         extras: An optional field for extra information to be included in
    192                 test result.
    193     """
    194     if expr:
    195         skip(reason, extras)
    196 
    197 
    198 def abort_class(reason, extras=None):
    199     """Abort all subsequent test cases within the same test class in one
    200     iteration.
    201 
    202     If one test class is requested multiple times in a test run, this can
    203     only abort one of the requested executions, NOT all.
    204 
    205     Args:
    206         reason: The reason to abort.
    207         extras: An optional field for extra information to be included in
    208                 test result.
    209 
    210     Raises:
    211         signals.TestAbortClass is raised to abort all subsequent tests in a
    212         test class.
    213     """
    214     raise signals.TestAbortClass(reason, extras)
    215 
    216 
    217 def abort_class_if(expr, reason, extras=None):
    218     """Abort all subsequent test cases within the same test class in one
    219     iteration, if expression evaluates to True.
    220 
    221     If one test class is requested multiple times in a test run, this can
    222     only abort one of the requested executions, NOT all.
    223 
    224     Args:
    225         expr: The expression that is evaluated.
    226         reason: The reason to abort.
    227         extras: An optional field for extra information to be included in
    228                 test result.
    229 
    230     Raises:
    231         signals.TestAbortClass is raised to abort all subsequent tests in a
    232         test class.
    233     """
    234     if expr:
    235         abort_class(reason, extras)
    236 
    237 
    238 def abort_all(reason, extras=None):
    239     """Abort all subsequent test cases, including the ones not in this test
    240     class or iteration.
    241 
    242     Args:
    243         reason: The reason to abort.
    244         extras: An optional field for extra information to be included in
    245                 test result.
    246 
    247     Raises:
    248         signals.TestAbortAll is raised to abort all subsequent tests.
    249     """
    250     raise signals.TestAbortAll(reason, extras)
    251 
    252 
    253 def abort_all_if(expr, reason, extras=None):
    254     """Abort all subsequent test cases, if the expression evaluates to
    255     True.
    256 
    257     Args:
    258         expr: The expression that is evaluated.
    259         reason: The reason to abort.
    260         extras: An optional field for extra information to be included in
    261                 test result.
    262 
    263     Raises:
    264         signals.TestAbortAll is raised to abort all subsequent tests.
    265     """
    266     if expr:
    267         abort_all(reason, extras)
    268 
    269 
    270 def fail(msg, extras=None):
    271     """Explicitly fail a test case.
    272 
    273     Args:
    274         msg: A string explaining the details of the failure.
    275         extras: An optional field for extra information to be included in
    276                 test result.
    277 
    278     Raises:
    279         signals.TestFailure is raised to mark a test case as failed.
    280     """
    281     raise signals.TestFailure(msg, extras)
    282 
    283 
    284 def explicit_pass(msg, extras=None):
    285     """Explicitly pass a test case.
    286 
    287     A test with not uncaught exception will pass implicitly so the usage of
    288     this is optional. It is intended for reporting extra information when a
    289     test passes.
    290 
    291     Args:
    292         msg: A string explaining the details of the passed test.
    293         extras: An optional field for extra information to be included in
    294                 test result.
    295 
    296     Raises:
    297         signals.TestPass is raised to mark a test case as passed.
    298     """
    299     raise signals.TestPass(msg, extras)
    300 
    301 
    302 class _AssertRaisesContext(object):
    303     """A context manager used to implement TestCase.assertRaises* methods."""
    304 
    305     def __init__(self, expected, expected_regexp=None, extras=None):
    306         self.expected = expected
    307         self.failureException = signals.TestFailure
    308         self.expected_regexp = expected_regexp
    309         self.extras = extras
    310 
    311     def __enter__(self):
    312         return self
    313 
    314     def __exit__(self, exc_type, exc_value, tb):
    315         if exc_type is None:
    316             try:
    317                 exc_name = self.expected.__name__
    318             except AttributeError:
    319                 exc_name = str(self.expected)
    320             raise signals.TestFailure(
    321                 "{} not raised".format(exc_name), extras=self.extras)
    322         if not issubclass(exc_type, self.expected):
    323             # let unexpected exceptions pass through
    324             return False
    325         self.exception = exc_value  # store for later retrieval
    326         if self.expected_regexp is None:
    327             return True
    328 
    329         expected_regexp = self.expected_regexp
    330         if isinstance(expected_regexp, str):
    331             expected_regexp = re.compile(expected_regexp)
    332         if not expected_regexp.search(str(exc_value)):
    333             raise signals.TestFailure(
    334                 '"%s" does not match "%s"' %
    335                 (expected_regexp.pattern, str(exc_value)),
    336                 extras=self.extras)
    337         return True
    338