1#
2# Copyright (C) 2016 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17import re
18import unittest
19
20from vts.runners.host 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.
27class _ProxyTest(unittest.TestCase):
28    def runTest(self):
29        pass
30
31
32_pyunit_proxy = _ProxyTest()
33
34
35def assertEqual(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        first, second: The arguments that will be tested for equality.
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    try:
48        _pyunit_proxy.assertEqual(first, second)
49    except AssertionError as e:
50        my_msg = str(e)
51        if msg:
52            my_msg = "%s %s" % (my_msg, msg)
53        fail(my_msg, extras=extras)
54
55
56def assertNotEqual(first, second, msg=None, extras=None):
57    """Assert that the first and second args are not equal, otherwise fail
58    the test.
59
60    Error message is "first == second" by default. Additional explanation can
61    be supplied in the message.
62
63    Args:
64        first, second: The arguments that will be tested for inequality.
65        msg: A string that adds additional info about the failure.
66        extras: An optional field for extra information to be included in
67                test result.
68    """
69    try:
70        _pyunit_proxy.assertNotEqual(first, second)
71    except AssertionError as e:
72        my_msg = str(e)
73        if msg:
74            my_msg = "%s %s" % (my_msg, msg)
75        fail(my_msg, extras=extras)
76
77
78def assertRaises(expected_exception, extras=None, *args, **kwargs):
79    """Assert that an exception is raised when a function is called.
80
81    If no exception is raised, test fail. If an exception is raised but not
82    of the expected type, the exception is let through.
83
84    This should only be used as a context manager:
85        with assertRaises(Exception):
86            func()
87
88    Args:
89        expected_exception: An exception class that is expected to be
90                            raised.
91        extras: An optional field for extra information to be included in
92                test result.
93    """
94    context = _AssertRaisesContext(expected_exception, extras=extras)
95    return context
96
97
98def assertRaisesRegex(expected_exception,
99                      expected_regex,
100                      extras=None,
101                      *args,
102                      **kwargs):
103    """Assert that an exception is raised when a function is called.
104
105    If no exception is raised, test fail. If an exception is raised but not
106    of the expected type, the exception is let through. If an exception of the
107    expected type is raised but the error message does not match the
108    expected_regex, test fail.
109
110    This should only be used as a context manager:
111        with assertRaises(Exception):
112            func()
113
114    Args:
115        expected_exception: An exception class that is expected to be
116                            raised.
117        extras: An optional field for extra information to be included in
118                test result.
119    """
120    context = _AssertRaisesContext(
121        expected_exception, expected_regex, extras=extras)
122    return context
123
124
125def assertTrue(expr, msg, extras=None):
126    """Assert an expression evaluates to True, otherwise fail the test.
127
128    Args:
129        expr: The expression that is evaluated.
130        msg: A string explaining the details in case of failure.
131        extras: An optional field for extra information to be included in
132                test result.
133    """
134    if not expr:
135        fail(msg, extras)
136
137
138def assertFalse(expr, msg, extras=None):
139    """Assert an expression evaluates to False, otherwise fail the test.
140
141    Args:
142        expr: The expression that is evaluated.
143        msg: A string explaining the details in case of failure.
144        extras: An optional field for extra information to be included in
145                test result.
146    """
147    if expr:
148        fail(msg, extras)
149
150
151def assertLess(first, second, msg=None, extras=None):
152    """Assert first < second, otherwise fail the test.
153
154    Error message is "first >= second" by default. Additional explanation can
155    be supplied in the message.
156
157    Args:
158        first: The actual value which is supposed to be smaller than `second`.
159        second: The threshold.
160        msg: A string that adds additional info about the failure.
161        extras: An optional field for extra information to be included in
162                test result.
163    """
164    try:
165        _pyunit_proxy.assertLess(first, second)
166    except AssertionError as e:
167        my_msg = str(e)
168        if msg:
169            my_msg = "%s %s" % (my_msg, msg)
170        fail(my_msg, extras=extras)
171
172
173def skip(reason, extras=None):
174    """Skip a test case.
175
176    Args:
177        reason: The reason this test is skipped.
178        extras: An optional field for extra information to be included in
179                test result.
180
181    Raises:
182        signals.TestSkip is raised to mark a test case as skipped.
183    """
184    raise signals.TestSkip(reason, extras)
185
186
187def skipIf(expr, reason, extras=None):
188    """Skip a test case if expression evaluates to True.
189
190    Args:
191        expr: The expression that is evaluated.
192        reason: The reason this test is skipped.
193        extras: An optional field for extra information to be included in
194                test result.
195    """
196    if expr:
197        skip(reason, extras)
198
199
200def abortClass(reason, extras=None):
201    """Abort all subsequent test cases within the same test class in one
202    iteration.
203
204    If one test class is requested multiple times in a test run, this can
205    only abort one of the requested executions, NOT all.
206
207    Args:
208        reason: The reason to abort.
209        extras: An optional field for extra information to be included in
210                test result.
211
212    Raises:
213        signals.TestAbortClass is raised to abort all subsequent tests in a
214        test class.
215    """
216    raise signals.TestAbortClass(reason, extras)
217
218
219def abortClassIf(expr, reason, extras=None):
220    """Abort all subsequent test cases within the same test class in one
221    iteration, if expression evaluates to True.
222
223    If one test class is requested multiple times in a test run, this can
224    only abort one of the requested executions, NOT all.
225
226    Args:
227        expr: The expression that is evaluated.
228        reason: The reason to abort.
229        extras: An optional field for extra information to be included in
230                test result.
231
232    Raises:
233        signals.TestAbortClass is raised to abort all subsequent tests in a
234        test class.
235    """
236    if expr:
237        abortClass(reason, extras)
238
239
240def abortAll(reason, extras=None):
241    """Abort all subsequent test cases, including the ones not in this test
242    class or iteration.
243
244    Args:
245        reason: The reason to abort.
246        extras: An optional field for extra information to be included in
247                test result.
248
249    Raises:
250        signals.TestAbortAll is raised to abort all subsequent tests.
251    """
252    raise signals.TestAbortAll(reason, extras)
253
254
255def abortAllIf(expr, reason, extras=None):
256    """Abort all subsequent test cases, if the expression evaluates to
257    True.
258
259    Args:
260        expr: The expression that is evaluated.
261        reason: The reason to abort.
262        extras: An optional field for extra information to be included in
263                test result.
264
265    Raises:
266        signals.TestAbortAll is raised to abort all subsequent tests.
267    """
268    if expr:
269        abortAll(reason, extras)
270
271
272def fail(msg, extras=None):
273    """Explicitly fail a test case.
274
275    Args:
276        msg: A string explaining the details of the failure.
277        extras: An optional field for extra information to be included in
278                test result.
279
280    Raises:
281        signals.TestFailure is raised to mark a test case as failed.
282    """
283    raise signals.TestFailure(msg, extras)
284
285
286def explicitPass(msg, extras=None):
287    """Explicitly pass a test case.
288
289    A test with not uncaught exception will pass implicitly so the usage of
290    this is optional. It is intended for reporting extra information when a
291    test passes.
292
293    Args:
294        msg: A string explaining the details of the passed test.
295        extras: An optional field for extra information to be included in
296                test result.
297
298    Raises:
299        signals.TestPass is raised to mark a test case as passed.
300    """
301    raise signals.TestPass(msg, extras)
302
303
304class _AssertRaisesContext(object):
305    """A context manager used to implement TestCase.assertRaises* methods."""
306
307    def __init__(self, expected, expected_regexp=None, extras=None):
308        self.expected = expected
309        self.failureException = signals.TestFailure
310        self.expected_regexp = expected_regexp
311        self.extras = extras
312
313    def __enter__(self):
314        return self
315
316    def __exit__(self, exc_type, exc_value, tb):
317        if exc_type is None:
318            try:
319                exc_name = self.expected.__name__
320            except AttributeError:
321                exc_name = str(self.expected)
322            raise signals.TestFailure(
323                "{} not raised".format(exc_name), extras=self.extras)
324        if not issubclass(exc_type, self.expected):
325            # let unexpected exceptions pass through
326            return False
327        self.exception = exc_value  # store for later retrieval
328        if self.expected_regexp is None:
329            return True
330
331        expected_regexp = self.expected_regexp
332        if isinstance(expected_regexp, str):
333            expected_regexp = re.compile(expected_regexp)
334        if not expected_regexp.search(str(exc_value)):
335            raise signals.TestFailure(
336                '"%s" does not match "%s"' %
337                (expected_regexp.pattern, str(exc_value)),
338                extras=self.extras)
339        return True
340