Repository navigation
test-guard(repo_tests): count the tests that run nothing, and unwrap four that could not fail (#15189) - #15254
Conversation
…four that could not fail (#15189)
✅ SSOT Configuration Compliance: Passing🎉 No new hardcoded values of either class — Known backlog in |
Review pass complete — no blocking findingsThe reviewer independently re-derived every number in the PR body rather than taking it on trust: the population (patterns read live from The ratchet is bidirectional, which is the thing that most often goes wrong here: it fails on AC5 (the contrast mutation demonstrated red in CI) is honestly disclosed as not met — mutations were run locally and no red commit was pushed. The PR correctly says Two detector gaps found, both minor, both false-positive directions that cannot hide a vacuous test, and neither triggered by any of the 11 sites today. Filed separately rather than held against this PR. |
|
Detector gaps filed as #15263 — |
Thinking Path
#15189 asked for three sweeps. Running them was only possible after #15195 landed as
c5306fe85, because until then the AST guard treated anassertthat was present as one that could fire — so nine of the ten functions that started this issue read as defended.The merged detector was read first and called, not re-implemented.
repo_tests/collected_test_modelalready owns "what pytest collects" and "can this assertion fire"; the third sweep is a third question asked of the same model, so it is a new function in that module rather than a second AST walk. Re-implementing the walk is how #14927 and #15018 happened.The third shape needed a definition narrow enough to survive contact with 27,000 tests. The rule chosen is the strictest one that still catches
test_migrated_files_import: every statement in the body is a no-op —pass,..., a bare string, or a bare-nameprint(...). One statement that calls anything and the function is out of scope, because "it does not raise" is a thin assertion but a real one. A test declaring@pytest.mark.skip/skipif/xfailis honest about not running and is not the subject. An over-flagging guard gets switched off, which is worse than the blindness.Counts were taken before anything was touched, and only the cheap, unambiguous conversions were made. What was left is named below with the reason.
What Changed
Sweep counts, measured on this branch before any fix:
swallowed_assertions_in(#15195)_offending_returns(#14920)empty_bodies_inPer tree — sweep 1 before:
autobot-backend6,autobot-infrastructure3. Sweep 2 before:autobot-backend86,autobot-infrastructure127,autobot-npu-worker7. Sweep 3:autobot-backend11, every other tree zero.New detector.
repo_tests/collected_test_model.pygainsdoes_nothing,is_declared_not_running,empty_bodiesandempty_bodies_in. No ratchet and no budget, matching the module's existing contract.New guard.
repo_tests/tests_that_execute_nothing_test.py— a per-tree down-only ratchet pinned at the measured 11, every other tree pinned at zero by derivation, a population floor so a collapsed walk cannot read as a drained tree, and a self-test that exercises every flagging branch and every sparing branch.Four tests converted, each of which now holds an assertion that reaches pytest:
autobot-backend/config/config_migration_integration_test.py:87test_secrets_service_config_migration— assertions moved out from underexcept Exception: pass.autobot-infrastructure/shared/scripts/test_configuration.py—test_config_values,test_config_manager,test_config_validationlost theirtry: … except Exception: return Falsewrappers. The driver contract is unchanged:main()already wraps every call in its own handler and records a crash as a failed test, so the verdict is identical and the traceback is no longer discarded.Ratchets lowered, never raised.
_SWALLOWED_ASSERTIONS:autobot-backend6 → 5, andautobot-infrastructuredeleted rather than left at zero._KNOWN_OFFENDERS["autobot-infrastructure"]: 127 → 121 — those six returns stopped being counted because the assertions beside them became live, which is the guard's own exemption rule, not a weakening of it.tests_that_return_instead_of_asserting_test.pyfinishes at 599 lines, underMAX_LINES = 600, with noKNOWN_LARGEentry added.Left deliberately, and why. Five swallowing sites remain:
autobot-backend/config/config_consolidation_p2_test.py:202(one 200-line function with tentry/assert/exceptsections — the same restructuring #15166 did for its siblingcache_consolidation_p4_test.py, and its own piece of work), and four*_full_pipelinemethods atautobot-backend/tests/integration/test_causal_framework_integration.py:152,394,707,937that catchAssertionErrorinto a scenario dataclass a report aggregator consumes. All eleven empty-bodied tests remain: each needs a real integration test written against a live endpoint or a realKnowledgeBase. Half-converting a population is how a ratchet gets stuck, so all sixteen are recorded under a down-only ceiling instead.Verification
Guards, after the change —
repo_tests/tests_that_return_instead_of_asserting_test.py,repo_tests/tests_that_execute_nothing_test.py,repo_tests/collected_test_model_test.py: 21 passed.repo_tests/python_file_size_ratchet_test.py: 31 passed.Contrast mutation 1 — the converted test now fails when its subject breaks.
ConfigManager.setwas mutated to store the wrong value, then reverted from a pristine copy confirmed withdiff -q:1 passed1 failed—AssertionError: assert 'MUTATED' == 'test_secrets_key'1 passedThe third row is the point: the same broken subject, and the pre-fix test reports green.
Contrast mutation 2 — the driver's swallow was hiding a real break. Same file, run directly:
7 passed, 7 warnings(every one aPytestReturnNotNoneWarning)3 failed, 4 passedThe three failures are pre-existing and are recorded as a finding below, not introduced here.
Contrast mutation 3 — the new guard catches the shape. A planted
def test_planted_empty_body(): pass+print("✅ subject verified")inautobot_shared/(a tree pinned at zero by derivation) fails the guard by name:Counter-mutation — the guards spare legitimate code. A probe file in the same clean tree holding a
try/except ValueErrorthatpytest.skips, atry/except Exceptionthat re-raises, a thin smoke test that only calls its subject, and a@pytest.mark.skiptest with an empty body: 13 passed, nothing flagged by either guard. The same shapes are pinned as synthetic cases in the new guard's self-test, so the sparing branches are proven rather than described.AC 6 — was
cache_consolidation_p4_test.pyever exempted? No, and nothing suppressed the warning either. It appears in no skip, ignore or exemption list anywhere in the repository; its only two mentions outside itself are a path mapping inscripts/migrate_tests.py:161and a historical note in the ratchet.pytest.inihas nofilterwarningssection at all, itsnorecursedirscovers only VCS and build directories, andaddoptsignores onlyautobot-slm-backend/ansibleandautobot-npu-worker/resources. The warning was emitted into a green run for its whole life and nothing was configured to catch it.What could not be verified. Every run above is local, on an interpreter 90 of 206 declared dependency floors below CI — the suite prints that banner itself. These runs demonstrate the shape of each guard and each fix; CI is the authority on whether they pass where they ship. The AC asking to "show CI fails" on a reintroduced swallowing test is met only to the extent that the planted probe fails the guard locally; no deliberately-red commit was pushed to prove it in CI.
filterwarnings = error::pytest.PytestReturnNotNoneWarningremains off, correctly: 214 returns are still in the tree, and 121 of them are in a tree no pytest invocation collects, so the config would report clean while more than half the population sat outside its reach.Feasibility of detecting the third shape: confirmed and cheap. 74 lines in the shared model, driven off the collection model that already existed, and the population is 11 in a single tree. The reason it was never measured is that nobody had asked the question, not that it was hard.
Model Used
Claude Opus 5 (1M context)
Refs #15189 — not
Closes. Two criteria are short: the contrast mutation is demonstrated locally rather than as an observed CI failure, and the sixteen remaining sites (5 swallowing + 11 empty-bodied) are measured and ratcheted down-only rather than converted.