📦 WP-128 — refine(tests): regroup flat-naming test functions into classes¶
Engineering log · Phase 11 — Rolling, toward v0.5.0
🔗 Depends on WP-117 · Needed by none
WP-128 — a shared prefix is not a shared caller¶
WP-117's own count -- "8 groups across 6 files" -- was a one-off human read, not a reproducible measurement, and the row it left behind said so: a first-shared-word ast scan over test_ names returns roughly 90 candidate groups, because this suite's house style already writes full descriptive sentences (test_boundary_sits_at_midpoint_of_overlap_band) that share a topic noun with unrelated tests -- indistinguishable from genuinely flat, mechanical enumeration (test_<subject>_<case>) by prefix-matching alone.
The discriminator that breaks the tie: a candidate prefix confirms only if it is a contiguous token subsequence -- prefix, suffix, or infix -- of some ast.Call function-name target actually present in the same file (Attribute.attr or Name.id, underscore-split, leading-underscore tokens dropped). A house-style sentence sharing a topic noun calls nothing that looks like it and never confirms; test_check_data_a/b/c calling check_data(...) does. Confirmed groups need 3+ members to enforce, matching the size floor tests/eval/test_tile_merge.py's own precedent already implied by example. Verified both directions against real cases in this tree: correctly includes check_data, plan_archives, verify_split, configure_optimizers, warmup_decay, on_after_batch_transfer, detections_to_predictions, and rotated_topk (confirmed as an infix of o2o_rotated_topk); correctly excludes deployed_view-style near-misses and the a_named_worker_count_* / an_auto_chosen_* house-style family WP-117's row already flagged as the hard case.
New scripts/lint/audit_flat_test_groups.py owns this rather than extending audit_test_doctests.py (129) -- that script's own docstring disclaims scanning for anything but missing doctest Examples, and stapling an unrelated structural check onto it would have made neither job legible from the file alone. Same shape as every other scripts/lint/ checker: main(argv) -> int CLI, a flat-test-group-audit local hook gated on files: ^(tests/.*/test_[^/]+\.py|scripts/_tests/test_[^/]+\.py)$ (pure repo content, not installed-environment state, so no always_run), and scripts/_tests/test_audit_flat_test_groups.py as the functional core -- synthetic tmp_path fixtures per confirmed/rejected case, plus the two "live tree is currently clean" sanity tests the hook itself depends on staying true.
Nine files regrouped under class Test<Subject>: once the discriminator was run against the whole tests/ and scripts/_tests/ tree rather than continuing WP-117's by-eye survey: test_verify.py, test_download.py, test_coco_eval.py, test_schedule.py, test_dota_parse.py, test_coco.py, test_obb_head.py, test_tiling.py, test_proto.py, test_module.py, and scripts/_tests/test_audit_docs_present.py (all 12 of its check-function pairs, for whole-file consistency, though the discriminator's crude grouping key only flagged 6) and test_audit_docs_site.py. Regrouping moves node ids, not test bodies -- Python resolves a module-level helper name at call time, not at class-body-definition time, so relocating a scattered function into a contiguous class needed no change to whatever module-level fixtures it called. Every citation of a moved node id in docs/ROADMAP.md (rows 044, 051, 110) and docs/ASSUMPTIONS.md (A45) was checked against a grep for the old id and updated; row 044's citation had already drifted stale before this WP touched the file (test_oracle moved under a pre-existing TestOracleRoundTrip by earlier, unrelated work) and was corrected in the same pass since the row was already open.