Skip to content

Commit b2aabfe

Browse files
committed
test: harden nvrtc regression test with PATH-based failure detection
Also load nvrtc with the normal environment first. If the normal load finds nvrtc in a directory on PATH but the CUDA_HOME/CUDA_PATH-stripped load fails, the test now fails instead of skipping — directly catching the Windows bug where SetDefaultDllDirectories excludes PATH from LoadLibraryExW. Made-with: Cursor
1 parent 2f024a5 commit b2aabfe

1 file changed

Lines changed: 60 additions & 29 deletions

File tree

‎cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py‎

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -146,25 +146,42 @@ def raise_child_process_failed():
146146
def test_load_nvrtc_without_cuda_home_or_cuda_path(info_summary_append):
147147
"""Regression test for issue #1781: nvrtc must load without CUDA_HOME/CUDA_PATH.
148148
149-
On Windows, when CUDA DLLs are discovered via PATH (system search), the
150-
previous LoadLibraryExW(flags=0) call would find the DLL but fail to
151-
resolve co-located dependencies like nvrtc-builtins (error 126).
152-
153-
The fix uses SearchPathW to resolve the full path, then loads with
154-
LOAD_WITH_ALTERED_SEARCH_PATH so dependency search starts from the
155-
DLL's directory.
156-
157-
This test strips CUDA_HOME and CUDA_PATH, then loads nvrtc in a fresh
158-
subprocess. In CI environments where nvrtc is only available via system
159-
search, this exercises the exact code path that was broken.
149+
On Windows, Python 3.8+ calls SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
150+
at startup, which excludes PATH from the LoadLibraryExW search order. The fix uses
151+
SearchPathW (unaffected by SetDefaultDllDirectories) to locate the DLL via PATH, then
152+
loads it by absolute path with LOAD_WITH_ALTERED_SEARCH_PATH.
153+
154+
This test loads nvrtc twice in fresh subprocesses: once with the normal environment,
155+
once with CUDA_HOME and CUDA_PATH stripped. If the normal load finds nvrtc in a
156+
directory on PATH, the stripped load must also succeed — otherwise the system search
157+
is broken.
160158
"""
159+
timeout = 120 if IS_WINDOWS else 30
160+
161+
# Phase 1: load nvrtc with normal environment.
162+
normal_result = run_load_nvidia_dynamic_lib_in_subprocess("nvrtc", timeout=timeout)
163+
if normal_result.returncode != 0:
164+
raise RuntimeError(build_child_process_failed_for_libname_message("nvrtc", normal_result))
165+
assert not normal_result.stderr
166+
normal_payload = parse_dynamic_lib_subprocess_payload(
167+
normal_result.stdout,
168+
libname="nvrtc",
169+
error_label="Load subprocess child process (normal env)",
170+
)
171+
if normal_payload.status == STATUS_NOT_FOUND:
172+
info_summary_append("nvrtc not found (normal env)")
173+
pytest.skip("nvrtc not available in this environment")
174+
normal_abs_path = normal_payload.abs_path
175+
assert normal_abs_path is not None
176+
assert os.path.isfile(normal_abs_path)
177+
info_summary_append(f"nvrtc (normal env): abs_path={quote_for_shell(normal_abs_path)}")
178+
179+
# Phase 2: load nvrtc without CUDA_HOME/CUDA_PATH.
161180
env = os.environ.copy()
162181
env.pop("CUDA_HOME", None)
163182
env.pop("CUDA_PATH", None)
164-
165-
timeout = 120 if IS_WINDOWS else 30
166183
command = build_dynamic_lib_subprocess_command(LOAD_NVIDIA_DYNAMIC_LIB_SUBPROCESS_MODE, "nvrtc")
167-
result = subprocess.run( # noqa: S603
184+
stripped_result = subprocess.run( # noqa: S603
168185
command,
169186
capture_output=True,
170187
text=True,
@@ -173,22 +190,36 @@ def test_load_nvrtc_without_cuda_home_or_cuda_path(info_summary_append):
173190
env=env,
174191
cwd=LOAD_NVIDIA_DYNAMIC_LIB_SUBPROCESS_CWD,
175192
)
176-
177-
if result.returncode != 0:
178-
raise RuntimeError(build_child_process_failed_for_libname_message("nvrtc", result))
179-
assert not result.stderr
180-
181-
payload = parse_dynamic_lib_subprocess_payload(
182-
result.stdout,
193+
if stripped_result.returncode != 0:
194+
raise RuntimeError(build_child_process_failed_for_libname_message("nvrtc", stripped_result))
195+
assert not stripped_result.stderr
196+
stripped_payload = parse_dynamic_lib_subprocess_payload(
197+
stripped_result.stdout,
183198
libname="nvrtc",
184199
error_label="Load subprocess child process (no CUDA_HOME/CUDA_PATH)",
185200
)
186201

187-
if payload.status == STATUS_NOT_FOUND:
188-
info_summary_append("nvrtc not found without CUDA_HOME/CUDA_PATH")
189-
pytest.skip("nvrtc not available without CUDA_HOME/CUDA_PATH")
190-
191-
abs_path = payload.abs_path
192-
assert abs_path is not None
193-
info_summary_append(f"nvrtc (no CUDA_HOME/CUDA_PATH): abs_path={quote_for_shell(abs_path)}")
194-
assert os.path.isfile(abs_path)
202+
# Phase 3: evaluate.
203+
if stripped_payload.status != STATUS_NOT_FOUND:
204+
stripped_abs_path = stripped_payload.abs_path
205+
assert stripped_abs_path is not None
206+
assert os.path.isfile(stripped_abs_path)
207+
info_summary_append(f"nvrtc (no CUDA_HOME/CUDA_PATH): abs_path={quote_for_shell(stripped_abs_path)}")
208+
return
209+
210+
# nvrtc was found normally but not without CUDA_HOME/CUDA_PATH.
211+
# If the DLL's directory is on PATH, the system search should have found it.
212+
dll_dir = os.path.normcase(os.path.normpath(os.path.dirname(normal_abs_path)))
213+
on_path = any(
214+
os.path.normcase(os.path.normpath(d)) == dll_dir for d in os.environ.get("PATH", "").split(os.pathsep) if d
215+
)
216+
if on_path:
217+
pytest.fail(
218+
f"nvrtc was found at {normal_abs_path!r} (directory is on PATH) "
219+
f"but could not be loaded without CUDA_HOME/CUDA_PATH. "
220+
f"System search should find DLLs in PATH directories."
221+
)
222+
info_summary_append(
223+
f"nvrtc (no CUDA_HOME/CUDA_PATH): not found "
224+
f"(normal-env directory not on PATH: {quote_for_shell(os.path.dirname(normal_abs_path))})"
225+
)

0 commit comments

Comments
 (0)