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
207 lines
7.1 KiB
Python
207 lines
7.1 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright © Advanced Micro Devices, Inc., or its affiliates.
|
|
# SPDX-License-Identifier: MIT
|
|
"""
|
|
Census of the integration-test suite (ALMIOPEN-2221, migration phase 1).
|
|
|
|
Runs the integration-test binary's `--gtest_list_tests` and parses the full
|
|
parametrized expansion into a manifest. This is the *denominator* for the C++
|
|
graph -> bundle migration: every C++ graph test that must be accounted for, with
|
|
the seed and parameter string GTest reports for it.
|
|
|
|
Why the binary, not the source: GTest's own enumeration expands every
|
|
testing::Combine / ValuesIn / typed instantiation into the real set of
|
|
Suite.Test names — the same expansion used at run time, and the only source of
|
|
truth for "how many tests are there." The ~17 .cpp files expand into thousands
|
|
of cases.
|
|
|
|
The manifest classifies each case:
|
|
|
|
* graph — a C++ graph test (suite under a Smoke/ or Full/ instantiation,
|
|
name starts with IntegrationGpu...). Its graph is built
|
|
programmatically in C++; these are what the capture step turns
|
|
into bundles. Their GetParam() carries the seed inline.
|
|
* bundle — an existing on-disk bundle test (GetParam() is a path to a
|
|
.json bundle). Already in the target format; nothing to migrate.
|
|
* other — anything else (e.g. perf microbenchmarks). Reported so the count
|
|
reconciles exactly and nothing is silently uncategorized.
|
|
|
|
Output is JSON on stdout (or --out FILE), plus a human summary on stderr.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from dataclasses import dataclass, field, asdict
|
|
|
|
|
|
# A GetParam() value that is a quoted path to a .json file marks an existing
|
|
# bundle test (the param IS the bundle on disk). Anything else is an inline-built
|
|
# C++ graph param (dims/seed/layout) or a non-parametrized case. Matched against
|
|
# the param text after the "GetParam() = " prefix has been stripped.
|
|
_BUNDLE_PARAM_RE = re.compile(r'^"(?P<path>.*\.json)"$')
|
|
_SEED_RE = re.compile(r"seed:(?P<seed>\d+)")
|
|
|
|
|
|
@dataclass
|
|
class Case:
|
|
suite: str # full gtest suite name, e.g. "Smoke/IntegrationGpuConvForwardFp16"
|
|
name: str # case name, e.g. "Correctness/0"
|
|
full: str # "{suite}.{name}" — the gtest identifier
|
|
kind: str # "graph" | "bundle" | "other"
|
|
seed: int | None = None # parsed from GetParam() for graph cases
|
|
param: str = "" # raw GetParam() text (after the '= '), if any
|
|
|
|
|
|
@dataclass
|
|
class Census:
|
|
total: int = 0
|
|
counts: dict = field(default_factory=dict)
|
|
cases: list = field(default_factory=list)
|
|
|
|
|
|
def classify(suite: str, param: str) -> str:
|
|
"""Bucket a case from its suite name and raw GetParam() text."""
|
|
if _BUNDLE_PARAM_RE.search(param):
|
|
return "bundle"
|
|
# Migration target: IntegrationGpu* suites build a graph and verify it
|
|
# across GPU plugins — these are the tests whose graphs can be bundled
|
|
# and re-executed with any plugin.
|
|
leaf = suite.split("/", 1)[-1]
|
|
if leaf.startswith("IntegrationGpu"):
|
|
return "graph"
|
|
return "other"
|
|
|
|
|
|
def parse_list_tests(text: str) -> Census:
|
|
"""Parse `gtest --gtest_list_tests` output into a Census.
|
|
|
|
Format (two indentation levels):
|
|
SuiteName.
|
|
CaseName # GetParam() = <param>
|
|
CaseName
|
|
OtherSuite.
|
|
...
|
|
A line with no leading space that ends in '.' is a suite header; an indented
|
|
line is a case under the most recent suite.
|
|
"""
|
|
census = Census()
|
|
suite = None
|
|
for raw in text.splitlines():
|
|
if not raw.strip():
|
|
continue
|
|
if not raw.startswith(" "):
|
|
# suite header line, e.g. "Smoke/IntegrationGpuConvForwardFp16."
|
|
suite = raw.strip().rstrip(".")
|
|
continue
|
|
if suite is None:
|
|
continue # defensive: indented line before any suite header
|
|
line = raw.strip()
|
|
param = ""
|
|
if "#" in line:
|
|
case_name, _, comment = line.partition("#")
|
|
case_name = case_name.strip()
|
|
m = re.search(r"GetParam\(\)\s*=\s*(.*)$", comment.strip())
|
|
param = m.group(1).strip() if m else ""
|
|
else:
|
|
case_name = line
|
|
kind = classify(suite, param)
|
|
seed = None
|
|
if kind == "graph":
|
|
sm = _SEED_RE.search(param)
|
|
seed = int(sm.group("seed")) if sm else None
|
|
census.cases.append(
|
|
Case(
|
|
suite=suite,
|
|
name=case_name,
|
|
full=f"{suite}.{case_name}",
|
|
kind=kind,
|
|
seed=seed,
|
|
param=param,
|
|
)
|
|
)
|
|
census.total = len(census.cases)
|
|
for c in census.cases:
|
|
census.counts[c.kind] = census.counts.get(c.kind, 0) + 1
|
|
return census
|
|
|
|
|
|
def run_list_tests(binary: str) -> str:
|
|
result = subprocess.run(
|
|
[binary, "--gtest_list_tests"],
|
|
capture_output=True,
|
|
text=True,
|
|
check=True,
|
|
)
|
|
return result.stdout
|
|
|
|
|
|
def main() -> int:
|
|
ap = argparse.ArgumentParser(description=__doc__)
|
|
ap.add_argument(
|
|
"binary",
|
|
help="path to the built hipdnn_integration_tests executable",
|
|
)
|
|
ap.add_argument(
|
|
"--out",
|
|
help="write the JSON manifest here (default: stdout)",
|
|
)
|
|
ap.add_argument(
|
|
"--graph-only",
|
|
action="store_true",
|
|
help="emit only graph-kind cases (the migration denominator)",
|
|
)
|
|
args = ap.parse_args()
|
|
|
|
try:
|
|
text = run_list_tests(args.binary)
|
|
except (subprocess.CalledProcessError, FileNotFoundError) as exc:
|
|
print(f"census: failed to run {args.binary}: {exc}", file=sys.stderr)
|
|
return 1
|
|
|
|
census = parse_list_tests(text)
|
|
|
|
# Flag graph cases missing a seed: the capture step relies on a known seed,
|
|
# so a graph case without one is something to look at (none expected today).
|
|
seedless = [c.full for c in census.cases if c.kind == "graph" and c.seed is None]
|
|
|
|
print("== integration-test census ==", file=sys.stderr)
|
|
print(f" total cases: {census.total}", file=sys.stderr)
|
|
for kind in sorted(census.counts):
|
|
print(f" {kind:7s}: {census.counts[kind]}", file=sys.stderr)
|
|
suites = sorted({c.suite for c in census.cases if c.kind == "graph"})
|
|
print(f" graph suites to migrate: {len(suites)}", file=sys.stderr)
|
|
if seedless:
|
|
print(
|
|
f" WARNING: {len(seedless)} graph cases have no seed in GetParam():",
|
|
file=sys.stderr,
|
|
)
|
|
for full in seedless[:10]:
|
|
print(f" {full}", file=sys.stderr)
|
|
|
|
cases = census.cases
|
|
if args.graph_only:
|
|
cases = [c for c in cases if c.kind == "graph"]
|
|
|
|
manifest = {
|
|
"total": census.total,
|
|
"counts": census.counts,
|
|
"graph_suite_count": len(suites),
|
|
"seedless_graph_cases": seedless,
|
|
"cases": [asdict(c) for c in cases],
|
|
}
|
|
out_text = json.dumps(manifest, indent=2)
|
|
if args.out:
|
|
with open(args.out, "w") as f:
|
|
f.write(out_text + "\n")
|
|
print(f" manifest written: {args.out}", file=sys.stderr)
|
|
else:
|
|
print(out_text)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|