diff --git a/tobiko/__init__.py b/tobiko/__init__.py index 1387b098f..b39b11beb 100644 --- a/tobiko/__init__.py +++ b/tobiko/__init__.py @@ -20,7 +20,6 @@ from tobiko.common import _detail from tobiko.common import _exception from tobiko.common import _fixture from tobiko.common import _logging -from tobiko.common.managers import testcase as testcase_manager from tobiko.common.managers import loader as loader_manager from tobiko.common import _operation from tobiko.common import _os @@ -64,7 +63,6 @@ list_required_fixtures = _fixture.list_required_fixtures SharedFixture = _fixture.SharedFixture FixtureManager = _fixture.FixtureManager -CaptureLogTest = _logging.CaptureLogTest CaptureLogFixture = _logging.CaptureLogFixture load_object = loader_manager.load_object @@ -83,8 +81,6 @@ get_operation = _operation.get_operation get_operation_name = _operation.get_operation_name operation_config = _operation.operation_config -discover_testcases = testcase_manager.discover_testcases - Selection = _select.Selection select = _select.select ObjectNotFound = _select.ObjectNotFound @@ -95,10 +91,12 @@ skip = _skip.skip skip_if = _skip.skip_if skip_unless = _skip.skip_unless +BaseTestCase = _testcase.BaseTestCase +discover_test_cases = _testcase.discover_test_cases get_test_case = _testcase.get_test_case pop_test_case = _testcase.pop_test_case push_test_case = _testcase.push_test_case -TobikoTestCase = _testcase.TobikoTestCase +TestCasesManager = _testcase.TestCasesManager get_short_hostname = _utils.get_short_hostname diff --git a/tobiko/cmd/fixture.py b/tobiko/cmd/fixture.py index 54eab7a8a..4de566a79 100644 --- a/tobiko/cmd/fixture.py +++ b/tobiko/cmd/fixture.py @@ -114,7 +114,7 @@ class FixtureUtil(base.TobikoCMD): def discover_testcases(self): args = self.args - return tobiko.discover_testcases( + return tobiko.discover_test_cases( config=args.config, repo_type=args.repo_type, repo_url=args.repo_url, diff --git a/tobiko/common/_logging.py b/tobiko/common/_logging.py index 971cfe179..93953897f 100644 --- a/tobiko/common/_logging.py +++ b/tobiko/common/_logging.py @@ -20,7 +20,6 @@ from testtools import content from tobiko.common import _detail from tobiko.common import _fixture -from tobiko.common import _testcase LOG = log.getLogger(__name__) @@ -78,15 +77,3 @@ class CaptureLogHandler(logging.Handler): def format_all(self): for record in self.records: yield self.format(record) + '\n' - - -class CaptureLogTest(_testcase.TobikoTestCase): - - capture_log_level = logging.DEBUG - capture_log_logger = logging.root - - def setUp(self): - self.useFixture(CaptureLogFixture(test_case_id=self.id(), - level=self.capture_log_level, - logger=self.capture_log_logger)) - super(CaptureLogTest, self).setUp() diff --git a/tobiko/common/_testcase.py b/tobiko/common/_testcase.py index 2b74ae794..c50276c25 100644 --- a/tobiko/common/_testcase.py +++ b/tobiko/common/_testcase.py @@ -13,10 +13,119 @@ # under the License. from __future__ import absolute_import +import logging +import os +import sys +from oslo_log import log +from stestr import config_file import testtools from tobiko.common import _exception +from tobiko.common import _logging + + +LOG = log.getLogger(__name__) + +os.environ.setdefault('PYTHON', sys.executable) + + +class TestCasesFinder(object): + + def __init__(self, config=None, repo_type=None, repo_url=None, + test_path=None, top_dir=None, group_regex=None, + blacklist_file=None, whitelist_file=None, black_regex=None, + filters=None): + """ + :param str config: The path to the stestr config file. Must be a + string. + :param str repo_type: This is the type of repository to use. Valid + choices are 'file' and 'sql'. + :param str repo_url: The url of the repository to use. + :param str test_path: Set the test path to use for unittest discovery. + If both this and the corresponding config file option are set, this + value will be used. + :param str top_dir: The top dir to use for unittest discovery. This + takes precedence over the value in the config file. (if one is + present in the config file) + :param str group_regex: Set a group regex to use for grouping tests + together in the stestr scheduler. If both this and the + corresponding config file option are set this value will be used. + :param str blacklist_file: Path to a blacklist file, this file contains + a separate regex exclude on each newline. + :param str whitelist_file: Path to a whitelist file, this file contains + a separate regex on each newline. + :param str black_regex: Test rejection regex. If a test cases name + matches on re.search() operation, it will be removed from the final + test list. + :param list filters: A list of string regex filters to initially apply + on the test list. Tests that match any of the regexes will be used. + (assuming any other filtering specified also uses it) + """ + + self.config = config or '.stestr.conf' + self.repo_type = repo_type or 'file' + self.repo_url = repo_url + self.test_path = test_path + self.top_dir = top_dir + self.group_regex = group_regex + self.blacklist_file = blacklist_file + self.whitelist_file = whitelist_file + self.black_regex = black_regex + self.filters = filters + + def discover_test_cases(self, **kwargs): + """Iterate over test_ids for a project + This method will print the test_ids for tests in a project. You can + filter the output just like with the run command to see exactly what + will be run. + """ + params = dict(config=self.config, repo_type=self.repo_type, + repo_url=self.repo_url, test_path=self.test_path, + top_dir=self.top_dir, group_regex=self.group_regex, + blacklist_file=self.blacklist_file, + whitelist_file=self.whitelist_file, + black_regex=self.black_regex, filters=self.filters) + if kwargs: + params.update(kwargs) + ids = None + config = params.pop('config') + conf = config_file.TestrConf(config) + filters = params.pop('filters') + blacklist_file = params.pop('blacklist_file') + whitelist_file = params.pop('whitelist_file') + black_regex = params.pop('black_regex') + cmd = conf.get_run_command( + regexes=filters, repo_type=params['repo_type'], + repo_url=params['repo_url'], group_regex=params['group_regex'], + blacklist_file=blacklist_file, whitelist_file=whitelist_file, + black_regex=black_regex, test_path=params['test_path'], + top_dir=params['top_dir']) + not_filtered = filters is None and blacklist_file is None\ + and whitelist_file is None and black_regex is None + + try: + cmd.setUp() + # List tests if the fixture has not already needed to to filter. + if not_filtered: + ids = cmd.list_tests() + else: + ids = cmd.test_ids + except SystemExit: + msg = ("Error discovering test cases IDs with parameters: " + "{!r}").format(params) + raise RuntimeError(msg) + finally: + cmd.cleanUp() + + return sorted(ids) + + +FINDER = TestCasesFinder() + + +def discover_test_cases(finder=FINDER, **kwargs): + return finder.discover_test_cases(**kwargs) class TestCasesManager(object): @@ -25,7 +134,10 @@ class TestCasesManager(object): self._test_cases = [] def get_test_case(self) -> testtools.TestCase: - return self._test_cases[-1] + try: + return self._test_cases[-1] + except IndexError: + return DUMMY_TEST_CASE def pop_test_case(self) -> testtools.TestCase: return self._test_cases.pop() @@ -50,11 +162,29 @@ def get_test_case(manager=TEST_CASES): return manager.get_test_case() -class TobikoTestCase(testtools.TestCase): +class BaseTestCase(testtools.TestCase): + + _capture_log = False + _capture_log_level = logging.DEBUG + _capture_log_logger = logging.root + + @classmethod + def setUpClass(cls): + super(BaseTestCase, cls).setUpClass() + from tobiko import config + cls._capture_log = config.CONF.tobiko.logging.capture_log def setUp(self): + super(BaseTestCase, self).setUp() self._push_test_case() - super(TobikoTestCase, self).setUp() + self._setup_capture_log() + + def _setup_capture_log(self): + if self._capture_log: + self.useFixture(_logging.CaptureLogFixture( + test_case_id=self.id(), + level=self._capture_log_level, + logger=self._capture_log_logger)) def _push_test_case(self): push_test_case(self) @@ -62,3 +192,12 @@ class TobikoTestCase(testtools.TestCase): def _pop_test_case(self): self.assertIs(self, pop_test_case()) + + +class DummyTestCase(BaseTestCase): + + def runTest(self): + pass + + +DUMMY_TEST_CASE = DummyTestCase() diff --git a/tobiko/common/managers/testcase.py b/tobiko/common/managers/testcase.py deleted file mode 100644 index 12cafde48..000000000 --- a/tobiko/common/managers/testcase.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright 2018 Red Hat -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from __future__ import absolute_import - -import os -import sys - -from oslo_log import log -from stestr import config_file - -LOG = log.getLogger(__name__) - -os.environ.setdefault('PYTHON', sys.executable) - - -def discover_testcases(manager=None, **kwargs): - manager = manager or TESTCASES - return manager.discover(**kwargs) - - -class TestCaseManager(object): - - def __init__(self, config=None, repo_type=None, repo_url=None, - test_path=None, top_dir=None, group_regex=None, - blacklist_file=None, whitelist_file=None, black_regex=None, - filters=None): - """ - :param str config: The path to the stestr config file. Must be a - string. - :param str repo_type: This is the type of repository to use. Valid - choices are 'file' and 'sql'. - :param str repo_url: The url of the repository to use. - :param str test_path: Set the test path to use for unittest discovery. - If both this and the corresponding config file option are set, this - value will be used. - :param str top_dir: The top dir to use for unittest discovery. This - takes precedence over the value in the config file. (if one is - present in the config file) - :param str group_regex: Set a group regex to use for grouping tests - together in the stestr scheduler. If both this and the - corresponding config file option are set this value will be used. - :param str blacklist_file: Path to a blacklist file, this file contains - a separate regex exclude on each newline. - :param str whitelist_file: Path to a whitelist file, this file contains - a separate regex on each newline. - :param str black_regex: Test rejection regex. If a test cases name - matches on re.search() operation, it will be removed from the final - test list. - :param list filters: A list of string regex filters to initially apply - on the test list. Tests that match any of the regexes will be used. - (assuming any other filtering specified also uses it) - """ - - self.config = config or '.stestr.conf' - self.repo_type = repo_type or 'file' - self.repo_url = repo_url - self.test_path = test_path - self.top_dir = top_dir - self.group_regex = group_regex - self.blacklist_file = blacklist_file - self.whitelist_file = whitelist_file - self.black_regex = black_regex - self.filters = filters - - def discover(self, **kwargs): - """Iterate over test_ids for a project - This method will print the test_ids for tests in a project. You can - filter the output just like with the run command to see exactly what - will be run. - """ - params = dict(config=self.config, repo_type=self.repo_type, - repo_url=self.repo_url, test_path=self.test_path, - top_dir=self.top_dir, group_regex=self.group_regex, - blacklist_file=self.blacklist_file, - whitelist_file=self.whitelist_file, - black_regex=self.black_regex, filters=self.filters) - if kwargs: - params.update(kwargs) - ids = None - config = params.pop('config') - conf = config_file.TestrConf(config) - filters = params.pop('filters') - blacklist_file = params.pop('blacklist_file') - whitelist_file = params.pop('whitelist_file') - black_regex = params.pop('black_regex') - cmd = conf.get_run_command( - regexes=filters, repo_type=params['repo_type'], - repo_url=params['repo_url'], group_regex=params['group_regex'], - blacklist_file=blacklist_file, whitelist_file=whitelist_file, - black_regex=black_regex, test_path=params['test_path'], - top_dir=params['top_dir']) - not_filtered = filters is None and blacklist_file is None\ - and whitelist_file is None and black_regex is None - - try: - cmd.setUp() - # List tests if the fixture has not already needed to to filter. - if not_filtered: - ids = cmd.list_tests() - else: - ids = cmd.test_ids - except SystemExit: - msg = ("Error discovering test cases IDs with parameters: " - "{!r}").format(params) - raise RuntimeError(msg) - finally: - cmd.cleanUp() - - return sorted(ids) - - -TESTCASES = TestCaseManager() diff --git a/tobiko/config.py b/tobiko/config.py index a6bf90442..3f0a577ef 100644 --- a/tobiko/config.py +++ b/tobiko/config.py @@ -226,10 +226,7 @@ def setup_tobiko_config(conf): warnings_logger.logger.setLevel(log.ERROR) tobiko.setup_fixture(HttpProxyFixture) - if conf.logging.capture_log: - monkey.patch(testtools, 'TestCase', tobiko.CaptureLogTest) - else: - monkey.patch(testtools, 'TestCase', tobiko.TobikoTestCase) + monkey.patch(testtools, 'TestCase', tobiko.BaseTestCase) for module_name in CONFIG_MODULES: module = importlib.import_module(module_name) diff --git a/tobiko/tests/unit/test_testcase.py b/tobiko/tests/unit/test_testcase.py index 3bfc48394..c287ceb5d 100644 --- a/tobiko/tests/unit/test_testcase.py +++ b/tobiko/tests/unit/test_testcase.py @@ -15,16 +15,18 @@ from __future__ import absolute_import import os +import testtools + import tobiko from tobiko.tests import unit -class TestCasesManagerTest(unit.TobikoUnitTest): +class DiscoverTestCasesTest(unit.TobikoUnitTest): test_path = os.path.dirname(__file__) def setUp(self): - super(TestCasesManagerTest, self).setUp() + super(DiscoverTestCasesTest, self).setUp() top_dir = os.path.abspath(self.test_path) while os.path.isdir(top_dir) and top_dir != os.path.sep: @@ -42,8 +44,59 @@ class TestCasesManagerTest(unit.TobikoUnitTest): self.addCleanup(os.chdir, original_work_dir) def test_discover_testcases(self): - testcases = tobiko.discover_testcases(test_path=self.test_path, - top_dir=self.top_dir, - repo_url=self.repo_url, - filters=[self.id()]) + testcases = tobiko.discover_test_cases(test_path=self.test_path, + top_dir=self.top_dir, + repo_url=self.repo_url, + filters=[self.id()]) self.assertIn(self.id(), testcases) + + +class TestCaseTest(unit.TobikoUnitTest): + + def setUp(self): + super(TestCaseTest, self).setUp() + self.addCleanup(self._pop_inner_test_cases) + + def _pop_inner_test_cases(self): + case = tobiko.get_test_case() + while case is not self: + tobiko.pop_test_case() + case = tobiko.get_test_case() + + def test_get_test_case(self): + result = tobiko.get_test_case() + self.assertIs(self, result) + + def test_get_test_case_out_of_context(self): + manager = tobiko.TestCasesManager() + result = tobiko.get_test_case(manager=manager) + self.assertIsInstance(result, tobiko.BaseTestCase) + self.assertEqual('tobiko.common._testcase.DummyTestCase.runTest', + result.id()) + + def test_push_test_case(self): + + class InnerTest(testtools.TestCase): + + def runTest(self): + pass + + inner_case = InnerTest() + + tobiko.push_test_case(inner_case) + self.assertIs(inner_case, tobiko.get_test_case()) + + def test_pop_test_case(self): + + class InnerTest(testtools.TestCase): + + def runTest(self): + pass + + inner_case = InnerTest() + tobiko.push_test_case(inner_case) + + result = tobiko.pop_test_case() + + self.assertIs(inner_case, result) + self.assertIs(self, tobiko.get_test_case()) diff --git a/tobiko/tripleo/overcloud.py b/tobiko/tripleo/overcloud.py index e373771f1..1838d791b 100644 --- a/tobiko/tripleo/overcloud.py +++ b/tobiko/tripleo/overcloud.py @@ -46,8 +46,12 @@ skip_if_missing_overcloud = tobiko.skip_unless( class OvercloudKeystoneCredentialsFixture( keystone.EnvironKeystoneCredentialsFixture): + def get_environ(self): - return load_overcloud_rcfile() + if has_overcloud(): + return load_overcloud_rcfile() + else: + return {} def list_overcloud_nodes(**params): @@ -161,9 +165,8 @@ class OvercloudHostConfig(tobiko.SharedFixture): def setup_overcloud_keystone_crederntials(): - if has_overcloud(): - keystone.DEFAULT_KEYSTONE_CREDENTIALS_FIXTURES.append( - OvercloudKeystoneCredentialsFixture) + keystone.DEFAULT_KEYSTONE_CREDENTIALS_FIXTURES.append( + OvercloudKeystoneCredentialsFixture) def get_overcloud_nodes_dataframe(oc_node_df_function):