test(adb-off audit): name the carve-outs, drive the paths the audit names (t25)

The t24 review's four observations on the t23 ADB-off audit. No production code
changes: button.py is untouched, deliberately (see O1).

O1 — THE PAIR ADB CARVE-OUT, ACCEPTED AND NAMED. The button's press builds its own
AdbMediaSessionPoller and connects without ever reading the entry's option. I
searched for a production route that reaches the press with ADB off and found
NONE, so no gate was added:
  * `button.async_setup_entry` is the ONLY place that entity is created, and it
    creates it only while the option is on (button.py:34-35);
  * every options save runs through `NvidiaShieldOptionsFlow(OptionsFlowWithReload)`,
    which reloads the entry and re-runs that setup — so turning ADB off takes the
    button away (config_flow.py:1340-1353);
  * the press is user-initiated in any case — the same class as the already
    declared `test_connect` pairing probe.
It is now a NAMED carve-out in the audit's `CARVE_OUTS` table, with what it does,
why that is safe, and the assertions behind both. The measurement is DRIVEN rather
than quoted: the press is executed with the option OFF and ON and asserted to cost
the same 1 ctor / 1 connect / 1 disconnect, so the carve-out's substance is a test
result and not prose. A meta test checks every carve-out's named assertion really
exists, so a carve-out cannot point at a phantom. MUTATION-CHECKED: making
`async_setup_entry` add the entity unconditionally fails three of the new tests.
The intent button is explicitly NOT a carve-out (it can exist for a disabled entry
and refuses through the t23 accessor gate) — stated in the same entry.

O2 — THE AUDIT NOW EXECUTES WHAT IT NAMES. `_open_logcat_stream` and the logcat
ladder are driven both ways: two new rows in the parametrized `AUDITED_PATHS` table
(so the disabled case costs zero traffic) plus dedicated assertions — the open
RAISES "ADB is switched off" in a `pytest.raises(match=...)`, and the ladder
RETURNS at once with `_logcat_failures == []` and `_logcat_disabled is False`, i.e.
a local option is not booked as a device failure. The anti-overcorrection pin is
kept in the same class: with ADB ON, five real ladder failures DO auto-disable.
MUTATION-CHECKED: removing either t23 logcat gate fails three of these tests.

O3 — THE ROTTABLE FIGURE IS GONE. The statement_blocks bullet no longer states a
count ("currently 28" was stale within one unit of work); the band (~2%) is the
policy, the current walk lives in the header the sweep prints, and the comment now
says why no band carries a count. The relative_imports bullet lost its illustrative
figure for the same reason.

O4 — THE ONE WRITE IS NAMED AS A DEBT. The audit's module docstring now states
exactly what "nothing is cached" means: the single write a disabled path can still
make is `_app_label_failed.add(package)` in `_dump_label` — a retry DEBT, not an
answer. It is pinned by a test: with ADB off the debt is recorded while
`_app_labels` stays EMPTY and the lookup still answers None; with ADB back on the
probe runs, the label is cached and the debt is settled (the discard that the code
already does).

FAIL-CLOSED, CONFIRMED AND LOCATED. The harness change (`entry=None` now reads as
ADB off) matches production because both read the option the same way: the manager
returns "disabled" for a missing option, and `__init__.py` builds no poller for the
same missing option — asserted by two new tests (an entry with `options={}`, and a
manager with no entry at all) and by a source assertion on `__init__.py`'s own
`if not entry.options.get(CONF_ADB_ENABLED, False):`. No production path is
affected: production always constructs the manager WITH the entry, and for an entry
whose option is on nothing changed at all.

No regressions: 942 passed / 5 skipped (V1), 943 / 4 (V2) — the +19 are the new
tests. ruff clean, drift exit 0, the real-HA pattern collects and runs its 3 files.
The device side remains a scripted double: the claim proven is that nothing was
sent, not what firmware would do.
This commit is contained in:
James Mackay committed 2026-10-12 13:46:55 +01:00
1 parent de1c34777d
commit 7ed982801a
3 files changed
+410 -8

No files matched your search

+14 -8
View File
@@ -158,14 +158,20 @@ HA_CALL_OWNERS: dict[str, tuple[str, str]] = {
#: sweep green) is what the 2 catches. This is the project's long-standing
#: canary (t62 wrote it as "two below the count"), now stated once instead of
#: being implied by each raise.
#: * relative_imports: ~2.5%, currently 7. The count is dominated by package-
#: relative imports that legitimately churn when files move; a collapse (an
#: unparsed module, a stale glob) is orders of magnitude, not 7.
#: * statement_blocks: ~2%, currently 28. This counter moves in BULK with every
#: edit, which is exactly why a fixed small margin would be wrong here (the
#: t19 complaint: at 1300 vs 1322 a 23-statement refactor tripped it). A 2%
#: band leaves a normal refactor alone while a parse failure still shows up as
#: a large drop.
#: * relative_imports: ~2.5%. The count is dominated by package-relative imports
#: that legitimately churn when files move; a collapse (an unparsed module, a
#: stale glob) is orders of magnitude, not a handful.
#: * statement_blocks: ~2%. This counter moves in BULK with every edit, which is
#: exactly why a fixed small margin would be wrong here (the t19 complaint: at
#: 1300 vs 1322 a 23-statement refactor tripped it). A 2% band leaves a normal
#: refactor alone while a parse failure still shows up as a large drop.
#:
#: No band states a COUNT: the current walk is in the header above and printed by
#: every sweep run, so a figure in this prose can only rot — a previous revision
#: carried exactly such a figure for statement_blocks and it was stale within one
#: unit of work (t24/O3). The BANDS are the policy and are deliberately the only
#: numbers here; a floor, once re-derived from a re-measured walk, is named next to
#: the counter that owns it.
#: * awaited_calls: 1 (measured 6, floor 5 — DELIBERATE, see below). Stated
#: plainly: with six awaited HA calls a percentage margin is meaningless, so
#: this floor is the weakest one here. It is still worth having — a walk that