From c8f437860559efde4992b87fbc1d5ebf038127d9 Mon Sep 17 00:00:00 2001 From: jyejare Date: Wed, 13 Dec 2023 19:18:56 +0530 Subject: [PATCH] Configurable Markers ignore list --- betelgeuse/__init__.py | 2 +- betelgeuse/collector.py | 25 +++++++++++++++---------- betelgeuse/parser.py | 7 ++++--- tests/test_parser.py | 11 +++++++---- tests/test_source_generator.py | 23 ++++++++++++++++++++++- 5 files changed, 49 insertions(+), 19 deletions(-) diff --git a/betelgeuse/__init__.py b/betelgeuse/__init__.py index 30121e7..dac5ecc 100644 --- a/betelgeuse/__init__.py +++ b/betelgeuse/__init__.py @@ -656,7 +656,7 @@ def test_case( testcases.append(properties) source_testcases = itertools.chain(*collector.collect_tests( - source_code_path, collect_ignore_path).values()) + source_code_path, collect_ignore_path, config=config).values()) for testcase in source_testcases: testcases.append( create_xml_testcase(config, testcase, automation_script_format)) diff --git a/betelgeuse/collector.py b/betelgeuse/collector.py index 00511f9..48b0e8e 100644 --- a/betelgeuse/collector.py +++ b/betelgeuse/collector.py @@ -22,7 +22,10 @@ def __init__(self, title, fields=None): class TestFunction(object): """Wrapper for ``ast.FunctionDef`` which parse docstring information.""" - def __init__(self, function_def, parent_class=None, testmodule=None): + def __init__( + self, function_def, parent_class=None, testmodule=None, + config=None + ): """``ast.FunctionDef`` instance used to extract information.""" #: The unparsed testcase docstring self.docstring = ast.get_docstring(function_def) @@ -89,19 +92,19 @@ def __init__(self, function_def, parent_class=None, testmodule=None): for decorator in self.parent_class_def.decorator_list ] self._parse_docstring() - self._parse_markers() + self._parse_markers(config) self.junit_id = self._generate_junit_id() if 'id' not in self.fields: self.fields['id'] = self.junit_id - def _parse_markers(self): + def _parse_markers(self, config=None): """Parse module, class and function markers.""" markers = [self.module_def.marker_list, self.class_decorators, self.decorators] if markers: - self.fields.update({'markers': parse_markers(markers)}) + self.fields.update({'markers': parse_markers(markers, config)}) def _parse_docstring(self): """Parse package, module, class and function docstrings.""" @@ -166,7 +169,7 @@ def _module_markers(module_def): return markers or None -def _get_tests(path): +def _get_tests(path, config=None): """Collect tests for the test module located at ``path``.""" tests = [] with open(path) as handler: @@ -177,20 +180,22 @@ def _get_tests(path): for node in ast.iter_child_nodes(root): if isinstance(node, ast.ClassDef): [ - tests.append(TestFunction(subnode, node, root)) + tests.append(TestFunction(subnode, node, root, config)) for subnode in ast.iter_child_nodes(node) if isinstance(subnode, ast.FunctionDef) and subnode.name.startswith('test_') ] elif (isinstance(node, ast.FunctionDef) and node.name.startswith('test_')): - tests.append(TestFunction(node, testmodule=root)) + tests.append(TestFunction( + node, testmodule=root, config=config)) return tests -def collect_tests(path, ignore_paths=None): +def collect_tests(path, ignore_paths=None, config=None): """Walk ``path`` and collect test methods and functions found. + :param config: The config object of `config.BetelgeuseConfig` :param path: Either a file or directory path to look for test methods and functions. :return: A dict mapping a test module path and its test cases. @@ -201,7 +206,7 @@ def collect_tests(path, ignore_paths=None): tests = collections.OrderedDict() if os.path.isfile(path) and path not in ignore_paths: if is_test_module(os.path.basename(path)): - tests[path] = _get_tests(path) + tests[path] = _get_tests(path, config) return tests for dirpath, _, filenames in os.walk(path): if dirpath in ignore_paths: @@ -211,5 +216,5 @@ def collect_tests(path, ignore_paths=None): if path in ignore_paths: continue if is_test_module(filename): - tests[path] = _get_tests(path) + tests[path] = _get_tests(path, config) return tests diff --git a/betelgeuse/parser.py b/betelgeuse/parser.py index 9917e29..fbf773b 100644 --- a/betelgeuse/parser.py +++ b/betelgeuse/parser.py @@ -189,17 +189,18 @@ def parse_docstring(docstring=None): return fields_dict -def parse_markers(all_markers=None): +def parse_markers(all_markers=None, config=None): """Parse the markers.""" - ignore_list = ['parametrize', 'skipif', 'usefixtures', 'skip_if_not_set'] resolved_markers = [] + ignore_list = getattr(config, 'MARKERS_IGNORE_LIST', None) def _process_marker(marker): # Fetching exact marker name marker_name = marker.split('mark.')[-1] if 'mark' in marker else marker # ignoring the marker if in ignore list - if not any(ignore_word in marker_name for ignore_word in ignore_list): + if ignore_list and not any( + ignore_word in marker_name for ignore_word in ignore_list): resolved_markers.append(marker_name) for sec_marker in all_markers: diff --git a/tests/test_parser.py b/tests/test_parser.py index f6f7829..b73ce95 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1,6 +1,7 @@ # coding=utf-8 """Tests for :mod:`betelgeuse.parser`.""" import pytest +import mock from betelgeuse import parser @@ -92,7 +93,7 @@ def test_parse_markers(): List should be comma separated list of markers from all levels after removing 'pytest.mark' text and ignore some markers. """ - _mod_markers = ['pytest.mark.e2e', 'pytest.mark.destructive'] + _mod_markers = 'pytest.mark.destructive' _class_markers = [ 'pytest.mark.on_prem_provisioning', "pytest.mark.usefixtures('cleandir')" @@ -104,6 +105,8 @@ def test_parse_markers(): ] _all_markers = [_mod_markers, _class_markers, _test_markers] - expected = 'e2e, destructive, on_prem_provisioning, tier1' - - assert parser.parse_markers(_all_markers) == expected + expected = 'destructive, on_prem_provisioning, tier1' + config = mock.MagicMock() + config.MARKERS_IGNORE_LIST = [ + 'parametrize', 'skipif', 'usefixtures', 'skip_if_not_set'] + assert parser.parse_markers(_all_markers, config=config) == expected diff --git a/tests/test_source_generator.py b/tests/test_source_generator.py index b369853..d7a1748 100644 --- a/tests/test_source_generator.py +++ b/tests/test_source_generator.py @@ -1,5 +1,6 @@ """Tests for :mod:`betelgeuse.source_generator`.""" from betelgeuse import collector +import mock def test_source_generator(): @@ -41,10 +42,30 @@ def test_source_generator(): def test_source_markers(): """Verifies if the test collection collects test markers.""" - tests = collector.collect_tests('tests/data/test_sample.py') + config = mock.MagicMock() + config.MARKERS_IGNORE_LIST = [ + 'parametrize', 'skipif', 'usefixtures', 'skip_if_not_set'] + tests = collector.collect_tests('tests/data/test_sample.py', config=config) marked_test = [ test for test in tests['tests/data/test_sample.py'] if test.name == 'test_markers_sample' ].pop() assert marked_test.fields['markers'] == ('run_in_one_thread, tier1, ' 'on_prem_provisioning, osp') + + +def test_source_singular_module_marker(): + """Verifies the single module level marker is retrieved.""" + mod_string = 'import pytest\n\npytestmark = pytest.mark.tier2' \ + '\n\ndef test_sing():\n\tpass' + with open('/tmp/test_singular.py', 'w') as tfile: + tfile.writelines(mod_string) + + config = mock.Mock() + config.MARKERS_IGNORE_LIST = ['tier3'] + tests = collector.collect_tests('/tmp/test_singular.py', config=config) + marked_test = [ + test for test in tests['/tmp/test_singular.py'] + if test.name == 'test_sing' + ].pop() + assert marked_test.fields['markers'] == 'tier2'