-
Notifications
You must be signed in to change notification settings - Fork 276
Expand file tree
/
Copy pathtest_build_hooks.py
More file actions
137 lines (110 loc) · 5.26 KB
/
test_build_hooks.py
File metadata and controls
137 lines (110 loc) · 5.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Tests for build_hooks.py build infrastructure.
These tests verify the CUDA version detection logic used during builds,
particularly the _determine_cuda_major_version() function which derives the
CUDA major version from headers.
Note: These tests do NOT require cuda.core to be built/installed since they
test build-time infrastructure. Run with --noconftest to avoid loading
conftest.py which imports cuda.core modules:
pytest tests/test_build_hooks.py -v --noconftest
These tests require Cython to be installed (build_hooks.py imports it).
"""
import importlib.util
import os
import tempfile
from pathlib import Path
from unittest import mock
import pytest
from cuda.pathfinder import get_cuda_path_or_home
# build_hooks.py imports Cython and setuptools at the top level, so skip if not available
pytest.importorskip("Cython")
pytest.importorskip("setuptools")
def _load_build_hooks():
"""Load build_hooks module from source without permanently modifying sys.path.
build_hooks.py is a PEP 517 build backend, not an installed module.
We use importlib to load it directly from source to avoid polluting
sys.path with the cuda_core/ directory (which contains cuda/core/ source
that could shadow the installed package).
"""
build_hooks_path = Path(__file__).parent.parent / "build_hooks.py"
spec = importlib.util.spec_from_file_location("build_hooks", build_hooks_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
# Load the module once at import time
build_hooks = _load_build_hooks()
def _check_version_detection(
cuda_version, expected_major, *, use_cuda_path=True, use_cuda_home=False, cuda_core_build_major=None
):
"""Test version detection with a mock cuda.h.
Args:
cuda_version: CUDA_VERSION to write in mock cuda.h (e.g., 12080)
expected_major: Expected return value (e.g., "12")
use_cuda_path: If True, set CUDA_PATH to the mock headers directory
use_cuda_home: If True, set CUDA_HOME to the mock headers directory
cuda_core_build_major: If set, override with this CUDA_CORE_BUILD_MAJOR env var
"""
with tempfile.TemporaryDirectory() as tmpdir:
include_dir = Path(tmpdir) / "include"
include_dir.mkdir()
cuda_h = include_dir / "cuda.h"
cuda_h.write_text(f"#define CUDA_VERSION {cuda_version}\n")
build_hooks._get_cuda_path.cache_clear()
build_hooks._determine_cuda_major_version.cache_clear()
get_cuda_path_or_home.cache_clear()
mock_env = {
k: v
for k, v in {
"CUDA_CORE_BUILD_MAJOR": cuda_core_build_major,
"CUDA_PATH": tmpdir if use_cuda_path else None,
"CUDA_HOME": tmpdir if use_cuda_home else None,
}.items()
if v is not None
}
with mock.patch.dict(os.environ, mock_env, clear=True):
result = build_hooks._determine_cuda_major_version()
assert result == expected_major
class TestGetCudaMajorVersion:
"""Tests for _determine_cuda_major_version()."""
@pytest.mark.parametrize("version", ["11", "12", "13", "14"])
def test_env_var_override(self, version):
"""CUDA_CORE_BUILD_MAJOR env var override works with various versions."""
build_hooks._get_cuda_path.cache_clear()
build_hooks._determine_cuda_major_version.cache_clear()
get_cuda_path_or_home.cache_clear()
with mock.patch.dict(os.environ, {"CUDA_CORE_BUILD_MAJOR": version}, clear=False):
result = build_hooks._determine_cuda_major_version()
assert result == version
@pytest.mark.parametrize(
("cuda_version", "expected_major"),
[
(11000, "11"), # CUDA 11.0
(11080, "11"), # CUDA 11.8
(12000, "12"), # CUDA 12.0
(12020, "12"), # CUDA 12.2
(12080, "12"), # CUDA 12.8
(13000, "13"), # CUDA 13.0
(13010, "13"), # CUDA 13.1
],
ids=["11.0", "11.8", "12.0", "12.2", "12.8", "13.0", "13.1"],
)
def test_cuda_headers_parsing(self, cuda_version, expected_major):
"""CUDA_VERSION is correctly parsed from cuda.h headers."""
_check_version_detection(cuda_version, expected_major)
def test_cuda_home_fallback(self):
"""CUDA_HOME is used if CUDA_PATH is not set."""
_check_version_detection(12050, "12", use_cuda_path=False, use_cuda_home=True)
def test_env_var_takes_priority_over_headers(self):
"""Env var override takes priority even when headers exist."""
_check_version_detection(12080, "11", cuda_core_build_major="11")
def test_missing_cuda_path_raises_error(self):
"""RuntimeError is raised when CUDA_PATH/CUDA_HOME not set and no env var override."""
build_hooks._get_cuda_path.cache_clear()
build_hooks._determine_cuda_major_version.cache_clear()
get_cuda_path_or_home.cache_clear()
with (
mock.patch.dict(os.environ, {}, clear=True),
pytest.raises(RuntimeError, match="CUDA_PATH or CUDA_HOME"),
):
build_hooks._determine_cuda_major_version()