Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

494 lines
20 KiB
Python

import json
from pathlib import Path
import os
import sys
import unittest
from unittest.mock import patch
sys.path.insert(0, os.fspath(Path(__file__).parent.parent))
import therock_configure_ci
class ConfigureCITest(unittest.TestCase):
def setUp(self):
# Keep run() tests independent of a sibling TheRock checkout and the
# GitHub Actions step output file.
select_build_runner_patcher = patch(
"therock_configure_ci.select_build_runner", return_value=""
)
self.addCleanup(select_build_runner_patcher.stop)
select_build_runner_patcher.start()
set_github_output_patcher = patch("therock_configure_ci.set_github_output")
self.addCleanup(set_github_output_patcher.stop)
set_github_output_patcher.start()
@patch("therock_configure_ci.get_modified_paths")
def test_pull_request(self, mock_get_modified):
args = {"is_pull_request": True, "base_ref": "HEAD^"}
mock_get_modified.return_value = ["projects/rocminfo/src/main.cpp"]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertGreaterEqual(len(project_to_run), 1)
@patch("therock_configure_ci.get_modified_paths")
def test_pull_request_empty(self, mock_get_modified):
args = {"is_pull_request": True, "base_ref": "HEAD^"}
mock_get_modified.return_value = []
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
# An empty diff should skip CI.
self.assertEqual(len(project_to_run), 0)
@patch("therock_configure_ci.get_modified_paths")
def test_workflow_dispatch(self, mock_get_modified):
args = {
"is_workflow_dispatch": True,
"input_projects": "projects/rocminfo projects/clr",
"base_ref": "HEAD^",
}
mock_get_modified.return_value = ["projects/rocminfo/src/main.cpp"]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertGreaterEqual(len(project_to_run), 1)
@patch("therock_configure_ci.get_modified_paths")
def test_workflow_dispatch_bad_input(self, mock_get_modified):
args = {
"is_workflow_dispatch": True,
"input_projects": "projects/invalid$$projects/fake",
"base_ref": "HEAD^",
}
mock_get_modified.return_value = ["projects/rocminfo/src/main.cpp"]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertEqual(len(project_to_run), 0)
@patch("therock_configure_ci.get_modified_paths")
def test_workflow_dispatch_all(self, mock_get_modified):
args = {
"is_workflow_dispatch": True,
"input_projects": "all",
"base_ref": "HEAD^",
}
mock_get_modified.return_value = ["projects/rocminfo/src/main.cpp"]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertGreaterEqual(len(project_to_run), 1)
@patch("therock_configure_ci.get_modified_paths")
def test_workflow_dispatch_empty(self, mock_get_modified):
args = {"is_workflow_dispatch": True, "input_projects": "", "base_ref": "HEAD^"}
mock_get_modified.return_value = ["projects/rocminfo/src/main.cpp"]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertEqual(len(project_to_run), 0)
def test_scheduled_run(self):
args = {"is_nightly": True, "input_projects": "", "base_ref": "HEAD^"}
project_to_run, test_type = therock_configure_ci.retrieve_projects(args)
self.assertEqual(len(project_to_run), 1)
self.assertEqual(test_type, "comprehensive")
@patch("therock_configure_ci.get_modified_paths")
def test_is_push(self, mock_get_modified):
args = {"is_push": True, "base_ref": "HEAD^"}
mock_get_modified.return_value = ["projects/rocminfo/src/main.cpp"]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertGreaterEqual(len(project_to_run), 1)
def test_is_path_skippable(self):
# SKIPPABLE_PATH_PATTERNS contains abstract fnmatch patterns. These
# cases make the intended matches concrete and verify how patterns
# behave with paths containing nested directories.
skippable_paths = [
"README.md",
"docs/guide.rst",
"projects/rocminfo/README.md",
"projects/rocminfo/docs/api.rst",
".gitignore",
"projects/rocminfo/.gitignore",
"projects/amdsmi/.pre-commit-config.yaml",
"projects/amdsmi/.github/CODEOWNERS",
"projects/amdsmi/LICENSE",
# A few source files that would normally trigger CI but are
# excluded based on which directory they are in.
"tools/systems_pr_bot/policy_check.py",
"experimental/perf-dkms/CMakeLists.txt",
"projects/rocr-runtime/libhsakmt/src/dxg/dxgmodule.c",
# GitHub metadata, workflow, and action files unrelated to TheRock
# CI are skippable.
".github/labeler.yml",
".github/labels.yml",
".github/actions/rocprofiler-sdk-util/action.yml",
".github/workflows/labeler.yml",
".github/workflows/rdc-ci.yml",
".github/workflows/amdsmi-manylinux-build.yml",
".github/workflows/rocjitsu-corpus-tests.yml",
]
for path in skippable_paths:
with self.subTest(path=path):
self.assertTrue(therock_configure_ci.is_path_skippable(path))
non_skippable_paths = [
"projects/rocminfo/src/main.cpp",
"CMakeLists.txt",
"projects/rocminfo/test/test.cpp",
# TheRock CI workflow, action, and script files are non-skippable so
# changes to them still trigger CI.
".github/workflows/therock-ci.yml",
".github/actions/therock-something/action.yml",
".github/scripts/therock_configure_ci.py",
]
for path in non_skippable_paths:
with self.subTest(path=path):
self.assertFalse(therock_configure_ci.is_path_skippable(path))
def test_check_for_non_skippable_path(self):
all_skippable = ["README.md", "docs/guide.rst"]
self.assertFalse(
therock_configure_ci.check_for_non_skippable_path(all_skippable)
)
some_skippable = ["README.md", "projects/rocminfo/src/main.cpp"]
self.assertTrue(
therock_configure_ci.check_for_non_skippable_path(some_skippable)
)
none_skippable = [
"CMakeLists.txt",
"projects/rocminfo/src/main.cpp",
]
self.assertTrue(
therock_configure_ci.check_for_non_skippable_path(none_skippable)
)
@patch("therock_configure_ci.get_modified_paths")
def test_docs_only_change_returns_empty_list(self, mock_get_modified):
args = {"is_pull_request": True, "base_ref": "HEAD^"}
mock_get_modified.return_value = [
"README.md",
"docs/guide.rst",
"projects/rocprim/docs/api.md",
]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertEqual(len(project_to_run), 0)
@patch("therock_configure_ci.get_modified_paths")
def test_linux_only_subtrees_returns_empty_list(self, mock_get_modified):
args = {
"is_pull_request": True,
"base_ref": "HEAD^",
"platform": "windows",
}
mock_get_modified.return_value = [
"projects/rocprofiler-compute/src/compute.cpp",
"projects/rocshmem/src/rocshmem.cpp",
"projects/amdsmi/src/amdsmi.cpp",
"projects/rocprofiler-register/src/register.cpp",
"projects/amdsmi/hello/test.cpp",
"projects/hipother/hello/test.cpp",
]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertEqual(len(project_to_run), 0)
@patch("therock_configure_ci.get_modified_paths")
def test_linux_only_subtrees_returns_correct_list(self, mock_get_modified):
args = {
"is_pull_request": True,
"base_ref": "HEAD^",
"platform": "windows",
}
mock_get_modified.return_value = [
"projects/rocprofiler-compute/src/compute.cpp",
"projects/rocshmem/src/rocshmem.cpp",
"projects/amdsmi/src/amdsmi.cpp",
"projects/rocprofiler-register/src/register.cpp",
"projects/amdsmi/hello/test.cpp",
"projects/hip/src/hip.cpp", # contains windows CI trigger
"projects/clr/src/hip.cpp", # contains windows CI trigger
".github/workflows/therock-ci.yml", # contains windows CI trigger
]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertEqual(len(project_to_run), 1)
@patch("therock_configure_ci.get_modified_paths")
def test_workflow_dispatch_explicit_project_not_overridden_by_ci_file_changes(
self, mock_get_modified
):
"""Regression test: workflow_dispatch with an explicit project must not be overridden
by CI-related file changes (e.g. therock_*.py) in modified_paths.
Previously, detecting .github/scripts/therock_*.py changes caused all subtrees to be
evaluated, ignoring the user-specified project selection entirely.
See: https://github.com/ROCm/rocm-systems/pull/3960
"""
args = {
"is_workflow_dispatch": True,
"input_projects": "projects/rdc",
"base_ref": "HEAD^",
"platform": "linux",
}
# Simulate a branch that touched CI scripts (the triggering condition for the bug)
mock_get_modified.return_value = [
".github/scripts/therock_configure_ci.py",
".github/scripts/therock_matrix.py",
]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertEqual(len(project_to_run), 1)
cmake_options = project_to_run[0]["cmake_options"]
# Must use the dc_tools-specific flags, not the all-projects fallback
self.assertIn("DTHEROCK_ENABLE_DC_TOOLS=ON", cmake_options)
self.assertNotIn("DTHEROCK_ENABLE_ALL=ON", cmake_options)
@patch("therock_configure_ci.get_modified_paths")
def test_workflow_dispatch_windows_not_skipped_by_modified_paths(
self, mock_get_modified
):
"""Regression test: workflow_dispatch on Windows must not be skipped when
modified_paths doesn't contain Windows-trigger patterns. The user explicitly
selected a project, so the Windows CI skip logic must not override that.
"""
args = {
"is_workflow_dispatch": True,
"input_projects": "projects/clr",
"base_ref": "HEAD^",
"platform": "windows",
}
# Simulate a branch whose last commit only touched a Linux-only subtree,
# which doesn't match any trigger_windows_ci_for_subtrees_paths pattern.
# Note: .github/scripts/therock_*.py would NOT be appropriate here because
# it matches the ".github/*/therock*" Windows-trigger pattern.
mock_get_modified.return_value = [
"projects/rocprofiler-compute/src/compute.cpp",
]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
# Windows CI must not be skipped for an explicit workflow_dispatch selection
self.assertEqual(len(project_to_run), 1)
@patch("therock_configure_ci.get_modified_paths")
def test_workflow_dispatch_shared_windows_subtree_triggers_windows_ci(
self, mock_get_modified
):
"""workflow_dispatch with shared/amdgpu-windows-interop must trigger Windows CI
even when the git diff contains no Windows-relevant file paths."""
args = {
"is_workflow_dispatch": True,
"input_projects": "shared/amdgpu-windows-interop",
"base_ref": "HEAD^",
"platform": "windows",
}
# Explicit workflow_dispatch selections do not depend on modified paths.
mock_get_modified.return_value = []
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertGreaterEqual(len(project_to_run), 1)
@patch("therock_configure_ci.get_modified_paths")
def test_workflow_dispatch_windows_only_subtree_skips_linux_ci(
self, mock_get_modified
):
"""workflow_dispatch with a windows_only subtree must not trigger Linux CI."""
args = {
"is_workflow_dispatch": True,
"input_projects": "shared/amdgpu-windows-interop",
"base_ref": "HEAD^",
"platform": "linux",
}
mock_get_modified.return_value = []
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertEqual(len(project_to_run), 0)
@patch("therock_configure_ci.get_modified_paths")
def test_rccl_ci_triggered_by_rccl_paths_pull_request(self, mock_get_modified):
"""A RCCL-only pull request should run RCCL CI but not regular CI."""
args = {"is_pull_request": True, "base_ref": "HEAD^", "platform": "linux"}
mock_get_modified.return_value = [
"projects/rccl/rccl.cpp",
"projects/rccl/test/test.cpp",
]
outputs = therock_configure_ci.run(args)
projects = json.loads(outputs["projects"])
self.assertEqual(len(projects), 0)
self.assertEqual(outputs["run_linux_rccl_ci"], "true")
@patch("therock_configure_ci.get_modified_paths")
def test_rccl_ci_not_triggered_by_non_rccl_paths_pull_request(
self, mock_get_modified
):
"""A non-RCCL pull request should run regular CI but not RCCL CI."""
args = {"is_pull_request": True, "base_ref": "HEAD^", "platform": "linux"}
mock_get_modified.return_value = ["projects/rocprim/rocprim.cpp"]
outputs = therock_configure_ci.run(args)
projects = json.loads(outputs["projects"])
self.assertGreaterEqual(len(projects), 1)
self.assertEqual(outputs["run_linux_rccl_ci"], "false")
def test_rccl_ci_triggered_nightly(self):
"""A nightly event should run both regular and RCCL CI."""
args = {"is_nightly": True, "base_ref": "HEAD^", "platform": "linux"}
outputs = therock_configure_ci.run(args)
projects = json.loads(outputs["projects"])
self.assertGreaterEqual(len(projects), 1)
self.assertEqual(outputs["run_linux_rccl_ci"], "true")
@patch("therock_configure_ci.get_modified_paths")
def test_rccl_ci_triggered_workflow_dispatch(self, mock_get_modified):
"""Selecting only RCCL manually should run RCCL CI but not regular CI."""
args = {
"is_workflow_dispatch": True,
"input_projects": "projects/rccl",
"base_ref": "HEAD^",
"platform": "linux",
}
mock_get_modified.return_value = []
outputs = therock_configure_ci.run(args)
projects = json.loads(outputs["projects"])
self.assertEqual(len(projects), 0)
self.assertEqual(outputs["run_linux_rccl_ci"], "true")
@patch("therock_configure_ci.get_modified_paths")
def test_non_rccl_push_runs_regular_ci_only(self, mock_get_modified):
"""A non-RCCL push should run regular CI but not RCCL CI."""
args = {"is_push": True, "base_ref": "HEAD^", "platform": "linux"}
mock_get_modified.return_value = ["projects/rocprim/rocprim.cpp"]
outputs = therock_configure_ci.run(args)
projects = json.loads(outputs["projects"])
self.assertGreaterEqual(len(projects), 1)
self.assertEqual(outputs["run_linux_rccl_ci"], "false")
# Push event combinations.
@patch("therock_configure_ci.get_modified_paths")
def test_push_only_rccl_skips_regular_ci(self, mock_get_modified):
"""Push with only RCCL changes should skip regular CI but run RCCL CI."""
args = {"is_push": True, "base_ref": "HEAD^", "platform": "linux"}
mock_get_modified.return_value = [
"projects/rccl/src/main.cpp",
"projects/rccl/test/test.cpp",
]
outputs = therock_configure_ci.run(args)
projects = json.loads(outputs["projects"])
self.assertEqual(len(projects), 0)
self.assertEqual(outputs["run_linux_rccl_ci"], "true")
@patch("therock_configure_ci.get_modified_paths")
def test_push_rccl_and_known_subtree_runs_both(self, mock_get_modified):
"""Push with RCCL + known subtree changes should run both regular and RCCL CI."""
args = {"is_push": True, "base_ref": "HEAD^", "platform": "linux"}
mock_get_modified.return_value = [
"projects/rccl/src/main.cpp",
"projects/clr/src/clr.cpp",
]
outputs = therock_configure_ci.run(args)
projects = json.loads(outputs["projects"])
self.assertGreaterEqual(len(projects), 1)
self.assertEqual(outputs["run_linux_rccl_ci"], "true")
@patch("therock_configure_ci.get_modified_paths")
def test_push_rccl_and_unknown_runs_full_ci(self, mock_get_modified):
"""Push with RCCL + unknown path changes should run full regular CI and RCCL CI."""
args = {"is_push": True, "base_ref": "HEAD^", "platform": "linux"}
mock_get_modified.return_value = [
"projects/rccl/src/main.cpp",
"unknown/path/file.cpp",
]
outputs = therock_configure_ci.run(args)
projects = json.loads(outputs["projects"])
# Should run full CI due to unknown path
self.assertGreaterEqual(len(projects), 1)
self.assertEqual(outputs["run_linux_rccl_ci"], "true")
@patch("therock_configure_ci.get_modified_paths")
def test_push_known_subtree_only_runs_regular_ci(self, mock_get_modified):
"""Push with only known subtree changes should run regular CI, not RCCL CI."""
args = {"is_push": True, "base_ref": "HEAD^", "platform": "linux"}
mock_get_modified.return_value = [
"projects/clr/src/clr.cpp",
"projects/rocminfo/src/main.cpp",
]
outputs = therock_configure_ci.run(args)
projects = json.loads(outputs["projects"])
self.assertGreaterEqual(len(projects), 1)
self.assertEqual(outputs["run_linux_rccl_ci"], "false")
@patch("therock_configure_ci.get_modified_paths")
def test_hipfile_pr_triggers_storage_libs_linux_ci(self, mock_get_modified):
"""PR with hipfile changes should trigger storage_libs build with THEROCK_ENABLE_STORAGE_LIBS=ON."""
args = {
"is_pull_request": True,
"base_ref": "HEAD^",
"platform": "linux",
}
mock_get_modified.return_value = [
"projects/hipfile/src/hipfile.cpp",
"projects/hipfile/include/hipfile.h",
]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertEqual(len(project_to_run), 1)
cmake_options = project_to_run[0]["cmake_options"]
self.assertIn("DTHEROCK_ENABLE_STORAGE_LIBS=ON", cmake_options)
self.assertNotIn("DTHEROCK_ENABLE_ALL=ON", cmake_options)
@patch("therock_configure_ci.get_modified_paths")
def test_hipfile_pr_skips_windows_ci(self, mock_get_modified):
"""PR with only hipfile changes should not trigger Windows CI (Linux-only component)."""
args = {
"is_pull_request": True,
"base_ref": "HEAD^",
"platform": "windows",
}
mock_get_modified.return_value = [
"projects/hipfile/src/hipfile.cpp",
"projects/hipfile/include/hipfile.h",
]
project_to_run, _ = therock_configure_ci.retrieve_projects(args)
self.assertEqual(len(project_to_run), 0)
if __name__ == "__main__":
unittest.main()