Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions homeassistant/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1724,6 +1724,8 @@ def __repr__(self) -> str:
class ServiceRegistry:
"""Offer the services over the eventbus."""

__slots__ = ("_services", "_hass")

def __init__(self, hass: HomeAssistant) -> None:
"""Initialize a service registry."""
self._services: dict[str, dict[str, Service]] = {}
Expand Down
154 changes: 37 additions & 117 deletions tests/components/google_assistant/test_smart_home.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Test Google Smart Home."""
import asyncio
from types import SimpleNamespace
from unittest.mock import ANY, call, patch
from unittest.mock import ANY, patch

import pytest
from pytest_unordered import unordered
Expand Down Expand Up @@ -488,76 +488,41 @@ async def test_execute(
events = async_capture_events(hass, EVENT_COMMAND_RECEIVED)
service_events = async_capture_events(hass, EVENT_CALL_SERVICE)

with patch.object(
Comment thread
bdraco marked this conversation as resolved.
hass.services, "async_call", wraps=hass.services.async_call
) as call_service_mock:
result = await sh.async_handle_message(
hass,
MockConfig(should_report_state=report_state),
None,
{
"requestId": REQ_ID,
"inputs": [
{
"intent": "action.devices.EXECUTE",
"payload": {
"commands": [
{
"devices": [
{"id": "light.non_existing"},
{"id": "light.ceiling_lights"},
{"id": "light.kitchen_lights"},
],
"execution": [
{
"command": "action.devices.commands.OnOff",
"params": {"on": True},
},
{
"command": "action.devices.commands.BrightnessAbsolute",
"params": {"brightness": 20},
},
],
}
]
},
}
],
},
const.SOURCE_CLOUD,
)
assert call_service_mock.call_count == 4
expected_calls = [
call(
"light",
"turn_on",
{"entity_id": "light.ceiling_lights"},
blocking=not report_state,
context=ANY,
),
call(
"light",
"turn_on",
{"entity_id": "light.kitchen_lights"},
blocking=not report_state,
context=ANY,
),
call(
"light",
"turn_on",
{"entity_id": "light.ceiling_lights", "brightness_pct": 20},
blocking=not report_state,
context=ANY,
),
call(
"light",
"turn_on",
{"entity_id": "light.kitchen_lights", "brightness_pct": 20},
blocking=not report_state,
context=ANY,
),
]
call_service_mock.assert_has_awaits(expected_calls, any_order=True)
result = await sh.async_handle_message(
hass,
MockConfig(should_report_state=report_state),
None,
{
"requestId": REQ_ID,
"inputs": [
{
"intent": "action.devices.EXECUTE",
"payload": {
"commands": [
{
"devices": [
{"id": "light.non_existing"},
{"id": "light.ceiling_lights"},
{"id": "light.kitchen_lights"},
],
"execution": [
{
"command": "action.devices.commands.OnOff",
"params": {"on": True},
},
{
"command": "action.devices.commands.BrightnessAbsolute",
"params": {"brightness": 20},
},
],
}
]
},
}
],
},
const.SOURCE_CLOUD,
)
await hass.async_block_till_done()

assert result == {
Expand Down Expand Up @@ -682,11 +647,7 @@ async def slow_turn_on(*args, **kwargs):
# Make DemoLigt.async_turn_on hang waiting for the turn_on_wait event
await turn_on_wait.wait()

with patch.object(
hass.services, "async_call", wraps=hass.services.async_call
) as call_service_mock, patch.object(
DemoLight, "async_turn_on", wraps=slow_turn_on
):
with patch.object(DemoLight, "async_turn_on", wraps=slow_turn_on):
result = await sh.async_handle_message(
hass,
MockConfig(should_report_state=report_state),
Expand Down Expand Up @@ -722,51 +683,10 @@ async def slow_turn_on(*args, **kwargs):
},
const.SOURCE_CLOUD,
)
# Only the two first calls are executed
assert call_service_mock.call_count == 2
expected_calls = [
call(
"light",
"turn_on",
{"entity_id": "light.ceiling_lights"},
blocking=not report_state,
context=ANY,
),
call(
"light",
"turn_on",
{"entity_id": "light.kitchen_lights"},
blocking=not report_state,
context=ANY,
),
]
call_service_mock.assert_has_awaits(expected_calls, any_order=True)

turn_on_wait.set()
await hass.async_block_till_done()
await hass.async_block_till_done()
# The remaining two calls should now have executed
assert call_service_mock.call_count == 4
expected_calls.extend(
[
call(
"light",
"turn_on",
{"entity_id": "light.ceiling_lights", "brightness_pct": 20},
blocking=not report_state,
context=ANY,
),
call(
"light",
"turn_on",
{"entity_id": "light.kitchen_lights", "brightness_pct": 20},
blocking=not report_state,
context=ANY,
),
]
)
call_service_mock.assert_has_awaits(expected_calls, any_order=True)
await hass.async_block_till_done()

assert result == {
"requestId": REQ_ID,
Expand Down
70 changes: 36 additions & 34 deletions tests/components/group/test_light.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""The tests for the Group Light platform."""
import unittest.mock
from unittest.mock import MagicMock, patch

import async_timeout
Expand All @@ -16,7 +15,6 @@
ATTR_COLOR_TEMP_KELVIN,
ATTR_EFFECT,
ATTR_EFFECT_LIST,
ATTR_FLASH,
ATTR_HS_COLOR,
ATTR_MAX_COLOR_TEMP_KELVIN,
ATTR_MIN_COLOR_TEMP_KELVIN,
Expand All @@ -26,7 +24,6 @@
ATTR_SUPPORTED_COLOR_MODES,
ATTR_TRANSITION,
ATTR_WHITE,
ATTR_XY_COLOR,
DOMAIN as LIGHT_DOMAIN,
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
Expand All @@ -39,16 +36,17 @@
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
EVENT_CALL_SERVICE,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant
from homeassistant.core import Event, HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component

from tests.common import get_fixture_path
from tests.common import async_capture_events, get_fixture_path


async def test_default_state(hass: HomeAssistant) -> None:
Expand Down Expand Up @@ -1443,6 +1441,7 @@ async def test_invalid_service_calls(hass: HomeAssistant) -> None:
await group.async_setup_platform(
hass, {"name": "test", "entities": ["light.test1", "light.test2"]}, add_entities
)
await async_setup_component(hass, "light", {})
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
Expand All @@ -1451,35 +1450,38 @@ async def test_invalid_service_calls(hass: HomeAssistant) -> None:
grouped_light = add_entities.call_args[0][0][0]
grouped_light.hass = hass

with unittest.mock.patch.object(hass.services, "async_call") as mock_call:
await grouped_light.async_turn_on(brightness=150, four_oh_four="404")
data = {ATTR_ENTITY_ID: ["light.test1", "light.test2"], ATTR_BRIGHTNESS: 150}
mock_call.assert_called_once_with(
LIGHT_DOMAIN, SERVICE_TURN_ON, data, blocking=True, context=None
)
mock_call.reset_mock()

await grouped_light.async_turn_off(transition=4, four_oh_four="404")
data = {ATTR_ENTITY_ID: ["light.test1", "light.test2"], ATTR_TRANSITION: 4}
mock_call.assert_called_once_with(
LIGHT_DOMAIN, SERVICE_TURN_OFF, data, blocking=True, context=None
)
mock_call.reset_mock()

data = {
ATTR_BRIGHTNESS: 150,
ATTR_XY_COLOR: (0.5, 0.42),
ATTR_RGB_COLOR: (80, 120, 50),
ATTR_COLOR_TEMP_KELVIN: 1234,
ATTR_EFFECT: "Sunshine",
ATTR_TRANSITION: 4,
ATTR_FLASH: "long",
}
await grouped_light.async_turn_on(**data)
data[ATTR_ENTITY_ID] = ["light.test1", "light.test2"]
mock_call.assert_called_once_with(
LIGHT_DOMAIN, SERVICE_TURN_ON, data, blocking=True, context=None
)
service_call_events = async_capture_events(hass, EVENT_CALL_SERVICE)

await grouped_light.async_turn_on(brightness=150, four_oh_four="404")
data = {ATTR_ENTITY_ID: ["light.test1", "light.test2"], ATTR_BRIGHTNESS: 150}
assert len(service_call_events) == 1
service_event_call: Event = service_call_events[0]
assert service_event_call.data["domain"] == LIGHT_DOMAIN
assert service_event_call.data["service"] == SERVICE_TURN_ON
assert service_event_call.data["service_data"] == data
service_call_events.clear()

await grouped_light.async_turn_off(transition=4, four_oh_four="404")
data = {ATTR_ENTITY_ID: ["light.test1", "light.test2"], ATTR_TRANSITION: 4}
assert len(service_call_events) == 1
service_event_call: Event = service_call_events[0]
assert service_event_call.data["domain"] == LIGHT_DOMAIN
assert service_event_call.data["service"] == SERVICE_TURN_OFF
assert service_event_call.data["service_data"] == data
service_call_events.clear()

data = {
ATTR_BRIGHTNESS: 150,
ATTR_COLOR_TEMP_KELVIN: 1234,
ATTR_TRANSITION: 4,
}
await grouped_light.async_turn_on(**data)
data[ATTR_ENTITY_ID] = ["light.test1", "light.test2"]
service_event_call: Event = service_call_events[0]
assert service_event_call.data["domain"] == LIGHT_DOMAIN
assert service_event_call.data["service"] == SERVICE_TURN_ON
assert service_event_call.data["service_data"] == data
service_call_events.clear()


async def test_reload(hass: HomeAssistant) -> None:
Expand Down
5 changes: 1 addition & 4 deletions tests/components/vesync/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,12 @@ async def test_async_setup_entry__not_login(
hass.config_entries, "async_forward_entry_setup"
) as setup_mock, patch(
"homeassistant.components.vesync.async_process_devices"
) as process_mock, patch.object(
hass.services, "async_register"
) as register_mock:
) as process_mock:
assert not await async_setup_entry(hass, config_entry)
await hass.async_block_till_done()
assert setups_mock.call_count == 0
assert setup_mock.call_count == 0
assert process_mock.call_count == 0
assert register_mock.call_count == 0

assert manager.login.call_count == 1
assert DOMAIN not in hass.data
Expand Down
6 changes: 3 additions & 3 deletions tests/components/zha/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,8 @@ def _zha_device(

@pytest.fixture
def hass_disable_services(hass):
"""Mock service register."""
with patch.object(hass.services, "async_register"), patch.object(
hass.services, "has_service", return_value=True
"""Mock services."""
with patch.object(
hass, "services", MagicMock(has_service=MagicMock(return_value=True))
):
yield hass