Skip to content

fix(tests): tag every tier a test belongs to, not just the invoked one - #847

Open
ashokponkumar wants to merge 1 commit into
torch-spyre:mainfrom
ashokponkumar:fix/tier-tags-declared-set
Open

fix(tests): tag every tier a test belongs to, not just the invoked one#847
ashokponkumar wants to merge 1 commit into
torch-spyre:mainfrom
ashokponkumar:fix/tier-tags-declared-set

Conversation

@ashokponkumar

Copy link
Copy Markdown
Contributor

Problem

result_tags() stamps one testtype__ tag, taken from SPYRE_TEST_TIER — the tier the run was invoked as. But tier membership is a set: _test_matrix.yaml declares it per matrix entry as test_types, and 11 of the 32 entries declare four tiers (unit integration regression trunk).

So a regression run records only testtype__regression on tests that are equally unit and integration members. Anything asking "has this artifact already been tested at tier X?" finds nothing and re-executes identical work. The tags exist today but can never satisfy a coverage query — which is what the v2 tier-delta work needs them for.

Fix

The membership set was already declared and already correct — it was simply never handed to pytest. matrix.test_types is in scope at both run-matrix-config call sites (which already pass matrix.cfg, matrix.test_target, matrix.runs_on); it was the one field not passed down.

  • run-matrix-config: new test_types input, exported as SPYRE_TEST_TIERS
  • _test_matrix.yaml: pass matrix.test_types at both call sites (main + retry)
  • tags.py: test_tiers() reads the set; one testtype__<tier> tag per member. Falls back to the invoked tier when unset, so a local make test and any un-updated caller keep tagging.

Two things worth reviewer attention

Membership is read, never inferred from a ladder. 13 of the 32 entries declare unit regression trunk without integration. Closing the ladder upward ("in unit ⇒ in everything above") would claim integration coverage for 13 legs that never ran it and silently skip real tests. There's a test asserting a ladder would still over-claim this matrix, so the shortcut can't creep back in.

The invocation tier is deliberately NOT tagged. Case tags are hashed into test_case_id (ingest_xml_si.v2_test_case_id), so tagging it gave one test a different identity per invoking tier — I verified the same test hashing to three different uuids before removing it. The invocation is a property of the run, and si_test_runs.test_type already records it.

Verification

  • A probe suite emits three testtype__ tags where it previously emitted one, with integration correctly absent
  • The ingest already on main parses all three with no change (one si_run_properties row per tag) — no ingest change needed
  • test_case_id is stable across invoking tiers (was three uuids, now one)
  • Pod-level retry needs nothing: the failed-suite descriptor is toJSON(matrix), so test_types is rebuilt with the rest of the entry
  • Two mutations — reverting to one tier, and closing the ladder — each fail the new tests
  • 13 new tests; ruff 0.14.0 check + format clean; both YAML files parse and both call sites asserted wired

Scope

Producer only. No change to which tests run — only to what the JUnit XML records. Safe to land before the consumer, and worth landing first: tags feed the v2 identity hash, and spyre_v2.test_case_runs currently holds 0 rows for spyre-inference, so re-keying costs nothing now and would orphan identities later.

result_tags() stamped one testtype__ tag, from SPYRE_TEST_TIER -- the tier the run was
INVOKED as. But a test's tier membership is a set: _test_matrix.yaml declares it per
matrix entry as `test_types`, and 11 of the 32 entries declare four tiers
("unit integration regression trunk").

So a regression run recorded only testtype__regression on tests that are equally unit
and integration members. Anything asking "has this artifact already been tested at tier
X" then finds nothing and re-executes identical work -- the tags existed but could never
satisfy a coverage query.

The membership set was already declared and already correct; it was simply never handed
to pytest. matrix.test_types is in scope at both run-matrix-config call sites (which
already pass matrix.cfg and matrix.runs_on) and was the one field not passed down. It now
threads through as SPYRE_TEST_TIERS and yields one tag per member.

Membership is READ, never inferred from a tier ladder. 13 of the 32 entries declare
`unit regression trunk` without `integration`, so closing the ladder upward would claim
integration coverage for 13 legs that never ran it and silently skip real tests. A test
asserts a ladder would still over-claim this matrix, so the shortcut cannot creep back in.

The invocation tier is deliberately NOT tagged. Case tags are hashed into test_case_id
(ingest_xml_si.v2_test_case_id), so tagging it gave one test a different identity per
invoking tier -- verified: the same test hashed to three uuids. It is a property of the
run, and si_test_runs.test_type already records it.

Verified end to end: a probe suite emits three testtype__ tags where it previously
emitted one, `integration` correctly absent; the ingest already on main parses all three
with no change, and the identity is now stable across invoking tiers. The pod-level retry
needs nothing -- the failed-suite descriptor is toJSON(matrix), so test_types is rebuilt
with the rest of the entry. Two mutations (reverting to one tier, and closing the ladder)
each fail the new tests.

Signed-off-by: Ashok Pon Kumar <ashokponkumar@gmail.com>
@github-actions

Copy link
Copy Markdown
Contributor

👋 Hi! Thank you for contributing.
Just a reminder: Make sure that your code passes all the linting checks, otherwise your PR won't be able to be merged. To do so, run ./format.sh.
Now you are good to go 🚀.

We also recommend installing prek and configuring it to check your code before every local commit.

@joerunde

Copy link
Copy Markdown
Collaborator

/claude-review

Raw test tier; resolved via `make print-test-type` and exported as
SPYRE_TEST_TIER for the testtype__<tier> tag. Empty = Makefile default.
Raw test tier this run was INVOKED as; resolved via `make print-test-type` and
exported as SPYRE_TEST_TIER for the invoked_as__<tier> tag. Empty = Makefile

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 doc now points at an invoked_as__<tier> tag that is never emitted. The PR deliberately stops tagging the invoked tier as a case tag (see test_tier() docstring and test_the_invocation_is_never_a_case_tag), so no invoked_as__ tag exists anywhere. Suggest describing what SPYRE_TEST_TIER is actually used for now: the run-row test_type and the test_tiers() fallback.

Comment thread tests/test_tags.py
doc = yaml.safe_load(_MATRIX.read_text(encoding="utf-8"))
jobs = doc["jobs"]
include = jobs["test"]["strategy"]["matrix"]["include"]
return [entry["test_types"].split() for entry in include if entry.get("test_types")]

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.

The actual behavior this PR changes -- the wiring -- is not covered by any test. Nothing asserts that run-matrix-config exports SPYRE_TEST_TIERS from its new test_types input, and nothing asserts that _test_matrix.yaml passes test_types: ${{ matrix.test_types }} at either call site. Delete either wire and every test here still passes; the tags silently degrade to the invoked-tier fallback in CI. The PR description says "both call sites asserted wired," but I do not see that assertion -- these tests only exercise tags.py in-process plus the matrix declarations. Consider a small YAML-parse test that greps both call sites for test_types: ${{ matrix.test_types }} and the action for the SPYRE_TEST_TIERS export.

Comment thread tests/test_tags.py
"""Guards the source of truth itself: if `test_types` ever disappears from the
matrix, the tags silently degrade to the invoked tier via the fallback."""
sets = _declared_sets()
assert len(sets) >= 20, f"expected the full shard matrix, got {len(sets)} entries"

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 guard is looser than its docstring claims. The matrix has 32 entries; >= 20 lets up to 12 entries drop test_types (and silently fall back to the invoked tier) without failing. Since _declared_sets() filters out entries with no test_types, a per-entry regression is invisible here. Assert that every include entry declares test_types (e.g. compare against the full include count) rather than a floor of 20.

@github-actions

Copy link
Copy Markdown
Contributor

Reviewed the tier-tagging change: the test_tiers() helper in tags.py, the SPYRE_TEST_TIERS export in run-matrix-config, both _test_matrix.yaml call sites, and the new tests/test_tags.py. The core logic is sound. Because each test-*-shard target uses a fixed MARK_OVERRIDE independent of TEST_TYPE, a leg runs the same tests regardless of the invoking tier, so tagging with the full declared membership set is correct rather than over-claiming.

No Spyre hardware was needed for this producer-only change. I ran the 13 CPU tests locally (all pass) and confirmed via --collect-only that they are collected and spread across the 8 smoke shards, so they do run in CI on single-card jobs, which is sufficient for CPU-only tests. I also confirmed all 32 matrix include entries declare test_types and that both run-matrix-config call sites (main and retry) pass it.

Three inline comments, all low-to-medium: the actual wiring (action export plus both call sites passing test_types) has no test guarding it despite the PR description claiming the call sites are "asserted wired"; the matrix guard uses >= 20 where 32 entries exist, so up to 12 could silently drop test_types; and the run-matrix-config doc references an invoked_as__<tier> tag that is never emitted. No correctness bugs found.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants