From 8c835cb2efb9324e31ce3941cd88a0bdac7248f8 Mon Sep 17 00:00:00 2001 From: AndreFGard <96950645+AndreFGard@users.noreply.github.com> Date: Sat, 27 Sep 2025 09:59:33 -0300 Subject: [PATCH 1/2] fix sorting for parametrized tests Previously, any parametrized test would raise a KeyError on TestOrder.lineno because that function didnt account for the 'file.py::function[param_id]' Hierarchy string format. Another check that deals with this was added and the function was repurposed into get_testInfo, as to allow reuse in TestOrder.function_source, which presented the same buggy behavior previously --- runner/__init__.py | 2 +- runner/sort.py | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/runner/__init__.py b/runner/__init__.py index 4c9a205..20f1a7b 100644 --- a/runner/__init__.py +++ b/runner/__init__.py @@ -45,7 +45,7 @@ def pytest_collection_modifyitems(self, session, config, items): def _sort_by_lineno(item): test_id = Hierarchy(item.nodeid) source = Path(item.fspath) - return TestOrder.lineno(test_id, source) + return TestOrder.get_testInfo(test_id, source).lineno items.sort(key=_sort_by_lineno) diff --git a/runner/sort.py b/runner/sort.py index 1abdd26..2ff52ff 100644 --- a/runner/sort.py +++ b/runner/sort.py @@ -84,14 +84,21 @@ def get_hierarchy(self, name: Hierarchy) -> Hierarchy: @classmethod - def lineno(cls, test_id: Hierarchy, source: Path) -> int: + def get_testInfo(cls, test_id: Hierarchy, source: Path) -> TestInfo: """ - Returns the line that the given test was defined on. + Returns the test's TestInfo (lineno, end_lineno, variants). """ if test_id not in cls._cache: tree = parse(source.read_text(), source.name) - cls(Hierarchy(test_id.split("::")[0])).visit(tree) - return cls._cache[test_id].lineno + cls(Hierarchy(test_id.split("::")[0]) + ).visit(tree) + + if test_id not in cls._cache: + #possibly a parametrized test. get the parametrized function's lineno instead + #in that case, test_id would be like path/file.py::parametrized_function[param_id] + test_id = Hierarchy(test_id.split('[')[0]) + + return cls._cache[test_id] @classmethod @@ -103,14 +110,10 @@ def function_source(cls, test_id: Hierarchy, source: Path) -> str: :return: str of the source code of the given test. """ text = source.read_text() - testinfo = cls._cache[test_id] + testinfo = cls.get_testInfo(test_id, source) lines = text.splitlines()[testinfo.lineno: testinfo.end_lineno + 1] - if test_id not in cls._cache: - tree = parse(text, source.name) - cls(Hierarchy(test_id.split("::")[0])).visit(tree) - if not lines[-1]: lines.pop() From cc55f5fadd22c39c1e036dbba24a140c741fd599 Mon Sep 17 00:00:00 2001 From: AndreFGard <96950645+AndreFGard@users.noreply.github.com> Date: Sat, 27 Sep 2025 10:00:30 -0300 Subject: [PATCH 2/2] add tests for parametrized testing --- .../example_parametrized.py | 2 + .../example_parametrized_test.py | 18 +++++++ test/example-parametrized/results.json | 47 +++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 test/example-parametrized/example_parametrized.py create mode 100644 test/example-parametrized/example_parametrized_test.py create mode 100644 test/example-parametrized/results.json diff --git a/test/example-parametrized/example_parametrized.py b/test/example-parametrized/example_parametrized.py new file mode 100644 index 0000000..64a7e71 --- /dev/null +++ b/test/example-parametrized/example_parametrized.py @@ -0,0 +1,2 @@ +def plus1(x): + return x + 1 \ No newline at end of file diff --git a/test/example-parametrized/example_parametrized_test.py b/test/example-parametrized/example_parametrized_test.py new file mode 100644 index 0000000..5a31d86 --- /dev/null +++ b/test/example-parametrized/example_parametrized_test.py @@ -0,0 +1,18 @@ +import pytest +from example_parametrized import plus1 + +params = [ + pytest.param(1, 2, id="test1"), + pytest.param(2, 3, id="test2"), + pytest.param(3, 4, id="test3"), +] + +@pytest.mark.parametrize(("x", "y"), params) +def test_plus1_parameter(x, y): + assert plus1(x) == y + +def test_unparametrized(x=1,y=2): + assert x+1 + +if __name__ == '__main__': + pytest.main() diff --git a/test/example-parametrized/results.json b/test/example-parametrized/results.json new file mode 100644 index 0000000..f2677b8 --- /dev/null +++ b/test/example-parametrized/results.json @@ -0,0 +1,47 @@ +{ + "version": 3, + "status": "pass", + "tests": [ + { + "name": "test plus1 parameter[test1]", + "status": "pass", + "test_code": "assert plus1(x) == y", + "task_id": 0, + "filename": "test/example-parametrized/example_parametrized_test.py", + "line_no": 9, + "duration": 9.589799446985126e-05, + "score": 2.5 + }, + { + "name": "test plus1 parameter[test2]", + "status": "pass", + "test_code": "assert plus1(x) == y", + "task_id": 0, + "filename": "test/example-parametrized/example_parametrized_test.py", + "line_no": 9, + "duration": 8.842398528940976e-05, + "score": 2.5 + }, + { + "name": "test plus1 parameter[test3]", + "status": "pass", + "test_code": "assert plus1(x) == y", + "task_id": 0, + "filename": "test/example-parametrized/example_parametrized_test.py", + "line_no": 9, + "duration": 7.641100091859698e-05, + "score": 2.5 + }, + { + "name": "test unparametrized", + "status": "pass", + "test_code": "assert x+1", + "task_id": 0, + "filename": "test/example-parametrized/example_parametrized_test.py", + "line_no": 13, + "duration": 7.80870032031089e-05, + "score": 2.5 + } + ], + "max_score": 10 +} \ No newline at end of file