diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 66e8c425e..9b29ba409 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -93,7 +93,7 @@ repos: files: ^\.github/workflows/.*\.yml$ pass_filenames: false - id: check-registered-tests - name: check registered tests have CI registry + name: validate registered test CI registries entry: python3 scripts/ci/check_registered_tests.py language: system files: ^test/registered/.*\.py$ diff --git a/scripts/ci/check_registered_tests.py b/scripts/ci/check_registered_tests.py index 66def25ff..7c012b45d 100755 --- a/scripts/ci/check_registered_tests.py +++ b/scripts/ci/check_registered_tests.py @@ -1,7 +1,16 @@ #!/usr/bin/env python3 """ -Pre-commit hook: validate that all Python test files under test/registered/ -contain a CI registry call (register_cuda_ci, register_amd_ci, etc.). +Pre-commit hook: validate CI registry calls under test/registered/. + +1. Every test file must contain a CI registry call (register_cuda_ci, + register_amd_ci, etc.). +2. A CUDA test must not register a `{stage}-test-{runner_config}`-shaped suite + via the legacy single-string `suite=`: that form is not dispatchable via + /rerun-test. Use the modern `stage=`/`runner_config=` form instead -- it + resolves to the identical suite (CIRegistry.effective_suite is + f"{stage}-test-{runner_config}"), so same stage and same runner, just + /rerun-test-able. Legacy `suite=` stays valid for nightly/stress/weekly + + AMD/CPU/NPU suites whose names don't follow that shape. Reuses ut_parse_one_file() from ci_register.py (AST-based parsing) to match the same logic used by run_suite.py's collect_tests(). @@ -10,8 +19,14 @@ to match the same logic used by run_suite.py's collect_tests(). import glob import importlib.util import os +import re import sys +# Suite names of the form `{stage}-test-{runner_config}` are exactly what the +# modern stage=/runner_config= form produces, so a legacy suite= carrying this +# shape is always expressible (and should be expressed) the modern way. +_MODERN_SHAPE = re.compile(r"^(.+)-test-(.+)$") + def main() -> int: # Import ci_register directly to avoid pulling in all of sglang @@ -21,6 +36,7 @@ def main() -> int: ) ci_register = importlib.util.module_from_spec(spec) spec.loader.exec_module(ci_register) + cuda = ci_register.HWBackend.CUDA # Same filter as run_suite.py: skip conftest.py, __init__.py, and utils.py files = sorted( @@ -31,25 +47,55 @@ def main() -> int: if not files: return 0 - errors = [] + missing = [] + legacy_shape = [] # (file, suite, stage, runner_config) for f in files: try: registries, _has_main_entry = ci_register.ut_parse_one_file(f) - if len(registries) == 0: - errors.append(f) except Exception: # Skip files that can't be parsed (syntax errors, etc.) - pass + continue + if len(registries) == 0: + missing.append(f) + continue + for r in registries: + # Pure legacy form on a CUDA registry: suite set, stage/runner unset. + if not ( + r.backend == cuda + and r.suite is not None + and r.stage is None + and r.runner_config is None + ): + continue + m = _MODERN_SHAPE.match(r.suite) + if m: + legacy_shape.append((f, r.suite, m.group(1), m.group(2))) - if errors: + exit_code = 0 + if missing: print("ERROR: Files in test/registered/ missing CI registry call:") print(" Move manual-only tests to test/manual/.\n") - for f in errors: + for f in missing: print(f" {f}") print() - return 1 + exit_code = 1 + if legacy_shape: + print( + "ERROR: CUDA test(s) register a `{stage}-test-{runner_config}`-shaped " + 'suite via the legacy `suite="..."` form, which is not dispatchable ' + "via /rerun-test. Switch to the modern `stage=`/`runner_config=` form " + "(same stage, same runner):\n" + ) + for f, suite, stage, runner_config in legacy_shape: + print( + f" {f}\n" + f' suite="{suite}"' + f' -> stage="{stage}", runner_config="{runner_config}"' + ) + print() + exit_code = 1 - return 0 + return exit_code if __name__ == "__main__": diff --git a/test/registered/tokenizer/test_multi_detokenizer.py b/test/registered/tokenizer/test_multi_detokenizer.py index 1f6e56c13..2129961a6 100644 --- a/test/registered/tokenizer/test_multi_detokenizer.py +++ b/test/registered/tokenizer/test_multi_detokenizer.py @@ -17,7 +17,7 @@ from sglang.test.test_utils import ( write_github_step_summary, ) -register_cuda_ci(est_time=211, suite="base-b-test-1-gpu-large") +register_cuda_ci(est_time=211, stage="base-b", runner_config="1-gpu-large") register_amd_ci(est_time=345, suite="stage-b-test-1-gpu-small-amd") diff --git a/test/registered/unit/spec/test_eagle_worker_v2_topk1_fastpath.py b/test/registered/unit/spec/test_eagle_worker_v2_topk1_fastpath.py index 864b54ef1..da5d4545c 100644 --- a/test/registered/unit/spec/test_eagle_worker_v2_topk1_fastpath.py +++ b/test/registered/unit/spec/test_eagle_worker_v2_topk1_fastpath.py @@ -19,7 +19,7 @@ from sglang.srt.utils import get_device from sglang.test.ci.ci_register import register_cuda_ci from sglang.test.test_utils import CustomTestCase -register_cuda_ci(est_time=20, suite="base-b-test-1-gpu-small") +register_cuda_ci(est_time=20, stage="base-b", runner_config="1-gpu-small") DEVICE = get_device() diff --git a/test/registered/vlm/test_token_id_retokenize_e2e.py b/test/registered/vlm/test_token_id_retokenize_e2e.py index 8a7192bd7..8d1062a33 100644 --- a/test/registered/vlm/test_token_id_retokenize_e2e.py +++ b/test/registered/vlm/test_token_id_retokenize_e2e.py @@ -35,7 +35,7 @@ from sglang.test.test_utils import ( popen_launch_server, ) -register_cuda_ci(est_time=300, suite="base-b-test-1-gpu-large") +register_cuda_ci(est_time=300, stage="base-b", runner_config="1-gpu-large") def _data_uri():