| # Copyright 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| from __future__ import absolute_import |
| import unittest |
| |
| from expect_tests.type_definitions import ( |
| Test, Result, MultiTest, FuncCall, Bind) |
| |
| from expect_tests.util import covers |
| |
| |
| def _SetUpClass(test_class): |
| inst = test_class('__init__') |
| inst.setUpClass() |
| return inst |
| |
| |
| def _TearDownClass(test_class_inst): |
| test_class_inst.tearDownClass() |
| |
| |
| def _RunTestCaseSingle(test_case, test_name, test_instance=None): |
| # The hack is so that unittest.TestCase has something to pretend is the |
| # test method without the BS of wrapping each method in a new TestCase |
| # class... |
| test_instance = test_instance or test_case('__init__') |
| test_method = getattr(test_instance, test_name) |
| |
| # Check if the test is supposed to be skipped. |
| if (getattr(test_instance.__class__, '__unittest_skip__', False) or |
| getattr(test_method, '__unittest__skip__', False)): |
| skip_why = (getattr(test_instance.__class__, '__unittest_skip_why__', '') or |
| getattr(test_method, '__unittest_skip_why__', '')) |
| raise unittest.SkipTest(skip_why) |
| |
| test_instance.setUp() |
| test_instance._test_failed_with_exception = False |
| try: |
| return Result(test_method()) |
| except KeyboardInterrupt: |
| raise |
| except: |
| test_instance._test_failed_with_exception = True |
| raise |
| finally: |
| test_instance.tearDown() |
| |
| |
| def UnittestTestCase(test_case, verbose): |
| """Yield a MultiTest or multiple Test instances for the unittest.TestCase |
| derived |test_case|. |
| |
| If the TestCase has a field `__expect_tests_serial__` defined to be True, then |
| all test methods in the TestCase will be guaranteed to run in a single process |
| with the same instance. This is automatically set to True if your test class |
| relies on setUpClass/tearDownClass. |
| |
| If the TestCase has a field `__expect_tests_atomic__` defined to be True, then |
| in the event of a test filter which matches any test method in |test_case|, |
| the ENTIRE |test_case| will be executed (i.e. the TestCase has interdependant |
| test methods). This should only need to be set for very poorly designed tests. |
| |
| `__expect_tests_atomic__` implies `__expect_tests_serial__`. |
| |
| @type test_case: unittest.TestCase |
| """ |
| if verbose: |
| # Set default maxDiff to no limit if verbosity is requested. |
| test_case.maxDiff = None |
| @covers(lambda: Test.covers_obj(test_case)) |
| def _inner(): |
| name_prefix = '.'.join((test_case.__module__, test_case.__name__)) |
| |
| def _tests_from_class(cls, *args, **kwargs): |
| for test_name in unittest.defaultTestLoader.getTestCaseNames(cls): |
| yield Test( |
| name_prefix + '.' + test_name, |
| FuncCall(_RunTestCaseSingle, cls, test_name, *args, **kwargs), |
| expect_dir=Test.expect_dir_obj(cls), |
| expect_base=cls.__name__ + '.' + test_name, |
| break_funcs=[getattr(cls, test_name)], |
| covers=Test.covers_obj(cls) |
| ) |
| |
| if hasattr(test_case, '__expect_tests_serial__'): |
| serial = getattr(test_case, '__expect_tests_serial__', False) |
| else: |
| default_setup = unittest.TestCase.setUpClass.__func__ |
| default_teardown = unittest.TestCase.tearDownClass.__func__ |
| serial = ( |
| test_case.setUpClass.__func__ is not default_setup or |
| test_case.tearDownClass.__func__ is not default_teardown) |
| |
| atomic = getattr(test_case, '__expect_tests_atomic__', False) |
| if atomic or serial: |
| yield MultiTest( |
| name_prefix, |
| FuncCall(_SetUpClass, test_case), |
| FuncCall(_TearDownClass, Bind(name='context')), |
| list(_tests_from_class(test_case, |
| test_instance=Bind(name='context'))), |
| atomic |
| ) |
| else: |
| for test in _tests_from_class(test_case): |
| yield test |
| return _inner |
| |
| |
| def _is_unittest(obj): |
| return isinstance(obj, type) and issubclass(obj, unittest.TestCase) |
| |
| |
| def UnitTestModule(test_module): |
| """Yield MultiTest's and/or Test's for the python module |test_module| which |
| contains zero or more unittest.TestCase implementations. |
| |
| @type test_module: types.ModuleType |
| """ |
| for name in dir(test_module): |
| obj = getattr(test_module, name) |
| if _is_unittest(obj): |
| for test in UnittestTestCase(obj)(): |
| yield test |
| # TODO(iannucci): Make this compatible with the awful load_tests hack? |