10 — Dock — Subsystem Test Map¶
The dock subsystem owns mop-wash / mop-dry / dust-empty actions: it resolves the upstream button entity per action, gates each action against capability, dock state, and job state, dispatches the gated action, and records dock-event timestamps + debounced counters. Covered by 26 tests in 1 file.
Source: custom_components/eufy_vacuum/dock/
Architecture reference: docs/dev/14-dock-manager.md
Coverage map¶
| Source module | Stmts | Cov | Test file | Layer |
|---|---|---|---|---|
manager.py |
167 | 97% | tests/integration/test_dock_manager.py |
integration |
(__init__.py is trivial; the dock services layer is covered separately by
tests/integration/test_services_dock.py.)
What's tested (DK, integration)¶
- Helpers —
_safe_int,_display_label. - Event recording —
record_dock_event(timestamp + counter increment, with the per-event debounce that blocks a double-count), thelast_dry_startduration field,set_dock_event_count(overwrite + unknown-type error), andget_dock_events. - The gating engine —
get_dock_action_statusacross every branch:ready,unsupported_feature,missing_action_entity,job_active,not_docked, the action-specificalready_washing/already_drying/not_drying/already_emptying, anddock_busy(adapterhard_service_states). - Dispatch —
_async_run_dock_action+ the fourasync_*wrappers: an allowed action presses the button (performed=True), a gated action returnsperformed=Falsewith the gate reason. - Entity resolution —
_get_dock_action_entityresolves a present button from the adapter'sdock_events.action_buttons(the test registers a config withentity_suffixes); an action absent from that map resolves toNone. A token-fallback test [DK-17] also exercises resolution of a differently-named button via the adapter'stoken_setswhen noentity_suffixmatches (firmware-naming drift).
How it's tested¶
DockManager(manager) against the real manager fixture. The gating engine
calls three manager methods (get_vacuum_capabilities, get_lifecycle_state,
get_active_job) plus _get_dock_action_entity; a _ready(...) helper
monkeypatches all four to a baseline "ready" context, and each test overrides
one field to drive a single gate branch. Dispatch tests register a fake
button.press service and assert it was (or wasn't) called.
Known gaps¶
manager.py (98%) has one uncovered line, a narrow fall-through branch:
- Line 94 — inside
_get_dock_action_entity, the registry-only arm of theentity_suffixesloop (registry.async_get(entity_id)matches an entity that is in the registry but not inhass.states). The states-present arm and thetoken_setsfallback are both covered ([DK-15], [DK-17]).
The except Exception: in record_dock_event's debounce timestamp-parse
(malformed *_last_counted_at, line 392) is now covered:
test_record_event_malformed_debounce_timestamp [DK-2b] registers a non-zero
debounce_seconds and seeds an unparseable stored timestamp, so
datetime.fromisoformat raises and the recovery branch is genuinely exercised
(its _LOGGER.debug log body remains # pragma: no cover).
The remaining gap is a defensive recovery branch; the gating, dispatch, entity-resolution (suffix + token fallback), and event-counting logic (including malformed-timestamp recovery) are covered.