Skip to content

test(cover,door-entry): restore 100% coverage on cover.py and fix doorbell dispatcher race - #876

Merged
GreenGrassBlueOcean merged 1 commit into
v2-phase1-architecturefrom
fix/ci-cover-tilt-coverage-and-doorbell-callback
Nov 8, 2026
Merged

GreenGrassBlueOcean merged 1 commit into
v2-phase1-architecturefrom
fix/ci-cover-tilt-coverage-and-doorbell-callback

Conversation

@GreenGrassBlueOcean

Copy link
Copy Markdown
Contributor

What is the problem

CI on v2-phase1-architecture (#232) is failing in test-coverage and PyPI Standards & Packaging (Run Pytest Suite & Coverage (Python 3.14)) for two distinct reasons:

  1. Strict 100.0% coverage drop in custom_components/myhome/cover.py (line 2675):
    • PR fix(cover): update Venetian blind tilt on intermediate Dimension 10 stops and level jumps (#868) #870 added the active-tilt status/poll branch in MyHOMECover.handle_event() (lines 2666–2677) with if (is_closed := getattr(message, "is_closed", None)) is not None: ... else: self._attr_is_closed = (self._attr_current_cover_position == 0).
    • test_poll_during_active_tilt_preserves_tilt_state only fed a parsed OWNAutomationEvent (*#2*0115*10*10*75*001*0##), where message.is_closed is False (non-None), leaving line 2675 at 0 hits (18,735 / 18,736 statements = 99.9%) and causing scripts/update_readme_coverage.py / scripts/verify_ownd_coverage.py to fail with exit code 1.
  2. Flaky executor-thread race in tests/test_door_entry_events.py::test_doorbell_fires_once_on_a_shared_bus:
    • Line 235 connected pri_dispatched.append (a C builtin method without @callback) directly to async_dispatcher_connect.
    • Because pri_dispatched.append lacks _hass_callback = True and is not a coroutine function, Home Assistant classifies it as HassJobType.Executor and schedules it on a background thread-pool worker instead of running it inline on the event loop. Under CI load, assert len(pri_dispatched) == 1 on line 246 evaluated 0 on the event loop thread microseconds before the worker thread appended the payload (the exact race class fixed in tests/test_multi_gateway.py by fix(test): wrap test dispatcher callbacks with callback() to eliminate executor thread race #850).
    • An AST audit of all async_dispatcher_connect and hass.bus.async_listen listeners across tests/ and custom_components/myhome/ also found un-decorated listeners in tests/test_issue_667_intercom_trace.py.

Addresses #232
Related to #850, #868, #870


What is the solution

  1. Cover is_closed is None fallback during active Venetian slat tilt (tests/test_issue_868_venetian_tilt_mhs1_trace.py):
    • Extend test_poll_during_active_tilt_preserves_tilt_state with a MagicMock(spec=OWNAutomationEvent) status poll message carrying is_closed = None while _is_tilting is True, restoring strict 100.0% statement coverage (18,736 / 18,736 statements across all 76 integration modules).
  2. Mark test dispatcher and bus listeners with @callback / callback(...) (tests/test_door_entry_events.py, tests/test_issue_667_intercom_trace.py):
    • Wrap pri_dispatched.append in callback(lambda payload: pri_dispatched.append(payload)) in test_doorbell_fires_once_on_a_shared_bus so async_dispatcher_send invokes it synchronously on the event loop.
    • Decorate _capture_dispatcher with @callback and wrap the hass.bus.async_listen lambdas with callback(...) in tests/test_issue_667_intercom_trace.py.

State Diagram

N/A — test-only CI and coverage fix (no runtime state machine changes).


Hardware Verification

  • N/A — test-only change.

How is it tested

  • tests/test_issue_868_venetian_tilt_mhs1_trace.py::test_poll_during_active_tilt_preserves_tilt_state:
    • Exercises cover.py line 2675 (is_closed is None fallback during active tilt pulse) and verifies 1,874 / 1,874 (100.0%) statements in custom_components/myhome/cover.py.
  • tests/test_door_entry_events.py::test_doorbell_fires_once_on_a_shared_bus:
    • Verifies synchronous @callback delivery of failover myhome_doorbell_event_{mac} dispatcher signals without executor thread races.
  • tests/test_issue_667_intercom_trace.py:
    • Verifies @callback bus and dispatcher capture across all 3 trace replay tests.
  • Full verification against pinned OWNd==2.0.0b13:
    • scripts/verify_ownd_coverage.py: 76/76 modules at 100.0% (18,736 / 18,736 statements, 0 missing).
    • ruff check custom_components tests scripts: 0 violations.
    • scripts/verify_ha_standards.py: PASS (54/54 Platinum rules).
    • scripts/sync_documentation.py --check: PASS.

Where are the traces

  • N/A — existing trace fixtures (tests/fixtures/traces/issue_868/myhome_trace_MyHomeServer1_venetian_blind_75_2026-11-05.json and tests/fixtures/traces/mh200_intercom_gate_trace.json) unchanged.

What does the Firmware Oracle / Encyclopedia say

  • N/A — test-only change; no OpenWebNet WHO / WHAT / Dimension protocol changes.

Checklist

  • PR Description: Follows the structured sections above (What is the problem, What is the solution, State Diagram, How is it tested, Where are the traces, What does the Firmware Oracle / Encyclopedia say).
  • Trace-Backed (if protocol/device change): Behavior is verified against real OpenWebNet bus traces or gateway firmware fixtures rather than guessed.
  • V2 Architecture: New or modified entities implement handle_event() and do not manually subscribe via async_dispatcher_connect.
  • Tests & Linters: pytest, ruff, and scripts/verify_ha_standards.py pass.

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@GreenGrassBlueOcean
GreenGrassBlueOcean merged commit 7819138 into v2-phase1-architecture Nov 8, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants