Skip to content

Commit da59966

Browse files
committed
test: add error-path coverage for cuda_utils and related regression check
1 parent 69d3a1f commit da59966

File tree

6 files changed

+141
-5
lines changed

6 files changed

+141
-5
lines changed

cuda_core/tests/test_cuda_utils.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
33
# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
44

5+
import dataclasses
6+
57
import pytest
68

79
from cuda.bindings import driver, runtime
810
from cuda.core._utils import cuda_utils
11+
from cuda.core._utils.clear_error_support import assert_type_str_or_bytes_like, raise_code_path_meant_to_be_unreachable
912

1013

1114
def test_driver_cu_result_explanations_health():
@@ -76,3 +79,60 @@ def test_check_runtime_error():
7679
assert enum_name in msg
7780
# Smoke test: We don't want most to be unexpected.
7881
assert num_unexpected < len(driver.CUresult) * 0.5
82+
83+
84+
def test_precondition():
85+
def checker(*args, what=""):
86+
if args[0] < 0:
87+
raise ValueError(f"{what}: negative")
88+
89+
@cuda_utils.precondition(checker, what="value check")
90+
def my_func(x):
91+
return x * 2
92+
93+
assert my_func(5) == 10
94+
with pytest.raises(ValueError, match="negative"):
95+
my_func(-1)
96+
97+
98+
@dataclasses.dataclass
99+
class _DummyOptions:
100+
x: int = 1
101+
y: str = "hello"
102+
103+
104+
def test_check_nvrtc_error_without_handle():
105+
from cuda.bindings import nvrtc
106+
107+
assert cuda_utils._check_nvrtc_error(nvrtc.nvrtcResult.NVRTC_SUCCESS) == 0
108+
with pytest.raises(cuda_utils.NVRTCError):
109+
cuda_utils._check_nvrtc_error(nvrtc.nvrtcResult.NVRTC_ERROR_COMPILATION)
110+
111+
112+
def test_check_nvrtc_error_with_handle(init_cuda):
113+
from cuda.bindings import nvrtc
114+
115+
err, prog = nvrtc.nvrtcCreateProgram(b"invalid code!@#$", b"test.cu", 0, [], [])
116+
assert err == nvrtc.nvrtcResult.NVRTC_SUCCESS
117+
try:
118+
(compile_result,) = nvrtc.nvrtcCompileProgram(prog, 0, [])
119+
assert compile_result != nvrtc.nvrtcResult.NVRTC_SUCCESS
120+
with pytest.raises(cuda_utils.NVRTCError, match="compilation log"):
121+
cuda_utils._check_nvrtc_error(compile_result, handle=prog)
122+
finally:
123+
nvrtc.nvrtcDestroyProgram(prog)
124+
125+
126+
def test_check_or_create_options_invalid_type():
127+
with pytest.raises(TypeError, match="must be provided as an object"):
128+
cuda_utils.check_or_create_options(_DummyOptions, 12345, options_description="test options")
129+
130+
131+
def test_assert_type_str_or_bytes_like_rejects_non_str_bytes():
132+
with pytest.raises(TypeError, match="Expected type str or bytes or bytearray"):
133+
assert_type_str_or_bytes_like(12345)
134+
135+
136+
def test_raise_code_path_meant_to_be_unreachable():
137+
with pytest.raises(RuntimeError, match="This code path is meant to be unreachable"):
138+
raise_code_path_meant_to_be_unreachable()

cuda_core/tests/test_launcher.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

44
import ctypes
@@ -382,3 +382,10 @@ def test_launch_with_buffers_allocated_by_memory_resource(init_cuda, memory_reso
382382

383383
# Verify buffer is properly closed
384384
assert buffer.handle == 0, f"{name} buffer should be closed"
385+
386+
387+
def test_kernel_arg_unsupported_type():
388+
from cuda.core._kernel_arg_handler import ParamHolder
389+
390+
with pytest.raises(TypeError, match="unsupported type"):
391+
ParamHolder(["not_a_valid_kernel_arg"])

cuda_core/tests/test_linker.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
33
# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
44

@@ -207,3 +207,17 @@ def test_linker_options_as_bytes_driver_not_supported():
207207
options = LinkerOptions(arch="sm_80")
208208
with pytest.raises(RuntimeError, match="as_bytes\\(\\) only supports 'nvjitlink' backend"):
209209
options.as_bytes("driver")
210+
211+
212+
def test_linker_logs_cached_after_link(compile_ptx_functions):
213+
"""After a successful link(), get_error_log/get_info_log should return cached strings."""
214+
options = LinkerOptions(arch=ARCH)
215+
linker = Linker(*compile_ptx_functions, options=options)
216+
linker.link("cubin")
217+
err_log = linker.get_error_log()
218+
info_log = linker.get_info_log()
219+
assert isinstance(err_log, str)
220+
assert isinstance(info_log, str)
221+
# Calling again should return the same observable values.
222+
assert linker.get_error_log() == err_log
223+
assert linker.get_info_log() == info_log

cuda_core/tests/test_memory.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,18 @@ def test_graph_memory_resource_object(init_cuda):
15011501
assert gmr1 == gmr2 == gmr3
15021502

15031503

1504+
@pytest.mark.skipif(np is None, reason="numpy is not installed")
1505+
def test_strided_memory_view_dlpack_errors():
1506+
arr = np.zeros(64, dtype=np.uint8)
1507+
smv = StridedMemoryView.from_any_interface(arr, stream_ptr=-1)
1508+
with pytest.raises(BufferError, match="dl_device other than None"):
1509+
smv.__dlpack__(dl_device=())
1510+
with pytest.raises(BufferError, match="copy=True"):
1511+
smv.__dlpack__(copy=True)
1512+
with pytest.raises(BufferError, match="Expected max_version"):
1513+
smv.__dlpack__(max_version=(9, 8, 7))
1514+
1515+
15041516
def test_memory_resource_alloc_zero_bytes(init_cuda, memory_resource_factory):
15051517
MR, MROps = memory_resource_factory
15061518

cuda_core/tests/test_multiprocessing_warning.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

44
"""
@@ -16,7 +16,7 @@
1616
from cuda.core._event import _reduce_event
1717
from cuda.core._memory._device_memory_resource import _deep_reduce_device_memory_resource
1818
from cuda.core._memory._ipc import _reduce_allocation_handle
19-
from cuda.core._utils.cuda_utils import reset_fork_warning
19+
from cuda.core._utils.cuda_utils import check_multiprocessing_start_method, reset_fork_warning
2020

2121

2222
def test_warn_on_fork_method_device_memory_resource(ipc_device):
@@ -145,3 +145,19 @@ def test_warning_emitted_only_once(ipc_device):
145145

146146
mr1.close()
147147
mr2.close()
148+
149+
150+
def test_warn_on_unset_start_method_linux():
151+
"""Test warning when get_start_method raises RuntimeError on Linux (unset start method)."""
152+
with (
153+
patch("multiprocessing.get_start_method", side_effect=RuntimeError),
154+
patch("platform.system", return_value="Linux"),
155+
warnings.catch_warnings(record=True) as w,
156+
):
157+
warnings.simplefilter("always")
158+
reset_fork_warning()
159+
check_multiprocessing_start_method()
160+
161+
fork_warnings = [x for x in w if "fork" in str(x.message).lower()]
162+
assert len(fork_warnings) == 1
163+
assert "not set" in str(fork_warnings[0].message).lower()

cuda_core/tests/test_program.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,19 @@ def test_program_compile_invalid_target_type():
427427
program.compile("invalid_target")
428428

429429

430+
def test_nvrtc_compile_invalid_code(init_cuda):
431+
"""Compiling invalid C++ exercises the HANDLE_RETURN_NVRTC error path with compilation log."""
432+
from cuda.core._utils.cuda_utils import NVRTCError
433+
434+
code = 'extern "C" __global__ void bad_kernel() { this_symbol_is_undefined(); }'
435+
program = Program(code, "c++")
436+
try:
437+
with pytest.raises(NVRTCError, match="compilation log"):
438+
program.compile("ptx")
439+
finally:
440+
program.close()
441+
442+
430443
def test_program_backend_property():
431444
code = 'extern "C" __global__ void my_kernel() {}'
432445
program = Program(code, "c++")
@@ -481,6 +494,20 @@ def test_nvvm_compile_invalid_target(nvvm_ir):
481494
program.close()
482495

483496

497+
@nvvm_available
498+
def test_nvvm_compile_invalid_ir():
499+
"""Compiling invalid NVVM IR exercises the HANDLE_RETURN_NVVM error path."""
500+
from cuda.bindings.nvvm import nvvmError
501+
502+
bad_ir = "this is not valid NVVM IR"
503+
program = Program(bad_ir, "nvvm")
504+
try:
505+
with pytest.raises(nvvmError):
506+
program.compile("ptx")
507+
finally:
508+
program.close()
509+
510+
484511
@nvvm_available
485512
@pytest.mark.parametrize("target_type", ["ptx", "ltoir"])
486513
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)