05 — Planning — Subsystem Test Map¶
The planning subsystem (RunPlanManager) is the authoritative rule-evaluation
point for a job start: it builds the effective start plan (queue + payload +
preflight), evaluates blocker/modifier rules and their fan-out, computes the
confirmation token for a reduced run, and produces the runtime path-block report
when a rule fires mid-job. Covered by 89 tests across 4 files.
Source: custom_components/eufy_vacuum/planning/
Architecture reference: 18 — The Access Graph
Coverage map¶
| Source module | Stmts | Cov | Test files | Layer | Mocking |
|---|---|---|---|---|---|
run_plan.py |
604 | 93% | test_run_plan_start_plan.py, test_run_plan_manager.py, test_run_plan_helpers.py (unit), test_water_vocabulary_boundary.py (unit) |
int + unit | bare x1 |
What's tested¶
- Start-plan gates (
SP) —_build_effective_start_planacross the state machine: ready blank graph, partial graph →incomplete_access_graph, rules without graph →access_graph_required_for_rules, single-dock without grants →access_graph_required, a matching direct blocker (→ blocked rooms + confirmation), a matching modifier (→ modified rooms), and access-dependency cascade (a blocked parent blocks its child). - Modifier fan-out (
SP) — a rule'sfan_out_room_idsapply to a derived target, plus the per-target guard branches (non-numeric / unknown / self / not-selected / blocked targets dropped; no-entity / no-match / empty-changes early-continues). - Runtime path-block report (
SP) —get_runtime_path_block_report(the #11 regression guard): reachability propagation marks a room reachable via an accessible parent; remaining rooms are classified directly- vs indirectly-blocked; idle job → None. - Water-usage estimation + helpers (unit) —
estimate_job_water_usage,_settings_profile_display, water-rate/level math. - Canonical/provider vocabulary boundary (
WV, unit, added 2026-08-07) —test_water_vocabulary_boundary.py(15 tests). Core owns the canonical water KEY SPACE (off/low/medium/high) that the rate table is keyed on; it may not assume a provider's vocabulary equals it. Both shipped brands hide the difference — Roborock's declared values already ARE the canonical keys, and Eufy's"Low"reaches"low"through a generic case-folding rule — so neither exercises the declared-alias path, and it could regress unnoticed. These tests use a brand whose water words resemble nothing canonical (SynthMist,SynthFlood), so aliases are the only possible route, with a mutation control (WV-2) that withdraws them and requires the same values to stop resolving.
How it's tested¶
RunPlanManager(manager) over the real manager fixture; _seed(...) lays down
managed rooms and merges per-room overrides, _blocker(...) / _modifier(...)
build rule dicts, and binary-sensor states drive rule matches.
Known gaps¶
run_plan.py (93%) leaves two fan-out per-target guards (the int()-except on a
non-numeric target id and the not-selected continue), the module-level helper
except-paths (_safe_int / _safe_float TypeError/ValueError arms), the
by_time wash-cycle branch and other water-usage partial branches, and the
defensive early-returns / disabled-rule and non-dict-room skips inside
get_runtime_path_block_report (no-queue, no-remaining, structural-issue, and
unchanged-signature returns) — all defensive or low-value branch tails.