From 992c403fc86724a3c971486399576e60ce9791ee Mon Sep 17 00:00:00 2001 From: Parth Patel <37349063+parthdpa@users.noreply.github.com> Date: Thu, 29 Apr 2021 10:02:43 -0400 Subject: [PATCH] Improve `iterparentnodeids` to consume `/` parts until the first `::` (#8577) * Fix issue where TestCase.setUpClass is not called for test methods with a / in its name by checking if there is :: before the selected / or any :: after. Also added a test case for this. * removed print statement that was added * Change iterparentnodeids to consume / parts until the first ::. Then consider ::. Tests were changed to reflect this. * Update changelog/8509.improvement.rst Co-authored-by: Ran Benita --- AUTHORS | 1 + changelog/8509.improvement.rst | 5 +++++ src/_pytest/nodes.py | 34 ++++++++++++++++++++++------------ testing/test_nodes.py | 6 ++++-- 4 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 changelog/8509.improvement.rst diff --git a/AUTHORS b/AUTHORS index f219c0c83..fcae73823 100644 --- a/AUTHORS +++ b/AUTHORS @@ -236,6 +236,7 @@ Omar Kohl Omer Hadari Ondřej Súkup Oscar Benjamin +Parth Patel Patrick Hayes Pauli Virtanen Pavel Karateev diff --git a/changelog/8509.improvement.rst b/changelog/8509.improvement.rst new file mode 100644 index 000000000..d72e8e487 --- /dev/null +++ b/changelog/8509.improvement.rst @@ -0,0 +1,5 @@ +Fixed issue where `TestCase.setUpClass` is not called when a test has `/` in its name since pytest 6.2.0. + +This refers to the path part in pytest node IDs, e.g. `TestClass::test_it` in the node ID `tests/test_file.py::TestClass::test_it`. + +Now, instead of assuming that the test name does not contain ``/``, it is assumed that test path does not contain ``::``. We plan to hopefully make both of these work in the future. diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 99b7eb1a6..40bc4d496 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -62,23 +62,33 @@ def iterparentnodeids(nodeid: str) -> Iterator[str]: "testing/code/test_excinfo.py::TestFormattedExcinfo" "testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source" - Note that :: parts are only considered at the last / component. + Note that / components are only considered until the first ::. """ pos = 0 - sep = SEP + first_colons: Optional[int] = nodeid.find("::") + if first_colons == -1: + first_colons = None + # The root Session node - always present. yield "" + # Eagerly consume SEP parts until first colons. while True: - at = nodeid.find(sep, pos) - if at == -1 and sep == SEP: - sep = "::" - elif at == -1: - if nodeid: - yield nodeid + at = nodeid.find(SEP, pos, first_colons) + if at == -1: break - else: - if at: - yield nodeid[:at] - pos = at + len(sep) + if at > 0: + yield nodeid[:at] + pos = at + len(SEP) + # Eagerly consume :: parts. + while True: + at = nodeid.find("::", pos) + if at == -1: + break + if at > 0: + yield nodeid[:at] + pos = at + len("::") + # The node ID itself. + if nodeid: + yield nodeid def _imply_path( diff --git a/testing/test_nodes.py b/testing/test_nodes.py index fbdbce395..4d7e6cba2 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -18,11 +18,13 @@ from _pytest.warning_types import PytestWarning ("a/b/c", ["", "a", "a/b", "a/b/c"]), ("a/bbb/c::D", ["", "a", "a/bbb", "a/bbb/c", "a/bbb/c::D"]), ("a/b/c::D::eee", ["", "a", "a/b", "a/b/c", "a/b/c::D", "a/b/c::D::eee"]), - # :: considered only at the last component. ("::xx", ["", "::xx"]), - ("a/b/c::D/d::e", ["", "a", "a/b", "a/b/c::D", "a/b/c::D/d", "a/b/c::D/d::e"]), + # / only considered until first :: + ("a/b/c::D/d::e", ["", "a", "a/b", "a/b/c", "a/b/c::D/d", "a/b/c::D/d::e"]), # : alone is not a separator. ("a/b::D:e:f::g", ["", "a", "a/b", "a/b::D:e:f", "a/b::D:e:f::g"]), + # / not considered if a part of a test name + ("a/b::c/d::e[/test]", ["", "a", "a/b", "a/b::c/d", "a/b::c/d::e[/test]"]), ), ) def test_iterparentnodeids(nodeid: str, expected: List[str]) -> None: