@@ -34,9 +34,9 @@ def pytest_collection_modifyitems(
3434 self .nodeids .update (item .nodeid for item in items )
3535
3636
37- @pytest . fixture ( scope = "module" )
38- def all_tests ( ) -> frozenset [str ]:
39- """Collect every test node ID in the suite, exactly once .
37+ @beartype
38+ def _tests_from_pattern ( * , ci_pattern : str ) -> set [str ]:
39+ """From a CI pattern, get all tests ``pytest`` would collect .
4040
4141 Uses a collection-hook plugin instead of parsing stdout: an in-process
4242 ``pytest.main()`` installs its own output capture, so reading from
@@ -57,42 +57,20 @@ def all_tests() -> frozenset[str]:
5757 # Unknown config option: retry_delay
5858 # ```
5959 "--disable-warnings" ,
60- "." ,
60+ ci_pattern ,
6161 ],
6262 plugins = [plugin ],
6363 )
6464 # Fail loudly on collection errors (import failures, syntax errors, etc.)
6565 # rather than silently using whatever items were captured before the
6666 # crash.
6767 assert exit_code == pytest .ExitCode .OK , (
68- f"Collection failed with exit code { exit_code } ."
68+ f"Collection for { ci_pattern !r } failed with exit code { exit_code } ."
6969 )
70- return frozenset (plugin .nodeids )
71-
70+ return plugin .nodeids
7271
73- @beartype
74- def _matches (* , nodeid : str , ci_pattern : str ) -> bool :
75- """Whether ``pytest <ci_pattern>`` would have collected ``nodeid``.
76-
77- The patterns in the CI matrix are all of the form ``path[/]`` or
78- ``path::Class[::method]``. A node ID matches if it equals the pattern,
79- is a directory child of a pattern ending with ``/``, or extends the
80- pattern at a ``::`` (sub-item), ``/`` (path), or ``[`` (parametrize)
81- boundary.
82- """
83- if nodeid == ci_pattern :
84- return True
85- if not nodeid .startswith (ci_pattern ):
86- return False
87- if ci_pattern .endswith ("/" ):
88- return True
89- return nodeid [len (ci_pattern )] in {":" , "/" , "[" }
9072
91-
92- def test_ci_patterns_valid (
93- request : pytest .FixtureRequest ,
94- all_tests : frozenset [str ],
95- ) -> None :
73+ def test_ci_patterns_valid (request : pytest .FixtureRequest ) -> None :
9674 """
9775 All of the CI patterns in the CI configuration match at least one
9876 test in
@@ -101,17 +79,14 @@ def test_ci_patterns_valid(
10179 ci_patterns = _ci_patterns (repository_root = request .config .rootpath )
10280
10381 for ci_pattern in ci_patterns :
104- matched = {
105- n for n in all_tests if _matches (nodeid = n , ci_pattern = ci_pattern )
106- }
82+ tests = _tests_from_pattern (ci_pattern = ci_pattern )
10783 message = f'"{ ci_pattern } " does not match any tests.'
108- assert matched , message
84+ assert tests , message
10985
11086
11187def test_tests_collected_once (
11288 * ,
11389 request : pytest .FixtureRequest ,
114- all_tests : frozenset [str ],
11590) -> None :
11691 """Each test in the test suite is collected exactly once.
11792
@@ -121,9 +96,9 @@ def test_tests_collected_once(
12196 tests_to_patterns : dict [str , set [str ]] = {}
12297
12398 for pattern in ci_patterns :
124- for test in all_tests :
125- if _matches ( nodeid = test , ci_pattern = pattern ) :
126- tests_to_patterns .setdefault (test , set ()).add (pattern )
99+ tests = _tests_from_pattern ( ci_pattern = pattern )
100+ for test in tests :
101+ tests_to_patterns .setdefault (test , set ()).add (pattern )
127102
128103 for test_name , patterns in tests_to_patterns .items ():
129104 message = (
@@ -133,5 +108,6 @@ def test_tests_collected_once(
133108 )
134109 assert len (patterns ) == 1 , message
135110
111+ all_tests = _tests_from_pattern (ci_pattern = "." )
136112 assert tests_to_patterns .keys () - all_tests == set ()
137113 assert all_tests - tests_to_patterns .keys () == set ()
0 commit comments