Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
3 changes: 1 addition & 2 deletions projects/rocblas/clients/gtest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,7 @@ if(ROCBLAS_HAS_CTEST_CATEGORIES)
rocblas-test
"${CMAKE_CURRENT_SOURCE_DIR}/test_categories.yaml"
"${PROJECT_BINARY_DIR}/staging"
INSTALL_TEST_FILE "${ROCBLAS_CTEST_INSTALL_FILE}"
USE_RTEST_DRIVER
"${ROCBLAS_CTEST_INSTALL_FILE}"
)
elseif(BUILD_CLIENTS_TESTS AND NOT ROCBLAS_ENABLE_CTEST)
message(STATUS "rocBLAS: ROCBLAS_ENABLE_CTEST=OFF (CTest category labels not applied)")
Expand Down
6 changes: 4 additions & 2 deletions projects/rocblas/clients/gtest/test_categories.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
# *quick* without *strided* is a proxy for smoke set until we use the smoke yaml.
test_categories:
quick:
description: "Smoke - rocblas_smoke.yaml with optional gtest filter refinements (timeout: 30 min)"
test_yaml: rocblas_smoke.yaml
description: "Smoke - not the same as rocblas_smoke.yaml TODO (timeout: 30 min)"
test_patterns:
- "*quick*"
exclude:
- "*strided*"
- "*known_bug*"
labels:
- "quick"
- "smoke"
Expand Down
49 changes: 14 additions & 35 deletions projects/rocblas/rtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import sys
import subprocess
import shlex
import shutil
import argparse
import pathlib
import platform
Expand All @@ -49,8 +48,7 @@ def parse_args():
Checks build arguments
""")
parser.add_argument( '--ci_labels', type=str, required=False, default="",
help='Semicolon-separated labels that may modify test runs (e.g. "gfx12;TestLevel1Only"). '
'If omitted, the GITHUB_PR_LABELS environment variable is used when set (e.g. CI PR labels).')
help='Semi-colon seperated list of labels that may modify test runs (optional, e.g. "gfx12;TestLevel1Only")')
parser.add_argument( '--ci_gfx', type=str, required=False, default="",
help='Semi-colon seperated list of gfx targets expected on test runs (optional, e.g. "gfx1030;gfx1201")')
parser.add_argument('-e', '--emulation', type=str, required=False,
Expand All @@ -73,36 +71,24 @@ def arg_into_list(arg) -> list:
arg = re.sub(r"['\"]|['\']",'', arg)
return arg.split(';')

def rocm_executable(exe_name: str) -> str:
if shutil.which(exe_name):
return exe_name
bin_dir = os.environ.get("ROCM_PATH") or os.environ.get("HIP_PATH")
if bin_dir:
candidate = pathlib.Path(bin_dir) / "bin" / exe_name
if candidate.exists():
return str(candidate)
return None

def vram_detect():
global OS_info
OS_info["VRAM"] = 0
if os.name == "nt":
cmd = rocm_executable("hipinfo.exe")
if cmd is not None:
process = subprocess.run([cmd], stdout=subprocess.PIPE)
for line_in in process.stdout.decode().splitlines():
if 'totalGlobalMem' in line_in:
OS_info["VRAM"] = float(line_in.split()[1])
break
cmd = "hipinfo.exe"
process = subprocess.run([cmd], stdout=subprocess.PIPE)
for line_in in process.stdout.decode().splitlines():
if 'totalGlobalMem' in line_in:
OS_info["VRAM"] = float(line_in.split()[1])
break
else:
cmd = rocm_executable("rocminfo")
if cmd is not None:
process = subprocess.run([cmd], stdout=subprocess.PIPE)
for line_in in process.stdout.decode().splitlines():
match = re.search(r'.*Size:.*([0-9]+)\(.*\).*KB', line_in, re.IGNORECASE)
if match:
OS_info["VRAM"] = float(match.group(1))/(1024*1024)
break
cmd = "rocminfo"
process = subprocess.run([cmd], stdout=subprocess.PIPE)
for line_in in process.stdout.decode().splitlines():
match = re.search(r'.*Size:.*([0-9]+)\(.*\).*KB', line_in, re.IGNORECASE)
if match:
OS_info["VRAM"] = float(match.group(1))/(1024*1024)
break

def os_detect():
global OS_info
Expand Down Expand Up @@ -419,13 +405,6 @@ def main():
os_detect()
args = parse_args()

# PR / CI labels (e.g. GitHub Actions): semicolon-separated, same format as --ci_labels.
# When --ci_labels is omitted, use GITHUB_PR_LABELS from the environment (e.g. set by CI before ctest).
if not args.ci_labels:
env_labels = os.environ.get("GITHUB_PR_LABELS", "").strip()
if env_labels:
args.ci_labels = env_labels

if args.emulation:
args.test = f'emulation_{args.emulation}'

Expand Down
14 changes: 0 additions & 14 deletions projects/rocblas/rtest.xml
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@

<testset>
<var name="TEST_BASE_OPTIONS" value="rocblas-test --gtest_output=xml --gtest_color=yes"></var>
<!-- CTest category suites (see clients/gtest/test_categories.yaml); driven by rocblas_rtest.py -t ctest_* -->
<test sets="ctest_quick">
<run name="ctest quick">{TEST_BASE_OPTIONS} --yaml rocblas_smoke.yaml</run>
</test>
<test sets="ctest_standard">
<run name="ctest standard">{TEST_BASE_OPTIONS} --gtest_filter=*quick*:*pre_checkin*-*known_bug*</run>
</test>
<test sets="ctest_comprehensive">
<run name="ctest comprehensive">{TEST_BASE_OPTIONS} --gtest_filter=*nightly*-*known_bug*</run>
</test>
<test sets="ctest_full">
<run name="ctest full">{TEST_BASE_OPTIONS} --gtest_filter=*quick*:*pre_checkin*:*nightly*-*known_bug*</run>
</test>
<!-- math-CI or devops old runs -->
<test sets="psdb">
<run name="quick_pre_checkin">{TEST_BASE_OPTIONS} --gtest_filter=*quick*:*pre_checkin*-*known_bug*</run>
</test>
Expand Down
28 changes: 8 additions & 20 deletions shared/ctest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Defines test organization:
Python script that:
- Parses YAML configuration files
- Detects runtime environment (OS, GPU architecture)
- Builds gtest filter strings with positive and negative patterns (default), or emits commands to run an **rtest** driver when **`--use-rtest-driver`** is passed
- Builds gtest filter strings with positive and negative patterns
- Generates CMake test registration code with proper exclusion syntax

#### 3. **TestCategories.cmake** (Shared)
Expand Down Expand Up @@ -135,19 +135,14 @@ execution_settings:

**Environment (optional):** Under `execution_settings`, an `environment` map sets env vars for all category tests (e.g. `OPENBLAS_NUM_THREADS`, `OMP_NUM_THREADS`). Keys and values are strings; they are passed to CTest as `ENVIRONMENT "VAR1=val1;VAR2=val2"`.

**Test YAML (optional, per-category):** A category may set `test_yaml` to a basename (e.g. `rocblas_smoke.yaml`). For direct gtest invocations, the parser injects `--yaml <file>` **before** `--gtest_filter=...`. A category may define `test_yaml` only, `test_patterns` only, or both. When `--use-rtest-driver` is enabled, the project's rtest XML must still define matching `--yaml` / filter behavior for `ctest_<category>` suites.

**Extra arguments (optional, per-category):** A category may set `extra_args` to a list (or single string) of additional command-line arguments that the parser appends to the test command after `--gtest_filter=...` (or after `--yaml` when no filter is used). Useful for projects whose test binary accepts runtime flags beyond gtest filtering (for example, a flag to select a reduced subset of tests, or to scale a sampling/iteration parameter). Each entry is shell-quoted with `shlex.quote`, so values with spaces or shell metacharacters are preserved as a single argument by CTest.
**Extra arguments (optional, per-category):** A category may set `extra_args` to a list (or single string) of additional command-line arguments that the parser appends to the test command after `--gtest_filter=...`. Useful for projects whose test binary accepts runtime flags beyond gtest filtering (for example, a flag to select a reduced subset of tests, or to scale a sampling/iteration parameter). Each entry is shell-quoted with `shlex.quote`, so values with spaces or shell metacharacters are preserved as a single argument by CTest.

```yaml
test_categories:
quick:
test_yaml: rocblas_smoke.yaml
test_patterns:
- "*quick*"
exclude:
- "*strided*"
extra_args: # appended after --gtest_filter (or after --yaml)
- "*"
extra_args: # appended after --gtest_filter
- "--quick-subset"
labels:
- "quick"
Expand Down Expand Up @@ -577,7 +572,7 @@ Each test gets labels that enable flexible CTest filtering:

## Function Reference

### `apply_test_category_labels(target_name yaml_file working_dir [install_test_file [resource_group [parser_extra]]])`
### `apply_test_category_labels(target_name yaml_file working_dir [install_test_file])`


| Parameter | Required | Description |
Expand All @@ -587,15 +582,8 @@ Each test gets labels that enable flexible CTest filtering:
| `working_dir` | Yes | Working directory for test execution (must be a valid directory) |
| `install_test_file` | No | Path to the generated install-time `CTestTestfile.cmake`. Not required for local builds, but |
| | | required for install/package workflows (for example, TheRock). |
| `resource_group` | No | CTest `RESOURCE_GROUPS` token; when set, suite names include `_<resource>` after the target name. |
| `parser_extra` | No | Extra argument(s) forwarded to `parse_test_categories.py` (for example `--use-rtest-driver`). |

**Validation:** The function validates all inputs before execution and emits `WARNING` messages if any check fails, returning early without generating tests.

### Optional rtest driver (`--use-rtest-driver`)

By default, generated CTest suites run the **gtest executable** with `--gtest_filter=...`. If the project passes **`--use-rtest-driver`** as the optional **`parser_extra`** argument to `apply_test_category_labels()`, the parser instead emits commands to run the **rtest** Python driver next to the binary. The driver basename is derived from the CTest name prefix (typically `mylib-test` → `mylib_rtest.py`). The driver is invoked as `<python> <stem>_rtest.py -t ctest_<category>` using a platform-appropriate interpreter on `PATH` at CTest run time (`python3` on Linux/macOS, `python` on Windows—not configure-time `Python3_EXECUTABLE`, which may be absent on CI test nodes). **`--cmake-python3`** may be passed explicitly to the parser to override the install-tree interpreter. PR-style labels are read at rtest run time from the **`GITHUB_PR_LABELS`** environment variable (semicolon-separated, same format as rtest’s `--ci_labels`) when that variable is set and `--ci_labels` was not passed on the command line. CTest **`LABELS`** on the suite are unchanged (for `ctest -L`). The project’s rtest XML must define matching `<test sets="ctest_<category>">` entries. GPU-specific exclusion suites still invoke the gtest binary directly. In **rocm-libraries**, only **rocBLAS** enables this via `USE_RTEST_DRIVER` in `projects/rocblas/clients/gtest/CMakeLists.txt`.

## YAML Reference

### `test_categories` (required)
Expand Down Expand Up @@ -663,11 +651,11 @@ When tests are installed (e.g. into `/opt/rocm/bin/`), the **build-tree** test d

**How it works:**

1. **Project enables it** by passing an optional **4th argument** to `apply_test_category_labels()`: a path to a file (e.g. `install_CTestTestfile.cmake`) that the parser will create or append to. The parser writes `add_test(...)` and `set_tests_properties(...)` lines into that file using a **relative** command (e.g. `"../my_project_gtest"`), so the test binary is found relative to the directory where the file will live after install.
1. **Project enables it** by passing an optional **4th argument** to `apply_test_category_labels()`: a path to a file (e.g. `install_CTestTestfile.cmake`) that the parser will create or append to. The parser writes `add_test(...)` and `set_tests_properties(...)` lines into that file using a **relative** command (e.g. `"../rocblas-test"`), so the test binary is found relative to the directory where the file will live after install.

2. **Project installs that file** to a fixed location under the install prefix, typically a **project-specific subdirectory** of the bin dir (e.g. `bin/my_project/`) and renames it to `CTestTestfile.cmake`. The test executable is installed in the parent `bin/` directory, so from `bin/my_project/` the path `../my_project_gtest` correctly points at the installed binary.
2. **Project installs that file** to a fixed location under the install prefix, typically a **project-specific subdirectory** of the bin dir (e.g. `bin/rocblas/` or `bin/MIOpen/`) and renames it to `CTestTestfile.cmake`. The test executable is installed in the parent `bin/` directory, so from `bin/rocblas/` the path `../rocblas-test` correctly points at the installed binary.

3. **TheRock (or any consumer)** runs CTest **from that installed directory** (e.g. `cd /opt/rocm/bin/my_project && ctest -L quick`). CTest reads the local `CTestTestfile.cmake`, runs the tests with the same labels and timeouts as in the build tree, and no build tree is required.
3. **TheRock (or any consumer)** runs CTest **from that installed directory** (e.g. `cd /opt/rocm/bin/rocblas && ctest -L quick`). CTest reads the local `CTestTestfile.cmake`, runs the tests with the same labels and timeouts as in the build tree, and no build tree is required.

**Example (rocBLAS):**

Expand Down
15 changes: 1 addition & 14 deletions shared/ctest/TestCategories.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ find_package(Python3 COMPONENTS Interpreter)
function(_parse_test_category_optional_args)
cmake_parse_arguments(
ARG
"USE_RTEST_DRIVER"
""
"INSTALL_TEST_FILE;RESOURCE_GROUP;TEST_NAME_PREFIX;INSTALL_EXECUTABLE"
"COMMAND_ARGS;INSTALL_COMMAND_ARGS;ADDITIONAL_LABELS;ENVIRONMENT"
${ARGN}
)

set(_install_test_file "${ARG_INSTALL_TEST_FILE}")
set(_resource_group "${ARG_RESOURCE_GROUP}")
set(_use_rtest_driver "${ARG_USE_RTEST_DRIVER}")
if(ARG_UNPARSED_ARGUMENTS)
list(LENGTH ARG_UNPARSED_ARGUMENTS _arg_count)
if(NOT _install_test_file AND _arg_count GREATER 0)
Expand All @@ -28,17 +27,10 @@ function(_parse_test_category_optional_args)
if(NOT _resource_group AND _arg_count GREATER 1)
list(GET ARG_UNPARSED_ARGUMENTS 1 _resource_group)
endif()
if(NOT _use_rtest_driver)
list(FIND ARG_UNPARSED_ARGUMENTS "--use-rtest-driver" _rtest_idx)
if(NOT _rtest_idx EQUAL -1)
set(_use_rtest_driver TRUE)
endif()
endif()
endif()

set(_TEST_CATEGORY_INSTALL_FILE "${_install_test_file}" PARENT_SCOPE)
set(_TEST_CATEGORY_RESOURCE_GROUP "${_resource_group}" PARENT_SCOPE)
set(_TEST_CATEGORY_USE_RTEST_DRIVER "${_use_rtest_driver}" PARENT_SCOPE)
set(_TEST_CATEGORY_NAME_PREFIX "${ARG_TEST_NAME_PREFIX}" PARENT_SCOPE)
set(_TEST_CATEGORY_INSTALL_EXECUTABLE "${ARG_INSTALL_EXECUTABLE}" PARENT_SCOPE)
set(_TEST_CATEGORY_COMMAND_ARGS "${ARG_COMMAND_ARGS}" PARENT_SCOPE)
Expand Down Expand Up @@ -75,9 +67,6 @@ function(_build_test_category_parser_args out_var)
foreach(extra_env_kv IN LISTS _TEST_CATEGORY_ENVIRONMENT)
list(APPEND extra_args "--environment" "${extra_env_kv}")
endforeach()
if(_TEST_CATEGORY_USE_RTEST_DRIVER)
list(APPEND extra_args "--use-rtest-driver")
endif()
set(${out_var} "${extra_args}" PARENT_SCOPE)
endfunction()

Expand Down Expand Up @@ -134,8 +123,6 @@ endfunction()
# generated suite, merged with execution_settings.environment from the
# YAML (this list wins on key conflicts). Use to forward CMake-side
# TEST_ENVIRONMENT (ASAN symbolizer path, coverage LLVM_PROFILE_FILE).
# USE_RTEST_DRIVER - Run the project's <stem>_rtest.py driver with
# -t ctest_<category> instead of invoking the gtest binary directly.
# ~~~
function(apply_test_category_labels target_name yaml_file working_dir)
_parse_test_category_optional_args(${ARGN})
Expand Down
Loading
Loading