Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cuda_core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ To run these tests:
* `python -m pytest tests/` with editable installations
* `pytest tests/` with installed packages

Alternatively, from the repository root you can use a simple script:
Comment thread
rparolin marked this conversation as resolved.

* `./toolshed/run_all_tests.sh core` to run only `cuda_core` tests
* `./toolshed/run_all_tests.sh` to run all package tests (pathfinder → bindings → core)
* `./toolshed/run_all_tests.sh smoke` to run meta-level smoke tests under `tests/integration`

### Cython Unit Tests

Cython tests are located in `tests/cython` and need to be built. These builds have the same CUDA Toolkit header requirements as [those of cuda.bindings](https://nvidia.github.io/cuda-python/cuda-bindings/latest/install.html#requirements) where the major.minor version must match `cuda.bindings`. To build them:
Expand Down
22 changes: 22 additions & 0 deletions tests/integration/test_smoke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

import importlib


def test_import_cuda_top_level():
m = importlib.import_module("cuda")
assert hasattr(m, "__file__")
Comment thread
rparolin marked this conversation as resolved.
Outdated


def test_import_subpackages_present_if_installed():
Comment thread
rparolin marked this conversation as resolved.
Outdated
for name in [
"cuda.core",
"cuda.bindings",
"cuda.pathfinder",
]:
try:
importlib.import_module(name)
except ModuleNotFoundError:
pass
264 changes: 264 additions & 0 deletions toolshed/run_all_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
#!/usr/bin/env bash
Comment thread
rparolin marked this conversation as resolved.

# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail

# Simple, dependency-free orchestrator to run tests for all packages.
# Usage:
# toolshed/run_all_tests.sh [ -v|--verbose ] [ --install | --no-install ] [ --with-cython | --skip-cython ]
# toolshed/run_all_tests.sh [ flags ] # pathfinder -> bindings -> core
# toolshed/run_all_tests.sh [ flags ] core # only core
# toolshed/run_all_tests.sh [ flags ] bindings # only bindings
# toolshed/run_all_tests.sh [ flags ] pathfinder # only pathfinder
# toolshed/run_all_tests.sh [ flags ] smoke # meta-level import smoke tests

repo_root=$(cd "$(dirname "$0")/.." && pwd)
cd "${repo_root}"

print_help() {
cat <<'USAGE'
Usage: toolshed/run_all_tests.sh [options] [target]

Targets:
all (default) Run pathfinder → bindings → core
core Run cuda_core tests
bindings Run cuda_bindings tests
pathfinder Run cuda_pathfinder tests
smoke Run meta-level smoke tests (tests/integration)
Comment thread
rparolin marked this conversation as resolved.

Options:
-v, --verbose Verbose pytest output (-ra -s -v)
--install Force editable install with [test] extras

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit out of scope for a script that runs tests. Personally I would be surprised if a script called run_test_all started installing stuff and assumed that pip install -e . is a thing from wherever I ran it.

Installing dependencies seems like a separate concern from running tests.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was on the fence about this but I was wanting this to be "one stop shop" script that handles everything a user would need when running from a clean branch.

--no-install Skip install checks (assume environment is ready)
--with-cython Build and run cython tests (needs CUDA_HOME for core)
--skip-cython Skip cython tests (default)
-h, --help Show this help and exit

Examples:
toolshed/run_all_tests.sh --install
toolshed/run_all_tests.sh --no-install core
toolshed/run_all_tests.sh -v --with-cython bindings
toolshed/run_all_tests.sh smoke
USAGE
}

# Parse optional flags
VERBOSE=0
RUN_CYTHON=0
INSTALL_MODE=auto # auto|force|skip
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
print_help
exit 0
;;
-v|--verbose)
VERBOSE=1
shift
;;
--install)
INSTALL_MODE=force
shift
;;
--no-install)
INSTALL_MODE=skip
shift
;;
--with-cython)
RUN_CYTHON=1
shift
;;
--skip-cython)
RUN_CYTHON=0
shift
;;
*)
break
;;
esac
done

target=${1:-all}

if [[ ${VERBOSE} -eq 1 ]]; then
PYTEST_FLAGS=( -ra -s -v )
else
# Very quiet: show failures/errors summary only
PYTEST_FLAGS=( -qq )
fi

declare -A RESULTS
ORDERED_RESULTS=()

add_result() {
local name="$1"; shift
local rc="$1"; shift
RESULTS["${name}"]="${rc}"
ORDERED_RESULTS+=("${name}")
}

status_from_rc() {
local rc="$1"
case "${rc}" in
0) echo "PASS" ;;
5) echo "SKIP(no-tests)" ;;
1) echo "FAIL" ;;
2) echo "INTERRUPTED" ;;
3) echo "ERROR" ;;
4) echo "USAGE" ;;
*) echo "RC=${rc}" ;;
esac
}

run_pytest() {
# Run pytest safely under set -e and return its exit code
set +e
pytest "${PYTEST_FLAGS[@]}" "$@"
local rc=$?
set -e
return ${rc}
}

ensure_installed() {
# Args: module.import.name repo_subdir
local mod_name="$1"; shift
local subdir_name="$1"; shift

if [[ "${INSTALL_MODE}" == "skip" ]]; then
return 0
fi

if [[ "${INSTALL_MODE}" == "force" ]]; then
pip install -e .[test]
return 0
fi

# auto-detect: if module imports from this repo, assume installed; otherwise install
python - <<PY 2>/dev/null
import importlib, sys, pathlib
mod = "${mod_name}"
try:
m = importlib.import_module(mod)
except Exception:
sys.exit(2)
p = pathlib.Path(getattr(m, "__file__", "")).resolve()
root = pathlib.Path(r"${repo_root}").resolve()
sub = pathlib.Path(r"${repo_root}/${subdir_name}").resolve()
print(p)
sys.exit(0 if str(p).startswith(str(sub)) else 3)
PY
rc=$?
if [[ $rc -ne 0 ]]; then
pip install -e .[test]
fi
}

run_pathfinder() {
echo "[tests] cuda_pathfinder"
cd "${repo_root}/cuda_pathfinder"
ensure_installed "cuda.pathfinder" "cuda_pathfinder"
run_pytest tests/
local rc=$?
add_result "pathfinder" "${rc}"
}

run_bindings() {
echo "[tests] cuda_bindings"
cd "${repo_root}/cuda_bindings"
ensure_installed "cuda.bindings" "cuda_bindings"
run_pytest tests/
local rc=$?
add_result "bindings" "${rc}"
if [ ${RUN_CYTHON} -eq 1 ] && [ -d tests/cython ]; then
if [ -x tests/cython/build_tests.sh ]; then
echo "[build] cuda_bindings cython tests"
( cd tests/cython && ./build_tests.sh ) || true
fi
run_pytest tests/cython
local rc_cy=$?
add_result "bindings-cython" "${rc_cy}"
fi
}

run_core() {
echo "[tests] cuda_core"
cd "${repo_root}/cuda_core"
ensure_installed "cuda.core" "cuda_core"
run_pytest tests/
local rc=$?
add_result "core" "${rc}"
if [ ${RUN_CYTHON} -eq 1 ] && [ -d tests/cython ]; then
if [ -x tests/cython/build_tests.sh ]; then
echo "[build] cuda_core cython tests"
if [ -z "${CUDA_HOME-}" ]; then
echo "[skip] CUDA_HOME not set; skipping cython tests"
else
( cd tests/cython && ./build_tests.sh ) || true
fi
fi
run_pytest tests/cython
local rc_cy=$?
add_result "core-cython" "${rc_cy}"
fi
}

run_smoke() {
echo "[tests] meta-level smoke"
cd "${repo_root}"
python - <<PY 2>/dev/null || pip install pytest>=6.2.4
import pytest
PY
run_pytest tests/integration
local rc=$?
add_result "smoke" "${rc}"
}

case "${target}" in
all)
run_pathfinder
run_bindings
run_core
;;
core)
run_core ;;
bindings)
run_bindings ;;
pathfinder)
run_pathfinder ;;
smoke)
run_smoke ;;
*)
echo "Unknown target: ${target}" >&2
exit 1
;;
esac

# Print summary
echo
echo "==================== Test Summary ===================="
overall_rc=0
if [ -t 1 ]; then
GREEN=$(printf '\033[32m')
RED=$(printf '\033[31m')
RESET=$(printf '\033[0m')
else
GREEN=""; RED=""; RESET=""
fi
for name in "${ORDERED_RESULTS[@]}"; do
rc="${RESULTS[$name]}"
status=$(status_from_rc "${rc}")
color=""
case "${status}" in
PASS) color="${GREEN}" ;;
FAIL|ERROR|INTERRUPTED|USAGE|RC=*) color="${RED}" ;;
*) color="" ;;
esac
printf "%-18s : %s%s%s\n" "${name}" "${color}" "${status}" "${RESET}"
if [[ "${rc}" -ne 0 && "${rc}" -ne 5 ]]; then
overall_rc=1
fi
done
echo "======================================================"
exit ${overall_rc}