diff --git a/cuda_core/pytest.ini b/cuda_core/pytest.ini index 64fcf312a79..8661d2cdc64 100644 --- a/cuda_core/pytest.ini +++ b/cuda_core/pytest.ini @@ -4,6 +4,7 @@ [pytest] addopts = --showlocals --durations=20 +pythonpath = tests norecursedirs = cython markers = # Keep this authorship marker registry in sync across all pytest config roots. diff --git a/cuda_core/tests/AGENTS.md b/cuda_core/tests/AGENTS.md index 39472d745b5..fe6f100b923 100644 --- a/cuda_core/tests/AGENTS.md +++ b/cuda_core/tests/AGENTS.md @@ -65,3 +65,28 @@ by `cuCtxSynchronize()` before popping the context. Tests should not rely on that as a substitute for cleaning up explicitly: prefer context managers for resources whose lifetime fits a single scope, and keep pool lifetimes inside the test that creates them. + +## Shared test support + +See also: https://docs.pytest.org/en/stable/reference/fixtures.html#conftest-py-sharing-fixtures-across-multiple-files + +Follow these rules when adding or moving shared test code: + +- Never import from a `conftest.py`. +- Put suite-wide fixtures and pytest hooks in `tests/conftest.py`. Put fixtures + needed only by one test subtree in that subtree's nearest `conftest.py`. +- Put a pytest hook in a nested `conftest.py` only if pytest supports that hook + there. If the hook receives suite-wide data, explicitly limit its effects to + the intended subtree. +- Code used only to implement fixtures or hooks may remain in the same + `conftest.py`. Put functions and constants imported by test modules in + `tests/helpers/` instead. +- Import helpers explicitly from the test root, for example: + `from helpers.memory import create_managed_memory_resource_or_skip`. +- Fixtures in a nested `conftest.py` are available to tests in its directory + and descendants; fixtures from applicable parent `conftest.py` files remain + available. +- Do not add `__init__.py` solely because a test directory contains a + `conftest.py`. +- In directories without `__init__.py`, keep test-module basenames unique + within this test suite. diff --git a/cuda_core/tests/conftest.py b/cuda_core/tests/conftest.py index b212633ebcf..69e774de08d 100644 --- a/cuda_core/tests/conftest.py +++ b/cuda_core/tests/conftest.py @@ -29,9 +29,8 @@ pytest_plugins = ["cuda_python_test_helpers._pytest_plugin"] -from cuda_python_test_helpers.marks import skipif_need_cuda_headers # noqa: F401 (re-exported for tests) -from cuda_python_test_helpers.mempool import xfail_if_mempool_oom from helpers.constants import POOL_SIZE +from helpers.memory import skip_if_pinned_memory_unsupported import cuda.core from cuda.bindings import driver @@ -45,7 +44,7 @@ PinnedMemoryResourceOptions, _device, ) -from cuda.core._utils.cuda_utils import CUDAError, handle_return +from cuda.core._utils.cuda_utils import handle_return def pytest_configure(config): @@ -141,85 +140,6 @@ def pytest_collection_modifyitems(self, config, items): item.obj = _wrap_worker_cuda_test(item.obj) -def skip_if_pinned_memory_unsupported(device): - try: - if not device.properties.host_memory_pools_supported: - pytest.skip("Device does not support host mempool operations") - except AttributeError: - pytest.skip("PinnedMemoryResource requires CUDA 13.0 or later") - - -def skip_if_managed_memory_unsupported(device): - try: - if not device.properties.memory_pools_supported or not device.properties.concurrent_managed_access: - pytest.skip("Device does not support managed memory pool operations") - except AttributeError: - pytest.skip("ManagedMemoryResource requires CUDA 13.0 or later") - try: - ManagedMemoryResource() - except CUDAError as e: - xfail_if_mempool_oom(e, device) - raise - except RuntimeError as e: - if "requires CUDA 13.0" in str(e): - pytest.skip("ManagedMemoryResource requires CUDA 13.0 or later") - raise - - -def create_managed_memory_resource_or_skip(*args, xfail_device=None, **kwargs): - # Keep the established "skip" helper name for call-site readability, even though - # Windows MCDM mempool OOM setup failures are xfailed instead of skipped. - try: - return ManagedMemoryResource(*args, **kwargs) - except CUDAError as e: - xfail_if_mempool_oom(e, _device_id_from_resource_options(xfail_device, args, kwargs)) - if "CUDA_ERROR_NOT_SUPPORTED" in str(e): - pytest.skip("ManagedMemoryResource is not supported on this platform/device") - raise - except RuntimeError as e: - if "requires CUDA 13.0" in str(e): - pytest.skip("ManagedMemoryResource requires CUDA 13.0 or later") - if "concurrent managed access is not available" in str(e).lower(): - pytest.skip("Device does not support concurrent managed memory access") - raise - - -def create_pinned_memory_resource_or_xfail(*args, xfail_device=None, **kwargs): - try: - return PinnedMemoryResource(*args, **kwargs) - except CUDAError as e: - xfail_if_mempool_oom(e, xfail_device) - raise - - -@contextmanager -def xfail_on_graph_mempool_oom(device=0): - try: - yield - except CUDAError as e: - xfail_if_mempool_oom(e, "cuGraphAddMemAllocNode", device) - raise - - -def _device_id_from_resource_options(device, args, kwargs): - if device is not None: - return device - options = kwargs.get("options") - if options is None and args: - options = args[0] - if options is None: - return 0 - if isinstance(options, dict): - preferred_location = options.get("preferred_location") - preferred_location_type = options.get("preferred_location_type") - else: - preferred_location = getattr(options, "preferred_location", None) - preferred_location_type = getattr(options, "preferred_location_type", None) - if preferred_location_type in (None, "device") and isinstance(preferred_location, int) and preferred_location >= 0: - return preferred_location - return 0 - - def _require_ipc_mempool_devices(devices): """Return devices if they all support IPC-enabled mempools, otherwise skip.""" from helpers import supports_ipc_mempool diff --git a/cuda_core/tests/example_tests/__init__.py b/cuda_core/tests/example_tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/cuda_core/tests/graph/test_graph_builder.py b/cuda_core/tests/graph/test_graph_builder.py index 0033cb99e2c..aa1c0f8e8b0 100644 --- a/cuda_core/tests/graph/test_graph_builder.py +++ b/cuda_core/tests/graph/test_graph_builder.py @@ -10,8 +10,7 @@ import helpers import numpy as np import pytest -from conftest import skipif_need_cuda_headers -from cuda_python_test_helpers.marks import requires_module +from cuda_python_test_helpers.marks import requires_module, skipif_need_cuda_headers from helpers.graph_kernels import compile_common_kernels, compile_conditional_kernels from helpers.misc import try_create_condition from packaging.version import Version diff --git a/cuda_core/tests/graph/test_graph_definition.py b/cuda_core/tests/graph/test_graph_definition.py index a273e8b6a01..4b3122c763e 100644 --- a/cuda_core/tests/graph/test_graph_definition.py +++ b/cuda_core/tests/graph/test_graph_definition.py @@ -9,8 +9,8 @@ from dataclasses import dataclass, field import pytest -from conftest import xfail_on_graph_mempool_oom from helpers.graph_kernels import compile_common_kernels +from helpers.memory import xfail_on_graph_mempool_oom from helpers.misc import try_create_condition from cuda.core import Device, LaunchConfig diff --git a/cuda_core/tests/graph/test_graph_definition_errors.py b/cuda_core/tests/graph/test_graph_definition_errors.py index d80118cdf7c..923ea5fb74e 100644 --- a/cuda_core/tests/graph/test_graph_definition_errors.py +++ b/cuda_core/tests/graph/test_graph_definition_errors.py @@ -6,8 +6,8 @@ import ctypes import pytest -from conftest import xfail_on_graph_mempool_oom from helpers.graph_kernels import compile_common_kernels +from helpers.memory import xfail_on_graph_mempool_oom from helpers.misc import try_create_condition from cuda.core import Device, LaunchConfig diff --git a/cuda_core/tests/graph/test_graph_definition_integration.py b/cuda_core/tests/graph/test_graph_definition_integration.py index 58f96e1bab3..629f302bd75 100644 --- a/cuda_core/tests/graph/test_graph_definition_integration.py +++ b/cuda_core/tests/graph/test_graph_definition_integration.py @@ -7,7 +7,7 @@ import numpy as np import pytest -from conftest import xfail_on_graph_mempool_oom +from helpers.memory import xfail_on_graph_mempool_oom from cuda.core import Device, EventOptions, LaunchConfig, Program, ProgramOptions from cuda.core._utils.cuda_utils import driver, handle_return diff --git a/cuda_core/tests/graph/test_graph_definition_lifetime.py b/cuda_core/tests/graph/test_graph_definition_lifetime.py index 93c1453753d..c9ff492a2a8 100644 --- a/cuda_core/tests/graph/test_graph_definition_lifetime.py +++ b/cuda_core/tests/graph/test_graph_definition_lifetime.py @@ -13,8 +13,8 @@ import weakref import pytest -from conftest import xfail_on_graph_mempool_oom from helpers.graph_kernels import compile_common_kernels +from helpers.memory import xfail_on_graph_mempool_oom from helpers.misc import try_create_condition from cuda_python_test_helpers import under_compute_sanitizer diff --git a/cuda_core/tests/graph/test_graph_memory_resource.py b/cuda_core/tests/graph/test_graph_memory_resource.py index 517f9c080b7..7b9c2aaf883 100644 --- a/cuda_core/tests/graph/test_graph_memory_resource.py +++ b/cuda_core/tests/graph/test_graph_memory_resource.py @@ -5,8 +5,8 @@ """Tests for GraphMemoryResource allocation and attributes during graph capture.""" import pytest -from conftest import xfail_on_graph_mempool_oom from helpers.buffers import compare_buffer_to_constant, make_scratch_buffer, set_buffer +from helpers.memory import xfail_on_graph_mempool_oom from cuda.core import ( Device, diff --git a/cuda_core/tests/helpers/memory.py b/cuda_core/tests/helpers/memory.py new file mode 100644 index 00000000000..5dac58bd03d --- /dev/null +++ b/cuda_core/tests/helpers/memory.py @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Memory-related test helpers (skip/xfail guards and resource factories).""" + +from contextlib import contextmanager + +import pytest +from cuda_python_test_helpers.mempool import xfail_if_mempool_oom + +from cuda.core import ManagedMemoryResource, PinnedMemoryResource +from cuda.core._utils.cuda_utils import CUDAError + + +def skip_if_pinned_memory_unsupported(device): + try: + if not device.properties.host_memory_pools_supported: + pytest.skip("Device does not support host mempool operations") + except AttributeError: + pytest.skip("PinnedMemoryResource requires CUDA 13.0 or later") + + +def skip_if_managed_memory_unsupported(device): + try: + if not device.properties.memory_pools_supported or not device.properties.concurrent_managed_access: + pytest.skip("Device does not support managed memory pool operations") + except AttributeError: + pytest.skip("ManagedMemoryResource requires CUDA 13.0 or later") + try: + ManagedMemoryResource() + except CUDAError as e: + xfail_if_mempool_oom(e, device) + raise + except RuntimeError as e: + if "requires CUDA 13.0" in str(e): + pytest.skip("ManagedMemoryResource requires CUDA 13.0 or later") + raise + + +def _device_id_from_resource_options(device, args, kwargs): + if device is not None: + return device + options = kwargs.get("options") + if options is None and args: + options = args[0] + if options is None: + return 0 + if isinstance(options, dict): + preferred_location = options.get("preferred_location") + preferred_location_type = options.get("preferred_location_type") + else: + preferred_location = getattr(options, "preferred_location", None) + preferred_location_type = getattr(options, "preferred_location_type", None) + if preferred_location_type in (None, "device") and isinstance(preferred_location, int) and preferred_location >= 0: + return preferred_location + return 0 + + +def create_managed_memory_resource_or_skip(*args, xfail_device=None, **kwargs): + # Keep the established "skip" helper name for call-site readability, even though + # Windows MCDM mempool OOM setup failures are xfailed instead of skipped. + try: + return ManagedMemoryResource(*args, **kwargs) + except CUDAError as e: + xfail_if_mempool_oom(e, _device_id_from_resource_options(xfail_device, args, kwargs)) + if "CUDA_ERROR_NOT_SUPPORTED" in str(e): + pytest.skip("ManagedMemoryResource is not supported on this platform/device") + raise + except RuntimeError as e: + if "requires CUDA 13.0" in str(e): + pytest.skip("ManagedMemoryResource requires CUDA 13.0 or later") + if "concurrent managed access is not available" in str(e).lower(): + pytest.skip("Device does not support concurrent managed memory access") + raise + + +def create_pinned_memory_resource_or_xfail(*args, xfail_device=None, **kwargs): + try: + return PinnedMemoryResource(*args, **kwargs) + except CUDAError as e: + xfail_if_mempool_oom(e, xfail_device) + raise + + +@contextmanager +def xfail_on_graph_mempool_oom(device=0): + try: + yield + except CUDAError as e: + xfail_if_mempool_oom(e, "cuGraphAddMemAllocNode", device) + raise diff --git a/cuda_core/tests/memory/__init__.py b/cuda_core/tests/memory/__init__.py deleted file mode 100644 index 27422b3cb7e..00000000000 --- a/cuda_core/tests/memory/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# SPDX-License-Identifier: Apache-2.0 diff --git a/cuda_core/tests/memory/test_copy_batch_options.py b/cuda_core/tests/memory/test_copy_batch_options.py index 275c5c3f327..43e832e4854 100644 --- a/cuda_core/tests/memory/test_copy_batch_options.py +++ b/cuda_core/tests/memory/test_copy_batch_options.py @@ -8,17 +8,17 @@ """ import pytest - -# Shared with test_managed_ops.py: handles the CUDA 13 requirement, mempool -# OOM, and CUDA_ERROR_NOT_SUPPORTED (managed pools are unavailable on -# Windows), so the location-hint tests skip rather than error there. -from conftest import create_managed_memory_resource_or_skip from helpers.buffers import compare_buffer_to_constant, set_buffer from helpers.copy_batch import ( COPY_BATCH_SIZE, assert_managed_holds, ) +# Shared with test_managed_ops.py: handles the CUDA 13 requirement, mempool +# OOM, and CUDA_ERROR_NOT_SUPPORTED (managed pools are unavailable on +# Windows), so the location-hint tests skip rather than error there. +from helpers.memory import create_managed_memory_resource_or_skip + from cuda.core import Host, LegacyPinnedMemoryResource from cuda.core._memory._copy_enums import _attr_run_starts, _reject_unsupported_during_api_call from cuda.core._memory._copy_ops import ( diff --git a/cuda_core/tests/memory/test_copy_single_options.py b/cuda_core/tests/memory/test_copy_single_options.py index bfaad76dcd7..1df17a1f758 100644 --- a/cuda_core/tests/memory/test_copy_single_options.py +++ b/cuda_core/tests/memory/test_copy_single_options.py @@ -4,9 +4,9 @@ """CopyOptions support for Buffer.copy_to / Buffer.copy_from (issue #2365).""" import pytest -from conftest import create_managed_memory_resource_or_skip from helpers.buffers import compare_equal_buffers, make_scratch_buffer, set_buffer from helpers.copy_batch import assert_managed_holds +from helpers.memory import create_managed_memory_resource_or_skip from cuda.core import Device, Host, LegacyPinnedMemoryResource from cuda.core._stream import LEGACY_DEFAULT_STREAM, PER_THREAD_DEFAULT_STREAM diff --git a/cuda_core/tests/memory/test_managed_ops.py b/cuda_core/tests/memory/test_managed_ops.py index ed7f44a97f4..cb686026c7e 100644 --- a/cuda_core/tests/memory/test_managed_ops.py +++ b/cuda_core/tests/memory/test_managed_ops.py @@ -4,8 +4,8 @@ import mmap import pytest -from conftest import create_managed_memory_resource_or_skip from helpers.buffers import DummyDeviceMemoryResource, DummyUnifiedMemoryResource +from helpers.memory import create_managed_memory_resource_or_skip from cuda.bindings import driver from cuda.core import Device, Host, ManagedBuffer @@ -37,9 +37,10 @@ def _page_base(buf): def _skip_if_raw_managed_alloc_unsupported(device): - # Raw `cuMemAllocManaged` capability — distinct from conftest's - # `skip_if_managed_memory_unsupported`, which gates `ManagedMemoryResource` - # pool creation. Used by tests that exercise `DummyUnifiedMemoryResource`. + # Raw `cuMemAllocManaged` capability — distinct from + # `helpers.memory.skip_if_managed_memory_unsupported`, which gates + # `ManagedMemoryResource` pool creation. Used by tests that exercise + # `DummyUnifiedMemoryResource`. try: if not device.properties.managed_memory: pytest.skip("Device does not support managed memory operations") diff --git a/cuda_core/tests/memory_ipc/__init__.py b/cuda_core/tests/memory_ipc/__init__.py deleted file mode 100644 index 27422b3cb7e..00000000000 --- a/cuda_core/tests/memory_ipc/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# SPDX-License-Identifier: Apache-2.0 diff --git a/cuda_core/tests/system/__init__.py b/cuda_core/tests/system/__init__.py deleted file mode 100644 index 79599c77db0..00000000000 --- a/cuda_core/tests/system/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# SPDX-License-Identifier: Apache-2.0 diff --git a/cuda_core/tests/test_launcher.py b/cuda_core/tests/test_launcher.py index e5cf05b435d..a71ee2d7b4f 100644 --- a/cuda_core/tests/test_launcher.py +++ b/cuda_core/tests/test_launcher.py @@ -4,7 +4,7 @@ import ctypes import helpers -from cuda_python_test_helpers.marks import requires_module +from cuda_python_test_helpers.marks import requires_module, skipif_need_cuda_headers from helpers.misc import StreamWrapper try: @@ -13,7 +13,6 @@ cp = None import numpy as np import pytest -from conftest import skipif_need_cuda_headers from cuda.core import ( Device, diff --git a/cuda_core/tests/test_managed_memory_warning.py b/cuda_core/tests/test_managed_memory_warning.py index f0596db2fdf..6edf2b7c05a 100644 --- a/cuda_core/tests/test_managed_memory_warning.py +++ b/cuda_core/tests/test_managed_memory_warning.py @@ -11,7 +11,8 @@ import warnings import pytest -from conftest import create_managed_memory_resource_or_skip, xfail_if_mempool_oom +from cuda_python_test_helpers.mempool import xfail_if_mempool_oom +from helpers.memory import create_managed_memory_resource_or_skip import cuda.bindings from cuda.core import Device, ManagedMemoryResource, ManagedMemoryResourceOptions diff --git a/cuda_core/tests/test_memory.py b/cuda_core/tests/test_memory.py index 60883544e9e..e2b0a0da3bd 100644 --- a/cuda_core/tests/test_memory.py +++ b/cuda_core/tests/test_memory.py @@ -14,12 +14,6 @@ import re import pytest -from conftest import ( - create_managed_memory_resource_or_skip, - create_pinned_memory_resource_or_xfail, - skip_if_managed_memory_unsupported, - skip_if_pinned_memory_unsupported, -) from helpers import supports_ipc_mempool from helpers.buffers import ( DummyDeviceMemoryResource, @@ -28,6 +22,12 @@ make_instrumented_memory_resource, ) from helpers.constants import POOL_SIZE +from helpers.memory import ( + create_managed_memory_resource_or_skip, + create_pinned_memory_resource_or_xfail, + skip_if_managed_memory_unsupported, + skip_if_pinned_memory_unsupported, +) from cuda.core import ( Buffer, diff --git a/cuda_core/tests/test_object_protocols.py b/cuda_core/tests/test_object_protocols.py index baf790abea8..6ea996bc3af 100644 --- a/cuda_core/tests/test_object_protocols.py +++ b/cuda_core/tests/test_object_protocols.py @@ -13,9 +13,9 @@ import weakref import pytest -from conftest import xfail_on_graph_mempool_oom from helpers.constants import POOL_SIZE from helpers.graph_kernels import compile_common_kernels +from helpers.memory import xfail_on_graph_mempool_oom from helpers.misc import try_create_condition from cuda.core import ( diff --git a/cuda_core/tests/test_tensor_map.py b/cuda_core/tests/test_tensor_map.py index 6f63938710f..a531b3f5757 100644 --- a/cuda_core/tests/test_tensor_map.py +++ b/cuda_core/tests/test_tensor_map.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from conftest import create_managed_memory_resource_or_skip, skip_if_managed_memory_unsupported +from helpers.memory import create_managed_memory_resource_or_skip, skip_if_managed_memory_unsupported from cuda.core import ( Device,