1# Copyright 2016 Google Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import re 16import unittest 17 18from mobly import signals 19 20# Have an instance of unittest.TestCase so we could reuse some logic 21# from python's own unittest. 22_pyunit_proxy = unittest.TestCase() 23_pyunit_proxy.maxDiff = None 24 25 26def _call_unittest_assertion(assertion_method, 27 *args, 28 msg=None, 29 extras=None, 30 **kwargs): 31 """Wrapper for converting a unittest assertion into a Mobly one. 32 33 Args: 34 assertion_method: unittest.TestCase assertion method to call. 35 *args: Positional arguments for the assertion call. 36 msg: A string that adds additional info about the failure. 37 extras: An optional field for extra information to be included in 38 test result. 39 **kwargs: Keyword arguments for the assertion call. 40 """ 41 my_msg = None 42 try: 43 assertion_method(*args, **kwargs) 44 except AssertionError as e: 45 my_msg = str(e) 46 if msg: 47 my_msg = f'{my_msg} {msg}' 48 49 # This raise statement is outside of the above except statement to 50 # prevent Python3's exception message from having two tracebacks. 51 if my_msg is not None: 52 raise signals.TestFailure(my_msg, extras=extras) 53 54 55def assert_equal(first, second, msg=None, extras=None): 56 """Asserts the equality of objects, otherwise fail the test. 57 58 Error message is "first != second" by default. Additional explanation can 59 be supplied in the message. 60 61 Args: 62 first: The first object to compare. 63 second: The second object to compare. 64 msg: A string that adds additional info about the failure. 65 extras: An optional field for extra information to be included in 66 test result. 67 """ 68 _call_unittest_assertion(_pyunit_proxy.assertEqual, 69 first, 70 second, 71 msg=msg, 72 extras=extras) 73 74 75def assert_not_equal(first, second, msg=None, extras=None): 76 """Asserts that first is not equal (!=) to second.""" 77 _call_unittest_assertion(_pyunit_proxy.assertNotEqual, 78 first, 79 second, 80 msg=msg, 81 extras=extras) 82 83 84def assert_almost_equal(first, 85 second, 86 places=None, 87 msg=None, 88 delta=None, 89 extras=None): 90 """Asserts that first is almost equal to second. 91 92 Fails if the two objects are unequal as determined by their difference 93 rounded to the given number of decimal places (default 7) and 94 comparing to zero, or by comparing that the difference between the two 95 objects is more than the given delta. 96 If the two objects compare equal then they automatically compare 97 almost equal. 98 99 Args: 100 first: The first value to compare. 101 second: The second value to compare. 102 places: How many decimal places to take into account for comparison. 103 Note that decimal places (from zero) are usually not the same 104 as significant digits (measured from the most significant digit). 105 msg: A string that adds additional info about the failure. 106 delta: Delta to use for comparison instead of decimal places. 107 extras: An optional field for extra information to be included in 108 test result. 109 """ 110 _call_unittest_assertion(_pyunit_proxy.assertAlmostEqual, 111 first, 112 second, 113 places=places, 114 msg=msg, 115 delta=delta, 116 extras=extras) 117 118 119def assert_not_almost_equal(first, 120 second, 121 places=None, 122 msg=None, 123 delta=None, 124 extras=None): 125 """Asserts that first is not almost equal to second. 126 127 Args: 128 first: The first value to compare. 129 second: The second value to compare. 130 places: How many decimal places to take into account for comparison. 131 Note that decimal places (from zero) are usually not the same 132 as significant digits (measured from the most significant digit). 133 msg: A string that adds additional info about the failure. 134 delta: Delta to use for comparison instead of decimal places. 135 extras: An optional field for extra information to be included in 136 test result. 137 """ 138 _call_unittest_assertion(_pyunit_proxy.assertNotAlmostEqual, 139 first, 140 second, 141 places=places, 142 msg=msg, 143 delta=delta, 144 extras=extras) 145 146 147def assert_in(member, container, msg=None, extras=None): 148 """Asserts that member is in container.""" 149 _call_unittest_assertion(_pyunit_proxy.assertIn, 150 member, 151 container, 152 msg=msg, 153 extras=extras) 154 155 156def assert_not_in(member, container, msg=None, extras=None): 157 """Asserts that member is not in container.""" 158 _call_unittest_assertion(_pyunit_proxy.assertNotIn, 159 member, 160 container, 161 msg=msg, 162 extras=extras) 163 164 165def assert_is(expr1, expr2, msg=None, extras=None): 166 """Asserts that expr1 is expr2.""" 167 _call_unittest_assertion(_pyunit_proxy.assertIs, 168 expr1, 169 expr2, 170 msg=msg, 171 extras=extras) 172 173 174def assert_is_not(expr1, expr2, msg=None, extras=None): 175 """Asserts that expr1 is not expr2.""" 176 _call_unittest_assertion(_pyunit_proxy.assertIsNot, 177 expr1, 178 expr2, 179 msg=msg, 180 extras=extras) 181 182 183def assert_count_equal(first, second, msg=None, extras=None): 184 """Asserts that two iterables have the same element count. 185 186 Element order does not matter. 187 Similar to assert_equal(Counter(list(first)), Counter(list(second))). 188 189 Args: 190 first: The first iterable to compare. 191 second: The second iterable to compare. 192 msg: A string that adds additional info about the failure. 193 extras: An optional field for extra information to be included in 194 test result. 195 196 Example: 197 assert_count_equal([0, 1, 1], [1, 0, 1]) passes the assertion. 198 assert_count_equal([0, 0, 1], [0, 1]) raises an assertion error. 199 """ 200 _call_unittest_assertion(_pyunit_proxy.assertCountEqual, 201 first, 202 second, 203 msg=msg, 204 extras=extras) 205 206 207def assert_less(a, b, msg=None, extras=None): 208 """Asserts that a < b.""" 209 _call_unittest_assertion(_pyunit_proxy.assertLess, 210 a, 211 b, 212 msg=msg, 213 extras=extras) 214 215 216def assert_less_equal(a, b, msg=None, extras=None): 217 """Asserts that a <= b.""" 218 _call_unittest_assertion(_pyunit_proxy.assertLessEqual, 219 a, 220 b, 221 msg=msg, 222 extras=extras) 223 224 225def assert_greater(a, b, msg=None, extras=None): 226 """Asserts that a > b.""" 227 _call_unittest_assertion(_pyunit_proxy.assertGreater, 228 a, 229 b, 230 msg=msg, 231 extras=extras) 232 233 234def assert_greater_equal(a, b, msg=None, extras=None): 235 """Asserts that a >= b.""" 236 _call_unittest_assertion(_pyunit_proxy.assertGreaterEqual, 237 a, 238 b, 239 msg=msg, 240 extras=extras) 241 242 243def assert_is_none(obj, msg=None, extras=None): 244 """Asserts that obj is None.""" 245 _call_unittest_assertion(_pyunit_proxy.assertIsNone, 246 obj, 247 msg=msg, 248 extras=extras) 249 250 251def assert_is_not_none(obj, msg=None, extras=None): 252 """Asserts that obj is not None.""" 253 _call_unittest_assertion(_pyunit_proxy.assertIsNotNone, 254 obj, 255 msg=msg, 256 extras=extras) 257 258 259def assert_is_instance(obj, cls, msg=None, extras=None): 260 """Asserts that obj is an instance of cls.""" 261 _call_unittest_assertion(_pyunit_proxy.assertIsInstance, 262 obj, 263 cls, 264 msg=msg, 265 extras=extras) 266 267 268def assert_not_is_instance(obj, cls, msg=None, extras=None): 269 """Asserts that obj is not an instance of cls.""" 270 _call_unittest_assertion(_pyunit_proxy.assertNotIsInstance, 271 obj, 272 cls, 273 msg=msg, 274 extras=extras) 275 276 277def assert_regex(text, expected_regex, msg=None, extras=None): 278 """Fails the test unless the text matches the regular expression.""" 279 _call_unittest_assertion(_pyunit_proxy.assertRegex, 280 text, 281 expected_regex, 282 msg=msg, 283 extras=extras) 284 285 286def assert_not_regex(text, unexpected_regex, msg=None, extras=None): 287 """Fails the test if the text matches the regular expression.""" 288 _call_unittest_assertion(_pyunit_proxy.assertNotRegex, 289 text, 290 unexpected_regex, 291 msg=msg, 292 extras=extras) 293 294 295def assert_raises(expected_exception, extras=None, *args, **kwargs): 296 """Assert that an exception is raised when a function is called. 297 298 If no exception is raised, test fail. If an exception is raised but not 299 of the expected type, the exception is let through. 300 301 This should only be used as a context manager: 302 with assert_raises(Exception): 303 func() 304 305 Args: 306 expected_exception: An exception class that is expected to be 307 raised. 308 extras: An optional field for extra information to be included in 309 test result. 310 """ 311 context = _AssertRaisesContext(expected_exception, extras=extras) 312 return context 313 314 315def assert_raises_regex(expected_exception, 316 expected_regex, 317 extras=None, 318 *args, 319 **kwargs): 320 """Assert that an exception is raised when a function is called. 321 322 If no exception is raised, test fail. If an exception is raised but not 323 of the expected type, the exception is let through. If an exception of the 324 expected type is raised but the error message does not match the 325 expected_regex, test fail. 326 327 This should only be used as a context manager: 328 with assert_raises(Exception): 329 func() 330 331 Args: 332 expected_exception: An exception class that is expected to be 333 raised. 334 extras: An optional field for extra information to be included in 335 test result. 336 """ 337 context = _AssertRaisesContext(expected_exception, 338 expected_regex, 339 extras=extras) 340 return context 341 342 343def assert_true(expr, msg, extras=None): 344 """Assert an expression evaluates to true, otherwise fail the test. 345 346 Args: 347 expr: The expression that is evaluated. 348 msg: A string explaining the details in case of failure. 349 extras: An optional field for extra information to be included in 350 test result. 351 """ 352 if not expr: 353 fail(msg, extras) 354 355 356def assert_false(expr, msg, extras=None): 357 """Assert an expression evaluates to false, otherwise fail the test. 358 359 Args: 360 expr: The expression that is evaluated. 361 msg: A string explaining the details in case of failure. 362 extras: An optional field for extra information to be included in 363 test result. 364 """ 365 if expr: 366 fail(msg, extras) 367 368 369def skip(reason, extras=None): 370 """Skip a test. 371 372 Args: 373 reason: The reason this test is skipped. 374 extras: An optional field for extra information to be included in 375 test result. 376 377 Raises: 378 signals.TestSkip: Mark a test as skipped. 379 """ 380 raise signals.TestSkip(reason, extras) 381 382 383def skip_if(expr, reason, extras=None): 384 """Skip a test if expression evaluates to True. 385 386 Args: 387 expr: The expression that is evaluated. 388 reason: The reason this test is skipped. 389 extras: An optional field for extra information to be included in 390 test result. 391 """ 392 if expr: 393 skip(reason, extras) 394 395 396def abort_class(reason, extras=None): 397 """Abort all subsequent tests within the same test class in one iteration. 398 399 If one test class is requested multiple times in a test run, this can 400 only abort one of the requested executions, NOT all. 401 402 Args: 403 reason: The reason to abort. 404 extras: An optional field for extra information to be included in 405 test result. 406 407 Raises: 408 signals.TestAbortClass: Abort all subsequent tests in a test class. 409 """ 410 raise signals.TestAbortClass(reason, extras) 411 412 413def abort_class_if(expr, reason, extras=None): 414 """Abort all subsequent tests within the same test class in one iteration, 415 if expression evaluates to True. 416 417 If one test class is requested multiple times in a test run, this can 418 only abort one of the requested executions, NOT all. 419 420 Args: 421 expr: The expression that is evaluated. 422 reason: The reason to abort. 423 extras: An optional field for extra information to be included in 424 test result. 425 426 Raises: 427 signals.TestAbortClass: Abort all subsequent tests in a test class. 428 """ 429 if expr: 430 abort_class(reason, extras) 431 432 433def abort_all(reason, extras=None): 434 """Abort all subsequent tests, including the ones not in this test class or 435 iteration. 436 437 Args: 438 reason: The reason to abort. 439 extras: An optional field for extra information to be included in 440 test result. 441 442 Raises: 443 signals.TestAbortAll: Abort all subsequent tests. 444 """ 445 raise signals.TestAbortAll(reason, extras) 446 447 448def abort_all_if(expr, reason, extras=None): 449 """Abort all subsequent tests, if the expression evaluates to True. 450 451 Args: 452 expr: The expression that is evaluated. 453 reason: The reason to abort. 454 extras: An optional field for extra information to be included in 455 test result. 456 457 Raises: 458 signals.TestAbortAll: Abort all subsequent tests. 459 """ 460 if expr: 461 abort_all(reason, extras) 462 463 464def fail(msg, extras=None): 465 """Explicitly fail a test. 466 467 Args: 468 msg: A string explaining the details of the failure. 469 extras: An optional field for extra information to be included in 470 test result. 471 472 Raises: 473 signals.TestFailure: Mark a test as failed. 474 """ 475 raise signals.TestFailure(msg, extras) 476 477 478def explicit_pass(msg, extras=None): 479 """Explicitly pass a test. 480 481 This will pass the test explicitly regardless of any other error happened 482 in the test body. E.g. even if errors have been recorded with `expects`, 483 the test will still be marked pass if this is called. 484 485 A test without uncaught exception will pass implicitly so this should be 486 used scarcely. 487 488 Args: 489 msg: A string explaining the details of the passed test. 490 extras: An optional field for extra information to be included in 491 test result. 492 493 Raises: 494 signals.TestPass: Mark a test as passed. 495 """ 496 raise signals.TestPass(msg, extras) 497 498 499class _AssertRaisesContext: 500 """A context manager used to implement TestCase.assertRaises* methods.""" 501 502 def __init__(self, expected, expected_regexp=None, extras=None): 503 self.expected = expected 504 self.failureException = signals.TestFailure 505 self.expected_regexp = expected_regexp 506 self.extras = extras 507 508 def __enter__(self): 509 return self 510 511 def __exit__(self, exc_type, exc_value, tb): 512 if exc_type is None: 513 try: 514 exc_name = self.expected.__name__ 515 except AttributeError: 516 exc_name = str(self.expected) 517 raise signals.TestFailure('%s not raised' % exc_name, extras=self.extras) 518 if not issubclass(exc_type, self.expected): 519 # let unexpected exceptions pass through 520 return False 521 self.exception = exc_value # store for later retrieval 522 if self.expected_regexp is None: 523 return True 524 525 expected_regexp = self.expected_regexp 526 if isinstance(expected_regexp, str): 527 expected_regexp = re.compile(expected_regexp) 528 if not expected_regexp.search(str(exc_value)): 529 raise signals.TestFailure('"%s" does not match "%s"' % 530 (expected_regexp.pattern, str(exc_value)), 531 extras=self.extras) 532 return True 533