From 9a831217e44580b374512ad583273813b00cbfe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= Date: Tue, 14 Jan 2025 10:54:05 +0100 Subject: [PATCH] test-specification: Add support for python f-strings in step and description This is only availible for tests that require paramters. If the test have a parameter --data the description and test steps can contain {data} which will be replaced with tha actual data of the parameter data in the test specification. --- test/spec/generate_spec.py | 46 ++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/test/spec/generate_spec.py b/test/spec/generate_spec.py index 09206503..3a68ca41 100755 --- a/test/spec/generate_spec.py +++ b/test/spec/generate_spec.py @@ -67,15 +67,42 @@ class TestStepVisitor(ast.NodeVisitor): self.description=self.description.strip() self.generic_visit(node) + def _rebuild_fstring(self, node: ast.JoinedStr) -> str: + """ + Rebuild an f-string (JoinedStr) by concatenating literal pieces and placeholders. + """ + parts = [] + for value in node.values: + if isinstance(value, ast.Constant): + parts.append(value.value) + elif isinstance(value, ast.FormattedValue): + expression = ast.unparse(value.value) if hasattr(ast, "unparse") else ast.dump(value.value) + parts.append(f"{{{expression}}}") + return "".join(parts) + # Check for test.step() for the actual test steps def visit_Call(self, node): """Extract test.step from test""" if isinstance(node.func, ast.Attribute) and node.func.attr == 'step': + arg = node.args[0] if isinstance(node.func.value, ast.Name) and node.func.value.id == 'test': - if node.args and isinstance(node.args[0], ast.Constant): - self.test_steps.append(node.args[0].value) + if node.args: + if isinstance(arg, ast.Constant): + self.test_steps.append(node.args[0].value) + elif isinstance(arg, ast.JoinedStr): + fstring_value = self._rebuild_fstring(arg) + self.test_steps.append(fstring_value) self.generic_visit(node) # Continue visiting other nodes +def replace_in_steps(steps, variables): + result = [] + for step in steps: + new_step = step + for k, v in variables.items(): + new_step = new_step.replace("{" + k + "}", v) + result.append(new_step) + return result + class TestCase: """All test specifcation resources for a test case""" def __init__(self, directory, rootdir=None): @@ -103,7 +130,7 @@ class TestCase: f.write(mod_content) - def generate_specification(self, name, case_path, spec_path): + def generate_specification(self, name, case_path, spec_path, variables): """Generate a ASCIIDOC specification for the test case""" with open(case_path, 'r') as file: script_content = file.read() @@ -113,6 +140,12 @@ class TestCase: visitor.visit(parsed_script) description=visitor.description if visitor.description != "" else "Undefined" test_steps=visitor.test_steps + if variables is not None: + for k,v in variables.items(): + if "{" + k + "}" in description: + description = description.replace("{" + k + "}", v) + + test_steps=replace_in_steps(test_steps, variables) if name is None: name = visitor.name self.generate_topology() @@ -150,6 +183,7 @@ def parse_suite(directory, root, suitefile): path = os.path.dirname(entry["suite"]) parse_suite(f"{directory}/{path}", root, f"{directory}/{entry['suite']}") elif entry.get("case"): + variables=None if not entry.get("infamy"): test_title = None # Set from the docstring in the test case else: @@ -161,10 +195,14 @@ def parse_suite(directory, root, suitefile): if readme is None: readme = open(f"{directory}/{path}/Readme.adoc.tmp", "w") test_case_id = entry["name"] + + if "opts" in entry: + variables = dict((k[2:], v) for k, v in zip(entry["opts"][::2], entry["opts"][1::2])) + test_file = f"{directory}/{entry['case']}" test_spec = f"{directory}/{path}/{test_case_id}.adoc" test_case = TestCase(f"{directory}/{path}", root) - test_case.generate_specification(test_title, test_file, test_spec) + test_case.generate_specification(test_title, test_file, test_spec, variables) readme.write(f"include::{path}{test_case_id}.adoc[]\n\n") if path != "": readme.close()