Refactor Harmony tests to better follow Home Assistant conventions (#47141)

This commit is contained in:
Mike Keesey 2021-03-10 11:19:04 -07:00 committed by GitHub
parent 7c8851264f
commit 78c974d527
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 199 additions and 148 deletions

View file

@ -37,10 +37,10 @@ IDS_TO_DEVICES = {
class FakeHarmonyClient:
"""FakeHarmonyClient to mock away network calls."""
def __init__(
def initialize(
self, ip_address: str = "", callbacks: ClientCallbackType = MagicMock()
):
"""Initialize FakeHarmonyClient class."""
"""Initialize FakeHarmonyClient class to capture callbacks."""
self._activity_name = "Watch TV"
self.close = AsyncMock()
self.send_commands = AsyncMock()
@ -49,6 +49,8 @@ class FakeHarmonyClient:
self._callbacks = callbacks
self.fw_version = "123.456"
return self
async def connect(self):
"""Connect and call the appropriate callbacks."""
self._callbacks.connect(None)
@ -130,13 +132,28 @@ class FakeHarmonyClient:
)
return config
def mock_reconnection(self):
"""Simulate reconnection to the hub."""
self._callbacks.connect(None)
def mock_disconnection(self):
"""Simulate disconnection to the hub."""
self._callbacks.disconnect(None)
@pytest.fixture()
def mock_hc():
"""Create a mock HarmonyClient."""
def harmony_client():
"""Create the FakeHarmonyClient instance."""
return FakeHarmonyClient()
@pytest.fixture()
def mock_hc(harmony_client):
"""Patch the real HarmonyClient with initialization side effect."""
with patch(
"homeassistant.components.harmony.data.HarmonyClient",
side_effect=FakeHarmonyClient,
side_effect=harmony_client.initialize,
) as fake:
yield fake

View file

@ -1,67 +0,0 @@
"""Test the Logitech Harmony Hub entities with connection state changes."""
from datetime import timedelta
from homeassistant.components.harmony.const import DOMAIN
from homeassistant.const import (
CONF_HOST,
CONF_NAME,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.util import utcnow
from .const import ENTITY_PLAY_MUSIC, ENTITY_REMOTE, ENTITY_WATCH_TV, HUB_NAME
from tests.common import MockConfigEntry, async_fire_time_changed
async def test_connection_state_changes(mock_hc, hass, mock_write_config):
"""Ensure connection changes are reflected in the switch states."""
entry = MockConfigEntry(
domain=DOMAIN, data={CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME}
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
data = hass.data[DOMAIN][entry.entry_id]
# mocks start with current activity == Watch TV
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
data._disconnected()
await hass.async_block_till_done()
# Entities do not immediately show as unavailable
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
future_time = utcnow() + timedelta(seconds=10)
async_fire_time_changed(hass, future_time)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_UNAVAILABLE)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_UNAVAILABLE)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_UNAVAILABLE)
data._connected()
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
data._disconnected()
data._connected()
future_time = utcnow() + timedelta(seconds=10)
async_fire_time_changed(hass, future_time)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)

View file

@ -1,4 +1,6 @@
"""Test sending commands to the Harmony Hub remote."""
"""Test the Logitech Harmony Hub remote."""
from datetime import timedelta
from aioharmony.const import SendCommandDevice
@ -9,6 +11,7 @@ from homeassistant.components.harmony.const import (
)
from homeassistant.components.harmony.remote import ATTR_CHANNEL, ATTR_DELAY_SECS
from homeassistant.components.remote import (
ATTR_ACTIVITY,
ATTR_COMMAND,
ATTR_DEVICE,
ATTR_NUM_REPEATS,
@ -16,18 +19,136 @@ from homeassistant.components.remote import (
DEFAULT_HOLD_SECS,
DOMAIN as REMOTE_DOMAIN,
SERVICE_SEND_COMMAND,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_NAME
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_HOST,
CONF_NAME,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.util import utcnow
from .conftest import TV_DEVICE_ID, TV_DEVICE_NAME
from .const import ENTITY_REMOTE, HUB_NAME
from .conftest import ACTIVITIES_TO_IDS, TV_DEVICE_ID, TV_DEVICE_NAME
from .const import ENTITY_PLAY_MUSIC, ENTITY_REMOTE, ENTITY_WATCH_TV, HUB_NAME
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, async_fire_time_changed
PLAY_COMMAND = "Play"
STOP_COMMAND = "Stop"
async def test_connection_state_changes(
harmony_client, mock_hc, hass, mock_write_config
):
"""Ensure connection changes are reflected in the remote state."""
entry = MockConfigEntry(
domain=DOMAIN, data={CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME}
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
# mocks start with current activity == Watch TV
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
harmony_client.mock_disconnection()
await hass.async_block_till_done()
# Entities do not immediately show as unavailable
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
future_time = utcnow() + timedelta(seconds=10)
async_fire_time_changed(hass, future_time)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_UNAVAILABLE)
harmony_client.mock_reconnection()
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
harmony_client.mock_disconnection()
harmony_client.mock_reconnection()
future_time = utcnow() + timedelta(seconds=10)
async_fire_time_changed(hass, future_time)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
async def test_remote_toggles(mock_hc, hass, mock_write_config):
"""Ensure calls to the remote also updates the switches."""
entry = MockConfigEntry(
domain=DOMAIN, data={CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME}
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
# mocks start with current activity == Watch TV
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
# turn off remote
await hass.services.async_call(
REMOTE_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: ENTITY_REMOTE},
blocking=True,
)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_OFF)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_OFF)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
# turn on remote, restoring the last activity
await hass.services.async_call(
REMOTE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_REMOTE},
blocking=True,
)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
# send new activity command, with activity name
await hass.services.async_call(
REMOTE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_REMOTE, ATTR_ACTIVITY: "Play Music"},
blocking=True,
)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_OFF)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_ON)
# send new activity command, with activity id
await hass.services.async_call(
REMOTE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_REMOTE, ATTR_ACTIVITY: ACTIVITIES_TO_IDS["Watch TV"]},
blocking=True,
)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
async def test_async_send_command(mock_hc, hass, mock_write_config):
"""Ensure calls to send remote commands properly propagate to devices."""
entry = MockConfigEntry(

View file

@ -1,6 +1,8 @@
"""Test the Logitech Harmony Hub activity switches."""
from datetime import timedelta
from homeassistant.components.harmony.const import DOMAIN
from homeassistant.components.remote import ATTR_ACTIVITY, DOMAIN as REMOTE_DOMAIN
from homeassistant.components.switch import (
DOMAIN as SWITCH_DOMAIN,
SERVICE_TURN_OFF,
@ -12,12 +14,58 @@ from homeassistant.const import (
CONF_NAME,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.util import utcnow
from .conftest import ACTIVITIES_TO_IDS
from .const import ENTITY_PLAY_MUSIC, ENTITY_REMOTE, ENTITY_WATCH_TV, HUB_NAME
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, async_fire_time_changed
async def test_connection_state_changes(
harmony_client, mock_hc, hass, mock_write_config
):
"""Ensure connection changes are reflected in the switch states."""
entry = MockConfigEntry(
domain=DOMAIN, data={CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME}
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
# mocks start with current activity == Watch TV
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
harmony_client.mock_disconnection()
await hass.async_block_till_done()
# Entities do not immediately show as unavailable
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
future_time = utcnow() + timedelta(seconds=10)
async_fire_time_changed(hass, future_time)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_UNAVAILABLE)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_UNAVAILABLE)
harmony_client.mock_reconnection()
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
harmony_client.mock_disconnection()
harmony_client.mock_reconnection()
future_time = utcnow() + timedelta(seconds=10)
async_fire_time_changed(hass, future_time)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
async def test_switch_toggles(mock_hc, hass, mock_write_config):
@ -54,74 +102,6 @@ async def test_switch_toggles(mock_hc, hass, mock_write_config):
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
async def test_remote_toggles(mock_hc, hass, mock_write_config):
"""Ensure calls to the remote also updates the switches."""
entry = MockConfigEntry(
domain=DOMAIN, data={CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME}
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
# mocks start with current activity == Watch TV
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
# turn off remote
await hass.services.async_call(
REMOTE_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: ENTITY_REMOTE},
blocking=True,
)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_OFF)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_OFF)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
# turn on remote, restoring the last activity
await hass.services.async_call(
REMOTE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_REMOTE},
blocking=True,
)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
# send new activity command, with activity name
await hass.services.async_call(
REMOTE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_REMOTE, ATTR_ACTIVITY: "Play Music"},
blocking=True,
)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_OFF)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_ON)
# send new activity command, with activity id
await hass.services.async_call(
REMOTE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_REMOTE, ATTR_ACTIVITY: ACTIVITIES_TO_IDS["Watch TV"]},
blocking=True,
)
await hass.async_block_till_done()
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
async def _toggle_switch_and_wait(hass, service_name, entity):
await hass.services.async_call(
SWITCH_DOMAIN,