Skip to content

feat(ci): derive test-subproject selection from the consumer graph - #6941

Merged
ScottTodd merged 13 commits into
ROCm:mainfrom
endurthiabhilash:users/aendurth/support-dynamic-subproject-testing
Aug 7, 2026
Merged

ScottTodd merged 13 commits into
ROCm:mainfrom
endurthiabhilash:users/aendurth/support-dynamic-subproject-testing

Conversation

@endurthiabhilash

@endurthiabhilash endurthiabhilash commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

TheRock CI selects which GPU test suites to run from the set of changed projects, via a hand-maintained TEST_SUBPROJECTS list on each therock_cmake_subproject_declare(). That list is only partially populated, so a change to most subprojects selects nothing beyond itself, producing passing CI runs that never exercised the affected downstream suite (false greens).

This PR makes test selection a derived property of the dependency edges the build already declares, rather than a parallel list kept in sync by hand. Selection walks the reverse-dependency (consumer) graph to a per-component level on a 1–5 gating ladder — default level 4 (the changed component + its direct consumers), with broader/narrower tiers available per component. See
RFC0013 for the design and gating background.

Issue ID: #3343

Technical Details

  • Consumer graph (CMake). therock_cmake_subproject_declare() records a reverse edge per BUILD_DEPS/RUNTIME_DEPS; therock_emit_consumer_graph() serializes it at configure time. The graph is committed to test_tools/therock_consumer_graph.json so the per-PR change-detection job reads it directly — no fetch or configure on the hot path. Removes the TEST_SUBPROJECTS keyword and its per-component uses.
  • Level-based selection (Python). determine_rocm_test_dependencies.py loads the committed graph and, for each changed subproject, walks consumer edges to the depth set by its policy level (5 = self only, 4 = direct consumers, 3 = transitive closure). Build stage is not consulted.
  • Policy overrides (test_tools/test_policies.toml). A small, hand-reviewed file layers explicit intent on the graph: per-component level, plus test_include (additive) and test_exclude (subtractive, applied last) for couplings the graph can't express, test-only targets with no link edge (e.g. rocgdb-cpu, hipinfo) and reverse edges absent from forward deps. Broad-blast-radius runtimes (e.g. rocr-runtime) use level = 3 to auto-derive their downstream set instead of listing it by hand.
  • Drift check (CI). consumer_graph_drift.yml regenerates the graph from a full-tree configure and fails if the committed copy is stale, so the checked-in graph can never silently diverge from the declared dependency edges.

Test Plan

  • test_tools/tests/determine_rocm_test_dependencies_test.py covers the level walk (each depth), test_include/test_exclude, and policy validation against the committed graph.
  • build_tools/github_actions/tests/check_consumer_graph_drift_test.py covers the drift check's normalization and pass/fail behavior.

Test Result

Submission Checklist

  • Reviewed CONTRIBUTING.md
  • Unit tests pass
  • RFC0013 reflects the merged design

@therock-pr-bot

therock-pr-bot Bot commented Jul 28, 2026 •

Copy link
Copy Markdown

✅ All Checks Passed — Ready for Review

Check Status Details
📝 PR Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🔎 pre-commit ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —
🤖 therock-pr-bot ✅ Pass —

🎉 All checks passed! This PR is ready for review.

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

@therock-pr-bot therock-pr-bot Bot added the Not ready to Review PR has unresolved policy failures — reviews blocked label Jul 28, 2026
@therock-pr-bot

therock-pr-bot Bot commented Jul 28, 2026 •

Copy link
Copy Markdown

🎉 All checks passed! This PR is ready for review.

endurthiabhilash and others added 4 commits July 28, 2026 17:37
…overrides

Move test-selection overrides into BUILD_TOPOLOGY.toml and drop the stale
test_subprojects_overrides.json references from the comments.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Re-adds the env-var short-circuit in configure() so downstream test
workflows can force changed_projects without hitting the gh-api change
detection path.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Add RFC0013 proposing derived test-subproject selection from the CMake
consumer graph plus BUILD_TOPOLOGY overrides, including DEFCON gating-level
background, and link it in the RFC index.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Adds a section framing DEFCON 4 as one policy over the consumer graph and
describes how levels 1-5 map to fan-out reach, plus the test-tier dimension
needed for true levels 1/2/5.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
@endurthiabhilash
endurthiabhilash force-pushed the users/aendurth/support-dynamic-subproject-testing branch from 937445c to 8f468a4 Compare July 29, 2026 00:38
@therock-pr-bot

Copy link
Copy Markdown

Pre-commit check failed

⛔ pre-commit failed

Please run locally:

  • python -m pip install pre-commit
  • pre-commit install
  • pre-commit run --all-files --show-diff-on-failure

This repo uses .pre-commit-config.yaml.

@ScottTodd
ScottTodd self-requested a review July 29, 2026 14:56

@ScottTodd ScottTodd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I left some initial comments on the design.

In general I think we do need this dynamic graph analysis in some form to make CI build/test selection scale well.

baseline derivable rather than curated, and the override keys become the knobs for
moving a component off that baseline.

[defcon]: https://amd.atlassian.net/wiki/spaces/MLSE/pages/1795331309/DEFCON+Enablement+Gating+Levels

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No direct links to internal pages in open source code. You can indirect through a u.amd.com shortlink as needed, but if we want to refer to this from open source the design should be public (I also don't know if we'd want to use the term "defcon" - @astyrrian1 ?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll remove the direct amd.atlassian.net link and can add a short public summary of the gating levels so the RFC is self-contained.

On the "defcon" term itself — I'd like to wait for confirmation from others (@astyrrian1, @suchitravemuri1) before renaming. If we settle on a different public name for the levels I'll rename throughout.

Comment on lines +18 to +32
## Background: DEFCON gating levels

This work exists to implement **DEFCON 4** per-PR gating across TheRock's
components. DEFCON is the ROCm enablement-gating scheme defined in
[DEFCON Enablement: Gating Levels][defcon] (MLSE space): a per-component tier
that fixes *how much* every PR must build and test, sized to the component's
downstream blast radius and regression history.

| Level | What every PR must build & test | Intended for |
| ------------ | ------------------------------------------------------------------- | -------------------------------------------------------- |
| **DEFCON 1** | Full QA integration + nightly suites | Components actively destabilizing releases |
| **DEFCON 2** | Frameworks built and their tests run | High downstream blast radius (e.g. framework-facing libs)|
| **DEFCON 3** | All rocm-libraries projects/tests + rest of rocm-systems (rccl etc.)| Broad-dependency components with regression history |
| **DEFCON 4** | Local + intimately related project builds/tests only | Well-tested components with contained surface |
| **DEFCON 5** | Fast unit tests only | Proven track record + strong local test coverage |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

By this definition, isn't this between 3 and 4?

4 is just "intimately related projects", e.g.

  • hip-clr edited -> test hip-tests
  • rocRoller edited -> test hipBLASLt (which directly depends on rocRoller), do NOT test MIOpen (which directly depends on hipBLASLt and indirectly depends on rocRoller)

3 would be "all projects/tests" which is for "I don't trust the build/test system to compute the impact of this change for me, so just build/test everything"

What using the build graph lets us do is compute direct and indirect users of each project and decide how far along those dependency graph edges we want to walk, e.g.:

  • rocRoller edited - > test hipBLASLt and MIOpen
  • hip-clr edited -> test all users of it (rocRAND, rocPRIM, rocRoller, hipBLASLt, MIOpen, etc.), do NOT test projects that don't have a direct or indirect dependency on hip-clr, like rocr-debug-agent or rdc

Comment on lines +70 to +72
- **Drift and false greens.** A stale or missing entry produces a passing CI run
that never exercised the affected downstream suite: the most dangerous class of
gap because it is invisible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Comment on lines +104 to +111
3. **Derive test subprojects in Python.** `determine_rocm_test_dependencies.py`
loads the graph, computes each subproject's build stage from the committed
`artifact-*.toml` descriptors + `BUILD_TOPOLOGY.toml`, and for each changed
subproject selects its **same-stage** direct consumers.
4. **Apply explicit overrides from `BUILD_TOPOLOGY.toml`.** Couplings the graph
cannot express (test-only projects with no link edge, cross-stage couplings,
and stage-less foundational deps) are declared with `test_include` /
`test_exclude` / `test_fanout_all` keys on the relevant artifact.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure I agree with the usage of BUILD_TOPOLOGY.toml stages here. "cross-stage couplings" are commonplace, e.g. many projects depend on amd-hip (the compiler), hip-clr (the runtime), and rocprofiler-sdk (the profiler).

Comment on lines +138 to +157
### The "same-stage cut"

Selecting *all* transitive consumers of a foundational dependency (e.g.
`hip-clr`, `ROCR-Runtime`, the compiler) would pull in nearly the entire test
tree: every math lib, ML lib, comm lib, etc. To keep selection bounded, the tool
only selects consumers **in the same BUILD_TOPOLOGY build stage** as the changed
subproject. Cross-stage consumers are treated as "universal" and cut.

Where a specific cross-stage or test-only coupling genuinely must run, it is
declared explicitly (see Overrides). This makes the expensive, broad selections
opt-in and auditable rather than accidental.

The same-stage cut is what keeps the default at **DEFCON 4** ("local + intimately
related") rather than sliding into DEFCON 3 ("all rocm-libraries / rocm-systems").
Selecting every transitive consumer of a foundational dep is effectively DEFCON 3
behavior on every touch of that dep; the cut confines the automatic selection to
the directly-related, same-stage neighborhood and keeps runs inside the 10–15 min
SLA. Deliberately widening a component toward DEFCON 3/2 is then an explicit,
reviewable act (`test_fanout_all` / `test_include`) rather than an accident of the
graph's shape.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Relating to my other comments... well yeah, if something depends on hip-clr then its builds/tests could be affected by changes there. I don't think "build stage" is the right boundary here. Viewing this through the lens of "direct" and "indirect" deps may be more useful.

The framing here as "an accident of the graph's shape" doesn't make sense to me - if a project takes a dependency on another project that's no accident.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agree with all the comments related to build stages. I'll come up with a plan on how to implement direct, transitive dependencies, update the RFC for another round of review.

Comment on lines +186 to +198
### CI integration

The change-detection job in `test_artifacts.yml` gains three steps before it
determines test dependencies:

1. `pip install -r requirements.txt`
2. `fetch_sources.py` — all subproject source trees must be present so every
`therock_cmake_subproject_declare()` runs and the emitted graph is complete.
3. A configure-only `cmake -B build -DTHEROCK_ENABLE_ALL=ON ...` that emits
`build/therock_consumer_graph.json` as a side effect.

`determine_rocm_test_dependencies.py` then reads the freshly emitted graph. The
graph is never committed, so it cannot drift from the build definitions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is pretty expensive to run: https://github.com/ROCm/TheRock/actions/runs/30411761253/job/90461735601?pr=6941. It takes around 6 minutes on a GitHub-hosted runner (4m40s on checkout, 1m40s on configure).

We'll also want more of this information to be available prior to building, so we can ensure

  • all projects we plan on testing are also built
  • (if we want) only projects we plan on testing are built

Some ideas:

  • Generate the graph and check it in / cache it somehow, so we don't need to recompute with this dynamic code
  • Have the CI configure step that runs at the start of the pipeline compute the graph (something similar is needed for build artifact reuse too, cc @erman-gurses @ammallya )
  • Have each build stage emit a subset of the graph and then merge those subgraphs back together when running tests

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ack. My earlier idea was to generate the graph, check it in and have a CI configure step. However, I didn't want to introduce a new json file to check in and went with dynamically generating each time.

I'll explore adding the consumer_graph.json and add a CI step to validate at the start of the pipeline.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed the dynamic-every-run configure is too expensive on the per-PR hot path - I've moved off it in the RFC. The approach I'm proposing:

  1. Commit therock_consumer_graph.json to the repo. The per-PR change-detection job reads the checked-in file directly — no cmake, no source fetch on the hot path.
  2. Add a CI validity check that regenerates the graph and fails if the committed copy is stale. It runs only when dependency-related files change (**/CMakeLists.txt, the emit/registration cmake, the committed graph itself), so the ~6-minute configure is confined to that low-frequency job instead of every PR. This turns silent drift into a CI failure similar to the contract BUILD_TOPOLOGY.toml already carries.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That works for me. The main thing I want to avoid is having a complex graph that is only manually updated, as then we can easily overlook that graph going stale or accidentally missing dependency edges that it should have. A dynamically generated graph or a static graph that is produced by a script (ideally with CI enforcing that it stays up to date) would both be okay.

Comment on lines +227 to +232
### What the generalization adds

1. **A `defcon` key** on `[artifacts.<name>]`, overridable in the
`test_overrides.<subproject>` sub-table and resolved by the existing
`get_test_overrides_for_subproject()` path. Absent means the default level 4,
which is exactly the RFC's "Phase 1 starts every component at DEFCON 4."

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like the idea of having a clear place to say "changes to these code paths/project should use this level of testing". I'm not convinced that the topology or artifacts are the right place for that though. Need to think on this some more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ACK

If topology or artifacts are not the right places, I'm leaning toward a dedicated file at the root or in testing_tools, e.g. test_policies.toml, keyed by component, carrying both the per-component level and any test_include / test_exclude. Keying by component removes the artifact-sharing issue and gives one clear place to answer "what testing does a change to this component trigger?".
Rough sketch:

[component.hipblaslt]
level = 4
test_include = ["hipsparselt"]

Let me know if you have other suggestions. Otherwise, I can document this approach in RFC and implement.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm coming around to the idea of putting more code in https://github.com/ROCm/TheRock/tree/main/test_tools specifically for these sorts of details in the CI system.

Ideally I'd like to have what build systems like Bazel have, where all build/package/test/etc. targets are known to the build system and you can just run bazel test and have it understand what targets already have cached outputs/results and which need to get built/run. To that end, I've hoped we could get the CMake superproject here to support similar features as natively as possible via our CMake modules and supporting files like BUILD_TOPOLOGY.toml.

I think a reasonable middle ground between "the build system fully models all dependencies and knows how to cache" and "we manually specify every dependency relationship and testing level for each project" would be to have code in https://github.com/ROCm/TheRock/tree/main/test_tools or a similar location that is generated from knowledge about the build system and has a few escape hatches / customization points where we can say "changes to this project should by default also build/test projects 'foo' and 'bar'" or "changes to this project should by default build/test all projects that depend on it: [generated list]"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That matches with where I've landed. Concretely I'd split it into two files:

therock_consumer_graph.json: generated, CI-monitored. The full reverse dependency graph, emitted from the build system, no hand editing. The validity check from the CI-cost thread is the "CI enforcing it stays up to date" part.
A hand-maintained overrides file (e.g. test_policies.toml): small and reviewed. Only the escape hatches, per-component level (how far to walk the generated graph) plus test_include / test_exclude for what the graph can't express.

The escape hatches you named map directly: "also build/test component a and b" -> test_include;
"ignore testing component c" -> test_exclude;
"build/test all projects that depend on it" -> level = 3 (transitive walk over the generated graph, so the list stays generated rather than pasted in).

I'd keep the generated graph and the overrides as separate files because they have opposite lifecycles: the graph is machine-generated and shouldn't be hand-edited (edits get clobbered on regen, and the whole point is it can't drift), the overrides are human intent. Splitting them lets the validity check just regenerate-and-diff the graph, and the overrides get reviewed like code, with no file that's half-generated and half-authored.

Drawbacks of the two-file split, to be upfront:

Two places to look. Answering "what does a change to X test?" means reading the graph and the overrides together instead of one entry. I'd add a determine_rocm_test_dependencies.py helper that prints the resolved selection for a component so we rarely compose by hand.
Referential integrity across files. An override can name a component that no longer exists in the graph, and the override file alone won't catch it. Needs a small validation step that checks override keys against the current graph.
Two-step edit sometimes. A genuinely new dep edge regenerates the graph (automatic) and may need an override tweak (manual), where a single hand-file would be one edit (but then it isn't generated at all).
Precedence has to be defined. With level plus include/exclude on a generated base, the resolution order (walk to level -> apply include -> apply exclude) needs to be specified and tested so results are deterministic.
I think the split is worth it, it's the only way the graph half stays CI-owned and drift-proof while the intent half stays hand-reviewed, and the drawbacks are all addressable with a resolver helper + an override validation check.

Comment on lines +100 to +103
2. **Emit a consumer graph.** At the end of the top-level configure,
`therock_emit_consumer_graph()` serializes the registry to
`build/therock_consumer_graph.json`, a configure-time side effect analogous to
`compile_commands.json`. It is **dynamic and never committed**.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you post the current therock_consumer_graph.json somewhere we can look at it? This comment thread, a gist linked from the PR description, etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I will check in therock_consumer_graph.json once I build it with direct, indirect consumers.

Add the drift-check tooling (check_consumer_graph_drift.py + test),
its CI workflow, the committed consumer graph and test_policies.toml,
and wire the emitter/topology changes. Rename RFC0013 to
Consumer-Based-Test-Selection and refresh its references.
Set all components to level 4 (default) and add test_include couplings so the
graph walk plus overrides covers the tracked downstream dependent set.
rocr-runtime uses level 3 to auto-derive its broad transitive closure, pinning
only the graph-unreachable couplings (amd-dbgapi, ocl-icd, rocgdb).
Drops the env-var short-circuit in configure_external_repo_ci.py (reverts
dfbc289) and its RFC0013 mention. The override is retained on the
...-testing-e2e branch for end-to-end selection testing; keep it out of the
mergeable change.
@therock-pr-bot therock-pr-bot Bot removed the Not ready to Review PR has unresolved policy failures — reviews blocked label Aug 5, 2026
@endurthiabhilash
endurthiabhilash marked this pull request as ready for review August 5, 2026 19:11
@endurthiabhilash
endurthiabhilash requested review from a team as code owners August 5, 2026 19:11

@ScottTodd ScottTodd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Architecturally LGTM. A mix of comments on style, specific policies to apply to individual subprojects for correctness, and some details on how to integrate into our existing infrastructure then I'll be okay with approving.

Comment on lines +61 to +65
- name: Check committed consumer graph is up to date
# Compares the freshly emitted graph (build/) against the committed copy
# (test_tools/) after normalizing both the same way, and fails on any real
# edge change. Logic lives in a unit-tested script, not inline here.
run: python3 ./build_tools/github_actions/check_consumer_graph_drift.py --check

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No need for verbose comments like "Logic lives in a unit-tested script, not inline here.", the code already expresses that.

Comment thread test_tools/test_policies.toml Outdated
Comment on lines +25 to +31
# Resolution order: walk to the component's level -> union test_include ->
# subtract test_exclude (last). Every component KEY must name a real
# consumer-graph key (it is the change trigger; a cross-file validation, task #5,
# fails the build otherwise). test_include / test_exclude VALUES need NOT be graph
# keys: their whole purpose is to name test-only targets the graph cannot express
# (e.g. ctest suites like rocgdb-cpu). There is no `test_fanout_all`: "test all
# downstream consumers" is expressed as `level = 3`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is task #5? (I've seen internal task planning state leak into code comments authored by AI agents, did that happen here?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My bad, I cleaned up all internal references, other internal comments but, clearly some escaped. Let me fix.

Comment thread test_tools/test_policies.toml Outdated
Comment on lines +33 to +34
# Components are DEFCON 4 by default (level 4 = self + direct consumers, so the
# level key is omitted). A few broad-blast-radius runtimes set level = 3 (full

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: this sentence is redundant with the table above that says the default level is 4. Could trim these comments down a bit.

Comment thread test_tools/test_policies.toml Outdated
Comment on lines +38 to +39
# UNION (test_include) covers the dependent set from the "DEFCON Enablement:
# Gating Levels" tracking page.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is the "DEFCON Enablement: Gating Levels tracking page"? (careful about notes that make sense to an agent with context spilling into open source code that needs to stand on its own)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Internal page, will remove.

Comment thread test_tools/test_policies.toml Outdated
Comment on lines +41 to +44
# --- Foundational runtimes: broad downstream dependent sets --------------------
# rocr-runtime / hip-clr / amd-llvm sit under most of the tree. Their direct
# graph consumers miss many real downstream projects (framework-facing libs, test
# harnesses), so the DEFCON 4 dependent list is carried explicitly here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The framing of this comment is confusing. The amd-llvm compiler is used to compile ~all projects and it is very close to the root of the build graph, so of course the "direct graph consumers" list is short for it. Changes to the compiler should build all projects that use the compiler, direct dependency or otherwise.

Note that deps on the compiler are carried via COMPILER_TOOLCHAIN (amd-hip or amd-llvm) and not BUILD_DEPS or TEST_DEPS. For example:

therock_cmake_subproject_declare(hipRAND
EXTERNAL_SOURCE_DIR "${THEROCK_ROCM_LIBRARIES_SOURCE_DIR}/projects/hiprand"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/hipRAND"
BACKGROUND_BUILD
CMAKE_ARGS
-DHIP_PLATFORM=amd
-DROCM_PATH=
-DROCM_DIR=
-DBUILD_TEST=${THEROCK_BUILD_TESTING}
COMPILER_TOOLCHAIN
amd-hip
BUILD_DEPS
rocm-cmake
therock-googletest

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This makes sense, I will update to use COMPILER_TOOLCHAIN for building consumer graph for compilers.

Comment on lines +210 to +211
def get_subprojects_to_test(
changed_subprojects: list[str],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is this function expecting for "changed_subprojects"? My review agent caught this:

The old stage-map mismatch was removed, but the end-to-end pipeline still uses
at least three different identifiers without an authoritative mapping:

Concrete current-head results:

projects/clr                     -> warning; ["clr"]
shared/rocroller                 -> warning; ["shared/rocroller"]
projects/hipdnn-integration-tests -> warning; ["hipdnn-integration-tests"]

The first two select no component job beyond sanity. The policy has the same
problem: test_policies.toml
adds hipdnn_integration_tests and hipdnn_samples for MIOpen and
hipkernelprovider, while the real matrix keys are
hipdnn-integration-tests and hipdnn-samples.
An end-to-end local run for a MIOpen change scheduled miopen and
miopenprovider, but silently omitted both intended hipDNN suites.

The unit tests stop at the selector's returned set, so they accept names that
cannot create a real CI job.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Earlier I was using an test override to mimic changed subprojects in rocm-systems, rocm-libraries. I removed that logic from here, created another test branch off of it for my e2e testing.

Comment on lines +132 to +138
A missing file yields empty policies (so every component uses the default
level and no include/exclude) rather than crashing — useful for clean-checkout
introspection, though the file is committed and normally present.
"""
test_deps = {}
cmake_files = list(Path(therock_dir).rglob("CMakeLists.txt"))
policies_path = _test_policies_path(therock_dir)
if not policies_path.exists():
return {}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems like a missing file should raise an exception rather than default to empty policies: https://github.com/ROCm/TheRock/blob/main/docs/development/style_guides/python_style_guide.md#fail-fast-behavior

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ack

Comment on lines +29 to +32
The per-component level, plus test_include/test_exclude, come from the
hand-maintained `test_tools/test_policies.toml`. A component with no table there
uses the default level 4 (depth 1) with no include/exclude. See the schema
contract in docs/development/test_selection_schemas.md.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

test_selection_schemas.md does not exist. Redirect to the RFC in docs/rfcs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ack

Comment on lines +119 to +123
hand-authored regions. Keeping them apart lets the graph be fully CI-owned
(regenerate-and-fail-on-diff) and the policy be fully human-owned (reviewed like
code). This mirrors ScottTodd's "generated from build-system knowledge, with a few
escape hatches" framing: the graph is the generated knowledge, the policy file is
the escape hatches.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: can drop the reference to my specific username and say instead "during review we decided..." or similar ;) the full discussion history is here on the PR

Comment on lines +456 to +458
- **Level naming in open source.** The "DEFCON" label is internal. Should the
public RFC and code use a different name for the gating levels? (Pending input
from @ScottTodd / @astyrrian1.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lol, @astyrrian1 @suchitravemuri1 how you feel about us using "DEFCON" as the public name for these test tiers?

@endurthiabhilash endurthiabhilash Aug 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My bad, I cleaned up all internal references, other internal comments but, clearly some escaped. Let me fix.

@lumachad

lumachad commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Just so I understand the impact of this change on ROCgdb CI and what we're trying to achieve here. We're throwing away TEST_SUBPROJECTS entirely and replacing it with another mechanism that does things automatically?

I went through the generated consumer graph json and there might be a gap here. Take for instance ROCgdb. ROCgdb does not depend directly on rocr-runtime, but when ROCgdb runs its tests that definitely depends on rocr-runtime. In fact that's been a frequent area of uncaught regressions from rocm-systems changes.

Could we please make sure we have that covered?

Another point is that we have a ROCgdb testing split to optimize capacity usage. rocgdb-cpu tests only have to be exercised due to changes in the amd-llvm compiler. rocgdb-gpu needs to be exercised whenever amd-llvm or rocr-runtime/clr change etc. Do we have any provisions to cater for that split so we don't run useless tests when not required?

Comment on lines +104 to +105
[component.rocgdb]
test_include = ["rocgdb-cpu", "rocgdb-gpu"]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just so I understand the impact of this change on ROCgdb CI and what we're trying to achieve here. We're throwing away TEST_SUBPROJECTS entirely and replacing it with another mechanism that does things automatically?

I went through the generated consumer graph json and there might be a gap here. Take for instance ROCgdb. ROCgdb does not depend directly on rocr-runtime, but when ROCgdb runs its tests that definitely depends on rocr-runtime. In fact that's been a frequent area of uncaught regressions from rocm-systems changes.

Could we please make sure we have that covered?

Another point is that we have a ROCgdb testing split to optimize capacity usage. rocgdb-cpu tests only have to be exercised due to changes in the amd-llvm compiler. rocgdb-gpu needs to be exercised whenever amd-llvm or rocr-runtime/clr change etc. Do we have any provisions to cater for that split so we don't run useless tests when not required?

I would expect:

  • This policy says "when rocgdb is modified, run rocgdb-cpu and rocgdb-gpu"
  • A policy for rocr-runtime of
    [component.rocr-runtime]
    # ROCr is used by many projects so test self + transitive users
    level = 3
    says "when rocr-runtime is changed, test every project that depends on rocr-runtime directly or indirectly"
  • A policy for rocr-runtime of
    [component.rocr-runtime]
    test_include = ["rocgdb"]
    (using the default level) would instead say "when rocr-runtime is changed, test rocr-runtime itself and the one additional project 'rocgdb'"

As for the cpu/gpu split, I don't see a clear spot for that in the current design. We could add some additional tiers of tests along those lines 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For cpu, gpu split, can we add explicit excludes for amd-llvm, hip-clr changes like below? I need to test, but this should work. If we need to add more specific tiers of tests then we can do that in the next iteration once this is merged.

[component.amd-llvm]
test_include = ["rocgdb-cpu"]
test_exclude = ["rocgdb-gpu"]

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.

Thanks for the examples. As long as we can specify these dependencies clearly (or if they get resolved automatically, even better), this should work for us. The split is more of an optimization of capacity use, not a hard requirement.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Another option to consider for cheap/fast CPU unit tests is therock_cmake_subproject_build_test which runs during the build itself, on CPU runners. That runs in parallel to other build jobs and does not need to wait until all other subproject builds run, the GPU sanity check job is scheduled, etc.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah that may be a better fit for rocgdb-cpu tests instead of building so many branches into the existing GPU testing scripts and workflows. See also #4789

@geomin12 geomin12 left a comment

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.

first pass

contents: read

jobs:
consumer_graph_drift:

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.

let's add test as if it is making sure it is not drifting

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Edge drift is caught by test_check_fails_on_edge_change in the unit tests, and by this workflow regenerating from a full configure and diffing. Let me know if you are thinking of other cases that I missed.

Comment on lines +52 to +56
# Level -> BFS walk depth over `consumers` edges. depth 0 = self only,
# depth 1 = self + direct consumers, None = unbounded (transitive closure).
# Levels 1 and 2 are the "stricter/broader" tiers on the DEFCON ladder; until a
# separate test-tier output exists they walk transitively (same depth as 3), so
# the accepted policy range [1, 5] and the engine never disagree.

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.

can we do a better comment?

Maybe a

depth 0: 
depth 1: 
depth 2:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

stream = sys.stdout if ok else sys.stderr
for line in messages:
print(line, file=stream)
sys.exit(0 if ok else 1)

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.

maybe instead of sys.exit, could we raise an Exception to indicate policy is not ok?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

else:
lines.append(" test_include: (none)")
if exclude:
lines.append(" test_exclude: " + ", ".join(sorted(exclude)))

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.

could we use \t instead of spaces?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

Record COMPILER_TOOLCHAIN edges in the consumer graph, rename the registry
property to THEROCK_DIRECT_CONSUMERS_OF_*, regenerate the committed graph, and
prune redundant policy includes. Add --build-dir to the drift checker, rename the
drift workflow, move policy validation into the test suite, fail fast on a missing
policy file, and simplify comments and the RFC.
# Levels 1 and 2 walk transitively (same as 3) until a separate test-tier output
# distinguishes them.
_DEFAULT_LEVEL = 4
_LEVEL_TO_DEPTH: dict[int, int | None] = {5: 0, 4: 1, 3: None, 2: None, 1: None}

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.

As per this, levels 1 and 2 also will get same treatment as level 3 - a transitive walk.
As per RFC0013:
Level 2 = self + a named frameworks group
Level 1 = everything (projects_to_test = "*") + a full-QA / nightly workflow dispatch

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, this PR is enabling only levels 3-5. So, temporarily levels 1, 2 are falling back to level 3. I'll add a TODO flag here to implement levels 1, 2 in the next iteration as improvement.

@ScottTodd ScottTodd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks complete enough to land and iterate.

You may want to sync the PR branch and let CI run again prior to merge. There was a github actions outage yesterday that caused jobs to not trigger. The code here mostly doesn't affect CI yet though as far as I can tell so it could be safe.

Comment thread test_tools/test_policies.toml Outdated
Comment on lines +118 to +119
[component.hipsparse]
test_include = ["rocsparse"]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Want to adjust these includes as part of this PR or a follow-up?

@endurthiabhilash

Copy link
Copy Markdown
Contributor Author

Looks complete enough to land and iterate.

You may want to sync the PR branch and let CI run again prior to merge. There was a github actions outage yesterday that caused jobs to not trigger. The code here mostly doesn't affect CI yet though as far as I can tell so it could be safe.

Synced the branch, also cleaned up some reverse edges in test_policies.toml. Any remaining ones will be cleaned up in next iteration. Do I also need approval from @ROCm/rocm-product-managers to merge this PR?

@ScottTodd

Copy link
Copy Markdown
Member

Do I also need approval from @ROCm/rocm-product-managers to merge this PR?

Technically yes, but admins could also merge through that requirement. The revirew request there is due to CODEOWNERS for docs/rfcs, see #5424. @saadrahim do you want to review the RFC about test selection here? We could also move the file to https://github.com/ROCm/TheRock/tree/main/docs/design to get around that review requirement if this doesn't feel as much like a product RFC so much as an engineering design doc. Not sure where we want to draw the lines there.

@endurthiabhilash

Copy link
Copy Markdown
Contributor Author

Do I also need approval from @ROCm/rocm-product-managers to merge this PR?

Technically yes, but admins could also merge through that requirement. The revirew request there is due to CODEOWNERS for docs/rfcs, see #5424. @saadrahim do you want to review the RFC about test selection here? We could also move the file to https://github.com/ROCm/TheRock/tree/main/docs/design to get around that review requirement if this doesn't feel as much like a product RFC so much as an engineering design doc. Not sure where we want to draw the lines there.

I have re-triggered CI after syncing the branch. All the required checks pass. I see some non-blocking test failures, but the PR should be safe to merge. cc: @geomin12, @suchitravemuri1

@ScottTodd, will you be able to override @ROCm/rocm-product-managers approval requirement and merge the PR?

@ScottTodd

Copy link
Copy Markdown
Member

Sure. If there are concerns about the RFC file, post-merge feedback is welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants