Skip to content

fix(test-helpers): match package markers on path segments so they apply in CI - #2566

Open
LeSingh1 wants to merge 1 commit into
NVIDIA:mainfrom
LeSingh1:helpers-pytest-plugin-nodeid
Open

fix(test-helpers): match package markers on path segments so they apply in CI#2566
LeSingh1 wants to merge 1 commit into
NVIDIA:mainfrom
LeSingh1:helpers-pytest-plugin-nodeid

Conversation

@LeSingh1

@LeSingh1 LeSingh1 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Stacking note: adds a file to cuda_python_test_helpers/tests/, the directory created by #2562. Different file, clean merge either way. (On a non-glibc host you need #2562 to import the package at all, but that is irrelevant on CI.)

Problem

pytest_collection_modifyitems tags every collected item by matching its nodeid against a repo-root-relative prefix:

nodeid = item.nodeid.replace("\\", "/")
if nodeid.startswith("cuda_core/tests/") or "/cuda_core/tests/" in nodeid:
    item.add_marker(pytest.mark.core)

But item.nodeid is relative to pytest's rootdir, and each subpackage ships its own pytest.ini. ci/tools/run-tests runs:

pushd ./cuda_core
pytest -rxXs -v --durations=0 --randomly-dont-reorganize tests/

so rootdir is cuda_core/ and every nodeid starts at tests/.

Confirmed against real CI logs

From run 31274048063 on main, counting node ids in the raw job logs:

job tests/…py:: cuda_core/tests/…py::
93145314849 — Python 3.12, CUDA 12.9.1 (local), GPU l4 9543 0
93145314844 — Python 3.11, CUDA 12.9.1 (wheels), GPU rtxpro6000 9519 0
93145314853 — Python 3.12, CUDA 13.3.0 (wheels), GPU l4 10447 0

Job 93145314853 also contains the cython suite, e.g. tests/cython/test_cython.py::test_ccuda_memcpy.

So the core / bindings / pathfinder markers are never applied in CI.

The cython marker is in the same position. For tests/cython/test_cython.py::test_ccuda_memcpy, none of the three conditions holds:

  • "/tests/cython/" in nodeid → no leading slash → False
  • nodeid.endswith("/tests/cython") → False
  • "/cython/" in nodeid and "/tests/" in nodeid/cython/ matches, /tests/ does not → False

Why the cython case matters beyond labelling

The CUDA-header gate is nested inside the cython branch:

item.add_marker(pytest.mark.cython)
if "core" in item.keywords and not have_headers:
    item.add_marker(pytest.mark.skip(reason="... CUDA_PATH or CUDA_HOME is not set ..."))

Since the outer branch never runs, the gate can never fire: core cython tests are never skipped when CUDA_PATH/CUDA_HOME is unset — they run and fail on a missing header instead. "core" in item.keywords is unreachable for a second, independent reason too: the core marker it depends on is itself one of the ones that never got applied. The skip reason string appears 0 times across all three job logs.

Fix

Match on path segments taken from item.path, which is absolute and does not move with rootdir, falling back to the nodeid for items that have no path:

  • a package marker when <package> is immediately followed by tests;
  • cython / smoke when tests is immediately followed by cython / integration.

Segment adjacency replaces substring matching, so toolshed/tests/... still gets no package marker and a stray cython directory outside a tests/ tree is not picked up.

Tests

cuda_python_test_helpers had no tests for this. Added cuda_python_test_helpers/tests/test_pytest_plugin.py, driving pytest_collection_modifyitems with fake items whose node ids are taken verbatim from the CI logs above:

  • package markers for the CI-shaped node ids (core, bindings, pathfinder);
  • the repo-root-shaped nodeid still works;
  • tests/cython/... gets both core and cython;
  • the header gate skips when _cuda_headers_available() is False and does not when it is True;
  • tests/integration/... gets smoke;
  • toolshed/tests/... gets no package marker.

Verification

Ran all eight cases against the upstream/main plugin and the fixed one, with cuda_python_test_helpers.marks stubbed (it imports cuda.pathfinder, which cannot load on macOS):

--- upstream/main ---
  FAIL tests/test_memory.py::test_buffer                 markers=[]
  FAIL tests/graph/test_graph_builder.py::test_build     markers=[]
  FAIL tests/test_cuda.py::test_x                        markers=[]
  FAIL tests/test_search_steps.py::test_y                markers=[]
  OK   cuda_core/tests/test_memory.py::test_buffer       markers=['core']
  FAIL tests/cython/test_cython.py::test_ccuda_memcpy    markers=[]
  OK   tests/integration/test_smoke.py::test_z           markers=['smoke']
  OK   toolshed/tests/test_thing.py::test_w              markers=[]
  FAIL header-gate skip applied without CUDA headers: False

--- WITH FIX ---   all eight OK, header-gate skip applied: True

The nodeid shape was also reproduced locally against the repo's own cuda_core/pytest.ini: pushd cuda_core && pytest tests/ yields tests/test_sample.py::test_one, and even pytest cuda_core/tests/ from the repo root yields the same, because rootdir resolves to cuda_core/ either way.

Not run: pytest cuda_python_test_helpers/tests/test_pytest_plugin.py as written — it imports the package, which imports cuda.pathfinder (unavailable on macOS). The stub run above exercises the same function with the same inputs. Please treat CI as the first real run.

Note on blast radius

This makes -m core, -m bindings, -m pathfinder and -m cython actually select tests in CI for the first time, and re-enables the header gate. If any job relies on those selectors currently matching nothing, it will start behaving differently — worth a look before merging. I did not find such a job in .github/workflows or ci/tools/run-tests.

… nodeid

pytest_collection_modifyitems tags every collected item by matching its
nodeid against a repo-root-relative prefix:

    if nodeid.startswith("cuda_core/tests/") or "/cuda_core/tests/" in nodeid:
        item.add_marker(pytest.mark.core)

but item.nodeid is relative to pytest's *rootdir*, and each subpackage ships
its own pytest.ini. ci/tools/run-tests runs

    pushd ./cuda_core
    pytest -rxXs -v --durations=0 --randomly-dont-reorganize tests/

so rootdir is cuda_core/ and every nodeid starts at "tests/". Confirmed
against real NVIDIA CI job logs (run 31274048063):

    job 93145314849:  9543 node ids matching  tests/...py::
                         0 node ids matching  cuda_core/tests/...py::
    job 93145314844:  9519 /  0
    job 93145314853: 10447 /  0,  including tests/cython/test_cython.py::test_ccuda_memcpy

So the core/bindings/pathfinder markers are never applied in CI. The same is
true of the cython marker: for "tests/cython/test_cython.py::test_x", none of
"/tests/cython/" in nodeid, nodeid.endswith("/tests/cython"), or
("/cython/" in nodeid and "/tests/" in nodeid) is true, because each needs a
leading slash that a rootdir-relative nodeid does not have.

That last one matters beyond labelling: the CUDA-header gate is nested inside
the cython branch, so

    if "core" in item.keywords and not have_headers: ... skip ...

can never fire. Core cython tests are never skipped when CUDA_PATH is unset;
they run and fail on a missing header instead. "core" in item.keywords is
also unreachable for a second reason -- the core marker it depends on is one
of the ones that never got applied.

Match on path segments from item.path, which is absolute and does not move
with rootdir, falling back to the nodeid when an item has no path. A package
marker is applied when "<package>" is immediately followed by "tests", and
cython/smoke when "tests" is immediately followed by "cython"/"integration".
The repo-root invocation keeps working; "toolshed/tests/..." still gets no
package marker.
@copy-pr-bot

copy-pr-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant