Skip to content

Commit 6177de4

Browse files
authored
coverage: add cuda.core test_utils.py tests for DLPack/StridedMemoryView (#2181)
* coverage: add cuda.core test_utils.py tests for DLPack/StridedMemoryView * Address cuda.core coverage test review comments * Fix pytest parametrize collection for CPU samples * coverage: refine cuda.core coverage tests comments * coverage: tag new cuda.core tests as HUMAN-REVIEWED * Revert "coverage: tag new cuda.core tests as HUMAN-REVIEWED" This reverts commit 6f92e2a.
1 parent ac3b15d commit 6177de4

3 files changed

Lines changed: 417 additions & 33 deletions

File tree

cuda_core/tests/test_tensor_map.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,22 @@ def test_invalid_data_type(self, dev, skip_if_no_tma):
308308
data_type=42,
309309
)
310310

311+
def test_as_tensor_map_host_view_rejected_without_tma(self):
312+
"""``as_tensor_map`` rejects a non-device-accessible (host) view with a
313+
clear error, exercising the ``as_tensor_map`` -> ``_from_tiled`` path
314+
without needing TMA-capable hardware."""
315+
host = np.zeros((64, 64), dtype=np.float32)
316+
view = StridedMemoryView.from_any_interface(host, stream_ptr=-1)
317+
with pytest.raises(ValueError, match="device-accessible"):
318+
view.as_tensor_map(
319+
box_dim=(32, 32),
320+
data_type=TensorMapDataType.FLOAT32,
321+
element_strides=(1, 1),
322+
swizzle=TensorMapSwizzle.SWIZZLE_128B,
323+
l2_promotion=TensorMapL2Promotion.L2_128B,
324+
oob_fill=TensorMapOOBFill.NAN_REQUEST_ZERO_FMA,
325+
)
326+
311327

312328
class TestTensorMapDtypeMapping:
313329
"""Test automatic dtype inference from numpy dtypes."""

cuda_core/tests/test_utils.py

Lines changed: 86 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,37 +1060,90 @@ def test_dlpack_export_non_native_endian_rejected():
10601060
bad_view.__dlpack__()
10611061

10621062

1063-
@pytest.mark.parametrize(
1064-
"dtype",
1065-
[
1066-
np.uint8,
1067-
np.uint16,
1068-
np.uint32,
1069-
np.uint64,
1070-
np.int8,
1071-
np.int16,
1072-
np.int32,
1073-
np.int64,
1074-
np.float16,
1075-
np.float32,
1076-
np.float64,
1077-
np.complex64,
1078-
np.complex128,
1079-
np.bool_,
1080-
],
1081-
)
1082-
def test_strided_memory_view_dtype_roundtrip_all(dtype):
1083-
"""Exercise dtype_dlpack_to_numpy for every NumPy-native DLPack dtype.
1084-
1085-
bfloat16 (kDLBfloat) is excluded -- NumPy's __dlpack__ doesn't reliably
1086-
export ml_dtypes-extended dtypes; cover separately via jax/torch if needed.
1063+
def test_strided_memory_view_proxy_cai_only_has_dlpack_false():
1064+
"""``_StridedMemoryViewProxy`` records ``has_dlpack=False`` for an object
1065+
that exposes only ``__cuda_array_interface__`` (check_has_dlpack CAI branch)."""
1066+
from cuda.core._memoryview import _StridedMemoryViewProxy
1067+
1068+
obj = _make_cuda_array_interface_obj(shape=(2,), strides=None)
1069+
proxy = _StridedMemoryViewProxy(obj)
1070+
assert proxy.has_dlpack is False
1071+
assert proxy.obj is obj
1072+
1073+
1074+
def test_view_as_cai_device_pointer_and_stream_ordering(init_cuda):
1075+
"""``view_as_cai`` on a real device pointer resolves the device ordinal via
1076+
``cuPointerGetAttribute`` and takes the cross-stream branch when the CAI
1077+
``stream`` differs from the consumer stream.
1078+
1079+
This only exercises the code path and checks *device* correctness (ptr,
1080+
device_id, shape); it does NOT verify stream-order correctness. Uses a
1081+
synthetic CAI object backed by a genuine device allocation, so the
1082+
cupy/numba-only device branch is exercised without those optional deps.
10871083
"""
1088-
src = np.zeros(3, dtype=dtype)
1089-
# Probe NumPy first: if it can't export this dtype, skip as env limit.
1090-
# Any failure AFTER the probe is OUR consumer regression and must fail.
1091-
try:
1092-
src.__dlpack__()
1093-
except (BufferError, TypeError) as e:
1094-
pytest.skip(f"NumPy does not export {np.dtype(dtype)} via DLPack: {e}")
1095-
view = StridedMemoryView.from_dlpack(src, stream_ptr=-1)
1096-
assert view.dtype == np.dtype(dtype) # .dtype triggers dtype_dlpack_to_numpy
1084+
dev = init_cuda
1085+
buffer = dev.memory_resource.allocate(64, stream=dev.default_stream)
1086+
producer = dev.create_stream()
1087+
consumer = dev.create_stream()
1088+
obj = _make_cuda_array_interface_obj(
1089+
shape=(8,),
1090+
strides=None,
1091+
typestr="<f4",
1092+
data=(int(buffer.handle), False),
1093+
)
1094+
obj.__cuda_array_interface__["stream"] = int(producer.handle)
1095+
1096+
view = StridedMemoryView.from_cuda_array_interface(obj, stream_ptr=consumer.handle)
1097+
1098+
assert view.is_device_accessible is True
1099+
assert view.ptr == int(buffer.handle)
1100+
assert view.device_id == dev.device_id
1101+
assert view.shape == (8,)
1102+
dev.default_stream.sync()
1103+
1104+
1105+
def test_strided_memory_view_init_cai_path_deprecated(init_cuda):
1106+
"""The deprecated ``StridedMemoryView(obj)`` constructor routes a CAI-only
1107+
object through the CAI branch (warn + ``view_as_cai``), not the DLPack one."""
1108+
obj = _make_cuda_array_interface_obj(shape=(4,), strides=None, typestr="<f4", data=(0, False))
1109+
with pytest.deprecated_call(match="CUDA-array-interface-supporting object is deprecated"):
1110+
view = StridedMemoryView(obj, stream_ptr=-1)
1111+
assert view.is_device_accessible is True
1112+
assert view.shape == (4,)
1113+
assert view.device_id == init_cuda.device_id
1114+
1115+
1116+
def test_dlpack_export_device_accessible_cai_view(init_cuda):
1117+
"""Exporting a device-accessible CAI-backed view (no dl_tensor) drives the
1118+
``_smv_get_dl_device`` branch that calls ``get_buffer``/``classify_dl_device``
1119+
and reports a CUDA device via ``__dlpack_device__``."""
1120+
dev = init_cuda
1121+
buffer = dev.memory_resource.allocate(64, stream=dev.default_stream)
1122+
obj = _make_cuda_array_interface_obj(
1123+
shape=(8,),
1124+
strides=None,
1125+
typestr="<f4",
1126+
data=(int(buffer.handle), False),
1127+
)
1128+
view = StridedMemoryView.from_cuda_array_interface(obj, stream_ptr=-1)
1129+
1130+
device_type, device_id = view.__dlpack_device__()
1131+
assert device_type == int(DLDeviceType.kDLCUDA)
1132+
assert device_id == dev.device_id
1133+
1134+
capsule = view.__dlpack__()
1135+
assert _PyCapsule_IsValid(capsule, b"dltensor") == 1
1136+
del capsule # unconsumed -> deleter frees the managed tensor
1137+
dev.default_stream.sync()
1138+
1139+
1140+
def test_strided_memory_view_repr_with_none_dtype(init_cuda):
1141+
"""``__repr__`` of a view whose dtype is None renders the dtype via
1142+
``get_simple_repr`` taking the builtins branch (NoneType)."""
1143+
dev = init_cuda
1144+
buffer = dev.memory_resource.allocate(16, stream=dev.default_stream)
1145+
view = StridedMemoryView.from_buffer(buffer, shape=(16,), itemsize=1, dtype=None)
1146+
assert view.dtype is None
1147+
r = repr(view)
1148+
assert r.startswith("StridedMemoryView(ptr=")
1149+
assert "dtype=NoneType" in r

0 commit comments

Comments
 (0)