Skip to content

Commit

Permalink
Configurable Markers ignore list
Browse files Browse the repository at this point in the history
  • Loading branch information
jyejare committed Dec 13, 2023
1 parent 6bdf021 commit c8f4378
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 19 deletions.
2 changes: 1 addition & 1 deletion betelgeuse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
25 changes: 15 additions & 10 deletions betelgeuse/collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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:
Expand All @@ -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.
Expand All @@ -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:
Expand All @@ -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
7 changes: 4 additions & 3 deletions betelgeuse/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 7 additions & 4 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# coding=utf-8
"""Tests for :mod:`betelgeuse.parser`."""
import pytest
import mock

from betelgeuse import parser

Expand Down Expand Up @@ -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')"
Expand All @@ -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
23 changes: 22 additions & 1 deletion tests/test_source_generator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Tests for :mod:`betelgeuse.source_generator`."""
from betelgeuse import collector
import mock


def test_source_generator():
Expand Down Expand Up @@ -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'

0 comments on commit c8f4378

Please sign in to comment.