mirror of
https://github.com/home-assistant/core.git
synced 2026-04-20 00:19:02 +02:00
Fix flaky fire_callbacks tests in casper_glow (#167418)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"""Casper Glow session fixtures."""
|
||||
|
||||
from collections.abc import Callable, Generator
|
||||
from collections.abc import Awaitable, Callable, Generator
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from pycasperglow import GlowState
|
||||
@@ -53,15 +53,17 @@ def mock_casper_glow() -> Generator[MagicMock]:
|
||||
|
||||
@pytest.fixture
|
||||
def fire_callbacks(
|
||||
hass: HomeAssistant,
|
||||
mock_casper_glow: MagicMock,
|
||||
) -> Callable[[GlowState], None]:
|
||||
) -> Callable[[GlowState], Awaitable[None]]:
|
||||
"""Return a helper that fires all registered device callbacks with a given state."""
|
||||
|
||||
def _fire(state: GlowState) -> None:
|
||||
async def _fire(state: GlowState) -> None:
|
||||
for cb in (
|
||||
call[0][0] for call in mock_casper_glow.register_callback.call_args_list
|
||||
):
|
||||
cb(state)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return _fire
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Test the Casper Glow binary sensor platform."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Awaitable, Callable
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from pycasperglow import GlowState
|
||||
@@ -43,7 +43,7 @@ async def test_paused_state_update(
|
||||
hass: HomeAssistant,
|
||||
mock_casper_glow: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
is_paused: bool,
|
||||
expected_state: str,
|
||||
) -> None:
|
||||
@@ -53,7 +53,7 @@ async def test_paused_state_update(
|
||||
):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
fire_callbacks(GlowState(is_paused=is_paused))
|
||||
await fire_callbacks(GlowState(is_paused=is_paused))
|
||||
state = hass.states.get(PAUSED_ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == expected_state
|
||||
@@ -63,7 +63,7 @@ async def test_paused_ignores_none_state(
|
||||
hass: HomeAssistant,
|
||||
mock_casper_glow: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Test that a callback with is_paused=None does not overwrite the state."""
|
||||
with patch(
|
||||
@@ -72,13 +72,13 @@ async def test_paused_ignores_none_state(
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
# Set a known value first
|
||||
fire_callbacks(GlowState(is_paused=True))
|
||||
await fire_callbacks(GlowState(is_paused=True))
|
||||
state = hass.states.get(PAUSED_ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
|
||||
# Callback with no is_paused data — state should remain unchanged
|
||||
fire_callbacks(GlowState(is_on=True))
|
||||
await fire_callbacks(GlowState(is_on=True))
|
||||
state = hass.states.get(PAUSED_ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
@@ -93,7 +93,7 @@ async def test_charging_state_update(
|
||||
hass: HomeAssistant,
|
||||
mock_casper_glow: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
is_charging: bool,
|
||||
expected_state: str,
|
||||
) -> None:
|
||||
@@ -103,7 +103,7 @@ async def test_charging_state_update(
|
||||
):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
fire_callbacks(GlowState(is_charging=is_charging))
|
||||
await fire_callbacks(GlowState(is_charging=is_charging))
|
||||
state = hass.states.get(CHARGING_ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == expected_state
|
||||
@@ -113,7 +113,7 @@ async def test_charging_ignores_none_state(
|
||||
hass: HomeAssistant,
|
||||
mock_casper_glow: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Test that a callback with is_charging=None does not overwrite the state."""
|
||||
with patch(
|
||||
@@ -122,13 +122,13 @@ async def test_charging_ignores_none_state(
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
# Set a known value first
|
||||
fire_callbacks(GlowState(is_charging=True))
|
||||
await fire_callbacks(GlowState(is_charging=True))
|
||||
state = hass.states.get(CHARGING_ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
|
||||
# Callback with no is_charging data — state should remain unchanged
|
||||
fire_callbacks(GlowState(is_on=True))
|
||||
await fire_callbacks(GlowState(is_on=True))
|
||||
state = hass.states.get(CHARGING_ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Test the Casper Glow light platform."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Awaitable, Callable
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from pycasperglow import CasperGlowError, GlowState
|
||||
@@ -83,19 +83,19 @@ async def test_turn_off(
|
||||
async def test_state_update_via_callback(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Test that the entity updates state when the device fires a callback."""
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
fire_callbacks(GlowState(is_on=True))
|
||||
await fire_callbacks(GlowState(is_on=True))
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
|
||||
fire_callbacks(GlowState(is_on=False))
|
||||
await fire_callbacks(GlowState(is_on=False))
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == STATE_OFF
|
||||
@@ -149,10 +149,10 @@ async def test_brightness_snap_to_nearest(
|
||||
async def test_brightness_update_via_callback(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Test that brightness updates via device callback."""
|
||||
fire_callbacks(GlowState(is_on=True, brightness_level=80))
|
||||
await fire_callbacks(GlowState(is_on=True, brightness_level=80))
|
||||
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state is not None
|
||||
@@ -196,7 +196,7 @@ async def test_state_update_via_callback_after_command_failure(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
mock_casper_glow: MagicMock,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Test that device callbacks correctly update state even after a command failure."""
|
||||
mock_casper_glow.turn_on.side_effect = CasperGlowError("Connection failed")
|
||||
@@ -215,7 +215,7 @@ async def test_state_update_via_callback_after_command_failure(
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
# Device sends a push state update — entity reflects true device state
|
||||
fire_callbacks(GlowState(is_on=True))
|
||||
await fire_callbacks(GlowState(is_on=True))
|
||||
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state is not None
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Test the Casper Glow select platform."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Awaitable, Callable
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from pycasperglow import CasperGlowError, GlowState
|
||||
@@ -44,14 +44,14 @@ async def test_entities(
|
||||
async def test_select_state_from_callback(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Test that the select entity shows dimming time reported by device callback."""
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
fire_callbacks(
|
||||
await fire_callbacks(
|
||||
GlowState(configured_dimming_time_minutes=int(DIMMING_TIME_OPTIONS[2]))
|
||||
)
|
||||
|
||||
@@ -64,7 +64,7 @@ async def test_select_option(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
mock_casper_glow: MagicMock,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Test selecting a dimming time option."""
|
||||
await hass.services.async_call(
|
||||
@@ -82,7 +82,7 @@ async def test_select_option(
|
||||
assert state.state == DIMMING_TIME_OPTIONS[1]
|
||||
|
||||
# A subsequent device callback must not overwrite the user's selection.
|
||||
fire_callbacks(
|
||||
await fire_callbacks(
|
||||
GlowState(configured_dimming_time_minutes=int(DIMMING_TIME_OPTIONS[0]))
|
||||
)
|
||||
|
||||
@@ -118,7 +118,7 @@ async def test_select_state_update_via_callback_after_command_failure(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
mock_casper_glow: MagicMock,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Test that device callbacks correctly update state even after a command failure."""
|
||||
mock_casper_glow.set_brightness_and_dimming_time.side_effect = CasperGlowError(
|
||||
@@ -138,7 +138,7 @@ async def test_select_state_update_via_callback_after_command_failure(
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
# Device sends a push state update — entity reflects true state
|
||||
fire_callbacks(
|
||||
await fire_callbacks(
|
||||
GlowState(configured_dimming_time_minutes=int(DIMMING_TIME_OPTIONS[1]))
|
||||
)
|
||||
|
||||
@@ -150,10 +150,10 @@ async def test_select_state_update_via_callback_after_command_failure(
|
||||
async def test_select_ignores_remaining_time_updates(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Test that callbacks with only remaining time do not change the select state."""
|
||||
fire_callbacks(GlowState(dimming_time_remaining_ms=2_640_000))
|
||||
await fire_callbacks(GlowState(dimming_time_remaining_ms=2_640_000))
|
||||
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state is not None
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Test the Casper Glow sensor platform."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Awaitable, Callable
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from pycasperglow import BatteryLevel, GlowState
|
||||
@@ -59,13 +59,13 @@ async def test_battery_state_updated_via_callback(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_casper_glow: MagicMock,
|
||||
fire_callbacks: Callable[[GlowState], None],
|
||||
fire_callbacks: Callable[[GlowState], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Test battery sensor updates when a device callback fires."""
|
||||
with patch("homeassistant.components.casper_glow.PLATFORMS", [Platform.SENSOR]):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
fire_callbacks(GlowState(battery_level=BatteryLevel.PCT_50))
|
||||
await fire_callbacks(GlowState(battery_level=BatteryLevel.PCT_50))
|
||||
|
||||
state = hass.states.get(BATTERY_ENTITY_ID)
|
||||
assert state is not None
|
||||
|
||||
Reference in New Issue
Block a user