diff --git a/betelgeuse/__init__.py b/betelgeuse/__init__.py index a74ebd7..30121e7 100644 --- a/betelgeuse/__init__.py +++ b/betelgeuse/__init__.py @@ -423,7 +423,7 @@ def get_requirement_field_values(config, requirement): def update_testcase_fields(config, testcase): """Apply testcase fields default values and transformations.""" - if testcase.docstring and not type(testcase.docstring) == str: + if testcase.docstring and not isinstance(testcase.docstring, str): testcase.docstring = testcase.docstring.decode('utf8') # Check if any field needs a default value diff --git a/betelgeuse/collector.py b/betelgeuse/collector.py index 95edce6..00511f9 100644 --- a/betelgeuse/collector.py +++ b/betelgeuse/collector.py @@ -6,6 +6,7 @@ import os from betelgeuse.parser import parse_docstring +from betelgeuse.parser import parse_markers from betelgeuse.source_generator import gen_source @@ -88,11 +89,20 @@ 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.junit_id = self._generate_junit_id() if 'id' not in self.fields: self.fields['id'] = self.junit_id + def _parse_markers(self): + """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)}) + def _parse_docstring(self): """Parse package, module, class and function docstrings.""" if self.docstring is None: @@ -137,12 +147,33 @@ def is_test_module(filename): return False +def _module_markers(module_def): + """Extract markers applied to testcases from the test module level. + + The markers list would be collected from the pytestmark global variable. + """ + markers = [] + for node in module_def.body: + if isinstance(node, ast.Assign): + for target in node.targets: + if isinstance(target, ast.Name) and target.id == 'pytestmark': + if isinstance(node.value, ast.List): + for item in node.value.elts: + if isinstance(item, ast.Attribute): + markers.append(item.attr) + elif isinstance(node.value, ast.Attribute): + markers.append(node.value.attr) + return markers or None + + def _get_tests(path): """Collect tests for the test module located at ``path``.""" tests = [] with open(path) as handler: root = ast.parse(handler.read()) root.path = path # TODO improve how to pass the path to TestFunction + # Updating test module with module level markers + root.__dict__['marker_list'] = _module_markers(root) for node in ast.iter_child_nodes(root): if isinstance(node, ast.ClassDef): [ diff --git a/betelgeuse/parser.py b/betelgeuse/parser.py index e4ea5d7..d7f6eb3 100644 --- a/betelgeuse/parser.py +++ b/betelgeuse/parser.py @@ -187,3 +187,31 @@ def parse_docstring(docstring=None): field_value = output fields_dict[field_name] = field_value return fields_dict + + +def parse_markers(all_markers=None): + """Parse the markers.""" + ignore_list = ['parametrize', 'skipif', 'usefixtures', 'skip_if_not_set'] + resolved_markers = [] + + 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): + resolved_markers.append(marker_name) + + for sec_marker in all_markers: + # If the marker is none + if not sec_marker: + continue + elif isinstance(sec_marker, list): + for marker in sec_marker: + _process_marker(marker) + else: + _process_marker(sec_marker) + + if resolved_markers: + resolved_markers = ', '.join(resolved_markers) + return resolved_markers