Skip to content

Commit 4b4e769

Browse files
committed
test(cuda.core): share VMM_HANDLE_TYPE and tighten gate-test patching
- helpers/memory.py: define VMM_HANDLE_TYPE once from IS_WINDOWS and use it in test_memory.py, graph/test_graph_definition_lifetime.py and memory/test_resource_live_count.py, replacing four local copies (one of which used platform.system()). - test_init_cuda_collect_gate.py: patch conftest globals through _patch_conftest_global, which asserts the name exists. monkeypatch.setitem silently adds a missing key, so a rename in conftest would have left the gate unpatched and a test passing without exercising it. - tests/AGENTS.md: state that the leftover and baseline-release checks track only owned memory pools and VA reservations, name both warnings escalated in pytest.ini, and note that default-pool wrappers, pool-less MemoryResource subclasses and their buffers are not counted.
1 parent 774ddcc commit 4b4e769

6 files changed

Lines changed: 40 additions & 28 deletions

File tree

‎cuda_core/tests/AGENTS.md‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,15 @@ whose lifetime fits a single scope, and keep pool lifetimes inside the test
6969
that creates them. The collected items and imported modules are frozen after
7070
collection, so the collect covers only objects created during the run.
7171

72-
A resource that outlives a test without `init_cuda` makes the next `init_cuda`
73-
test fail with `UnreleasedMemoryResourceWarning` (escalated to an error in
74-
`pytest.ini`). Release what you construct, and
75-
request `init_cuda` in any test that constructs one (see "Tests that touch CUDA
76-
must establish their own context").
72+
An owned memory pool or VA reservation that outlives a test without `init_cuda`
73+
makes the next `init_cuda` test fail with `UnreleasedMemoryResourceWarning`, and
74+
an `init_cuda` test that releases one created before it started fails with
75+
`BaselineMemoryResourceReleasedWarning` (both escalated to errors in
76+
`pytest.ini`). Other resources -- wrappers of the device default pool,
77+
`MemoryResource` subclasses that hold no pool, and their buffers -- are not
78+
counted, so leaking one goes undetected. Release what you construct regardless,
79+
and request `init_cuda` in any test that constructs a memory resource (see
80+
"Tests that touch CUDA must establish their own context").
7781

7882
## Shared test support
7983

‎cuda_core/tests/graph/test_graph_definition_lifetime.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
import pytest
1414
from cuda_python_test_helpers.subprocess_runner import run_python_snippet
1515
from helpers.graph_kernels import compile_common_kernels
16+
from helpers.memory import VMM_HANDLE_TYPE
1617
from helpers.misc import try_create_condition
1718

18-
from cuda_python_test_helpers import IS_WINDOWS, under_compute_sanitizer
19+
from cuda_python_test_helpers import under_compute_sanitizer
1920

2021
# Resource finalization triggered by graph destruction is not synchronous. A
2122
# CUDA user-object callback transfers each node attachment bundle to a
@@ -112,8 +113,7 @@ def _device_memory_resource(dev):
112113
def _virtual_memory_resource(dev):
113114
if not dev.properties.virtual_memory_management_supported:
114115
pytest.skip("Device does not support virtual memory management")
115-
handle_type = "win32_kmt" if IS_WINDOWS else "posix_fd"
116-
return VirtualMemoryResource(dev, config=VirtualMemoryResourceOptions(handle_type=handle_type))
116+
return VirtualMemoryResource(dev, config=VirtualMemoryResourceOptions(handle_type=VMM_HANDLE_TYPE))
117117

118118

119119
# Memory resources whose buffers a graph node can retain through its

‎cuda_core/tests/helpers/memory.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
from cuda.core import ManagedMemoryResource, PinnedMemoryResource
99
from cuda.core._utils.cuda_utils import CUDAError
10+
from cuda_python_test_helpers import IS_WINDOWS
11+
12+
# Shareable handle type for VirtualMemoryResource allocations on this platform.
13+
VMM_HANDLE_TYPE = "win32_kmt" if IS_WINDOWS else "posix_fd"
1014

1115

1216
class UnreleasedMemoryResourceWarning(Warning):

‎cuda_core/tests/memory/test_init_cuda_collect_gate.py‎

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,17 @@ def _init_cuda_func(request):
9393
return func
9494

9595

96+
def _patch_conftest_global(monkeypatch, func, name, value):
97+
"""Monkeypatch a conftest module global that ``func`` reads.
98+
99+
``monkeypatch.setitem`` silently adds a missing key, so after a rename in
100+
conftest the patch would no longer reach the gate; fail loudly instead.
101+
"""
102+
g = func.__globals__
103+
assert name in g, f"conftest no longer defines {name!r}"
104+
monkeypatch.setitem(g, name, value)
105+
106+
96107
def _spy_on_gc_collect(monkeypatch):
97108
"""Patch ``gc.collect`` to record each explicit call; returns the call list.
98109
@@ -122,11 +133,10 @@ def _finish_teardown(gen):
122133
@pytest.mark.agent_authored(model="claude-opus-4.8")
123134
def test_teardown_skips_collect_when_counts_unchanged(request, monkeypatch):
124135
func = _init_cuda_func(request)
125-
g = func.__globals__
126136
# Disable the leftover check so only the teardown gate can call gc.collect,
127137
# and script equal setup/teardown counts.
128-
monkeypatch.setitem(g, "_mr_expected_live", None)
129-
monkeypatch.setitem(g, "_live_resource_counts", _ScriptedCounts((2, 1)))
138+
_patch_conftest_global(monkeypatch, func, "_mr_expected_live", None)
139+
_patch_conftest_global(monkeypatch, func, "_live_resource_counts", _ScriptedCounts((2, 1)))
130140
collects = _spy_on_gc_collect(monkeypatch)
131141

132142
gen = func(_FakeRequest())
@@ -141,10 +151,9 @@ def test_teardown_skips_collect_when_counts_unchanged(request, monkeypatch):
141151
@pytest.mark.agent_authored(model="claude-opus-4.8")
142152
def test_teardown_collects_when_a_count_grows(request, monkeypatch):
143153
func = _init_cuda_func(request)
144-
g = func.__globals__
145-
monkeypatch.setitem(g, "_mr_expected_live", None)
154+
_patch_conftest_global(monkeypatch, func, "_mr_expected_live", None)
146155
# Owned-pool count grows from 2 to 3 across the test.
147-
monkeypatch.setitem(g, "_live_resource_counts", _ScriptedCounts((2, 1), (3, 1)))
156+
_patch_conftest_global(monkeypatch, func, "_live_resource_counts", _ScriptedCounts((2, 1), (3, 1)))
148157
collects = _spy_on_gc_collect(monkeypatch)
149158

150159
gen = func(_FakeRequest())
@@ -162,13 +171,12 @@ def test_teardown_collects_when_a_count_grows(request, monkeypatch):
162171
@pytest.mark.agent_authored(model="claude-opus-4.8")
163172
def test_setup_warns_when_resources_left_alive_since_last_init_cuda(request, monkeypatch):
164173
func = _init_cuda_func(request)
165-
g = func.__globals__
166174
# Force the leftover-check precondition: the item started with one more owned
167175
# pool than the baseline recorded after the previous init_cuda test.
168-
monkeypatch.setitem(g, "_mr_expected_live", (0, 0))
169-
monkeypatch.setitem(g, "_mr_live_at_item_start", (1, 0))
170-
monkeypatch.setitem(g, "_mr_last_init_cuda_nodeid", "tests/prev::test_prev")
171-
monkeypatch.setitem(g, "_live_resource_counts", _ScriptedCounts((1, 0)))
176+
_patch_conftest_global(monkeypatch, func, "_mr_expected_live", (0, 0))
177+
_patch_conftest_global(monkeypatch, func, "_mr_live_at_item_start", (1, 0))
178+
_patch_conftest_global(monkeypatch, func, "_mr_last_init_cuda_nodeid", "tests/prev::test_prev")
179+
_patch_conftest_global(monkeypatch, func, "_live_resource_counts", _ScriptedCounts((1, 0)))
172180

173181
gen = func(_FakeRequest())
174182
with pytest.warns(UnreleasedMemoryResourceWarning, match="left alive"):
@@ -180,11 +188,10 @@ def test_setup_warns_when_resources_left_alive_since_last_init_cuda(request, mon
180188
@pytest.mark.agent_authored(model="claude-opus-4.8")
181189
def test_teardown_warns_on_baseline_release(request, monkeypatch):
182190
func = _init_cuda_func(request)
183-
g = func.__globals__
184-
monkeypatch.setitem(g, "_mr_expected_live", None) # disable the leftover check
191+
_patch_conftest_global(monkeypatch, func, "_mr_expected_live", None) # disable the leftover check
185192
# Script a snapshot of (1, 0) at setup and (0, 0) at teardown: an owned pool
186193
# that existed at setup was released during the test.
187-
monkeypatch.setitem(g, "_live_resource_counts", _ScriptedCounts((1, 0), (0, 0)))
194+
_patch_conftest_global(monkeypatch, func, "_live_resource_counts", _ScriptedCounts((1, 0), (0, 0)))
188195

189196
gen = func(_FakeRequest())
190197
next(gen) # setup: live_at_setup == (1, 0)

‎cuda_core/tests/memory/test_resource_live_count.py‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
"""
1818

1919
import gc
20-
import platform
2120
import threading
2221

2322
import pytest
2423
from helpers.constants import POOL_SIZE
24+
from helpers.memory import VMM_HANDLE_TYPE
2525

2626
from cuda.core import (
2727
DeviceMemoryResource,
@@ -31,9 +31,6 @@
3131
)
3232
from cuda.core._memory._buffer import _live_owned_mempool_count, _live_va_reservation_count
3333

34-
# Matches the VMM handle type helper in test_memory.py.
35-
VMM_HANDLE_TYPE = "win32_kmt" if platform.system() == "Windows" else "posix_fd"
36-
3734
_THREAD_UNSAFE_MARKER = pytest.mark.thread_unsafe(reason="asserts on the process-global owned-resource counts")
3835

3936

‎cuda_core/tests/test_memory.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from helpers.constants import POOL_SIZE
3535
from helpers.contexts import assert_no_cuda_warning, current_context_handle, no_current_context
3636
from helpers.memory import (
37+
VMM_HANDLE_TYPE,
3738
create_managed_memory_resource_or_skip,
3839
create_pinned_memory_resource_or_xfail,
3940
skip_if_managed_memory_unsupported,
@@ -1270,7 +1271,6 @@ def get_handle_type():
12701271
return (("posix_fd", None),)
12711272

12721273

1273-
VMM_HANDLE_TYPE = "win32_kmt" if IS_WINDOWS else "posix_fd"
12741274
MIB = 1024 * 1024
12751275

12761276

@@ -1446,7 +1446,7 @@ def test_vmm_allocator_policy_configuration(init_cuda):
14461446
location_type="device",
14471447
granularity="recommended",
14481448
gpu_direct_rdma=False,
1449-
handle_type="posix_fd" if not IS_WINDOWS else "win32_kmt",
1449+
handle_type=VMM_HANDLE_TYPE,
14501450
peers=(),
14511451
self_access="r", # Read-only access
14521452
peer_access="r",

0 commit comments

Comments
 (0)