Run service call tasks eagerly (#112791)

This commit is contained in:
J. Nick Koston 2024-03-08 20:37:21 -10:00 committed by GitHub
parent 6a7c255b93
commit a50883d975
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 52 additions and 18 deletions

View file

@ -963,11 +963,10 @@ async def _handle_entity_call(
task: asyncio.Future[ServiceResponse] | None task: asyncio.Future[ServiceResponse] | None
if isinstance(func, str): if isinstance(func, str):
task = hass.async_run_hass_job( job = HassJob(partial(getattr(entity, func), **data)) # type: ignore[arg-type]
HassJob(partial(getattr(entity, func), **data)) # type: ignore[arg-type] task = hass.async_run_hass_job(job, eager_start=True)
)
else: else:
task = hass.async_run_hass_job(func, entity, data) task = hass.async_run_hass_job(func, entity, data, eager_start=True)
# Guard because callback functions do not return a task when passed to # Guard because callback functions do not return a task when passed to
# async_run_job. # async_run_job.
@ -1002,7 +1001,7 @@ async def _async_admin_handler(
if not user.is_admin: if not user.is_admin:
raise Unauthorized(context=call.context) raise Unauthorized(context=call.context)
result = hass.async_run_hass_job(service_job, call) result = hass.async_run_hass_job(service_job, call, eager_start=True)
if result is not None: if result is not None:
await result await result

View file

@ -72,12 +72,14 @@ async def test_remote_services(
with patch("hass_nabucasa.remote.RemoteUI.connect") as mock_connect: with patch("hass_nabucasa.remote.RemoteUI.connect") as mock_connect:
await hass.services.async_call(DOMAIN, "remote_connect", blocking=True) await hass.services.async_call(DOMAIN, "remote_connect", blocking=True)
await hass.async_block_till_done()
assert mock_connect.called assert mock_connect.called
assert cloud.client.remote_autostart assert cloud.client.remote_autostart
with patch("hass_nabucasa.remote.RemoteUI.disconnect") as mock_disconnect: with patch("hass_nabucasa.remote.RemoteUI.disconnect") as mock_disconnect:
await hass.services.async_call(DOMAIN, "remote_disconnect", blocking=True) await hass.services.async_call(DOMAIN, "remote_disconnect", blocking=True)
await hass.async_block_till_done()
assert mock_disconnect.called assert mock_disconnect.called
assert not cloud.client.remote_autostart assert not cloud.client.remote_autostart

View file

@ -50,7 +50,7 @@ async def test_setup_platform(hass: HomeAssistant, disable_platforms) -> None:
# Update (replaces 1 device). # Update (replaces 1 device).
async_fire_time_changed(hass, utcnow + DEFAULT_UPDATE_INTERVAL) async_fire_time_changed(hass, utcnow + DEFAULT_UPDATE_INTERVAL)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
# Get all states again, ensure that the number of states is still # Get all states again, ensure that the number of states is still
# the same, but the lists are different. # the same, but the lists are different.
all_states_updated = [ all_states_updated = [

View file

@ -1,6 +1,7 @@
"""Tests for the flux_led number platform.""" """Tests for the flux_led number platform."""
from unittest.mock import patch
from datetime import timedelta
from flux_led.const import COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB from flux_led.const import COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB
import pytest import pytest
@ -24,6 +25,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from . import ( from . import (
DEFAULT_ENTRY_TITLE, DEFAULT_ENTRY_TITLE,
@ -37,7 +39,7 @@ from . import (
async_mock_effect_speed, async_mock_effect_speed,
) )
from tests.common import MockConfigEntry from tests.common import MockConfigEntry, async_fire_time_changed
async def test_effects_speed_unique_id( async def test_effects_speed_unique_id(
@ -268,9 +270,7 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None:
) # Original addressable model ) # Original addressable model
bulb.color_modes = {FLUX_COLOR_MODE_RGB} bulb.color_modes = {FLUX_COLOR_MODE_RGB}
bulb.color_mode = FLUX_COLOR_MODE_RGB bulb.color_mode = FLUX_COLOR_MODE_RGB
with patch.object( with _patch_discovery(), _patch_wifibulb(device=bulb):
flux_number, "DEBOUNCE_TIME", 0
), _patch_discovery(), _patch_wifibulb(device=bulb):
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
await hass.async_block_till_done() await hass.async_block_till_done()
@ -291,6 +291,7 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None:
music_segments_entity_id = "number.bulb_rgbcw_ddeeff_music_segments" music_segments_entity_id = "number.bulb_rgbcw_ddeeff_music_segments"
state = hass.states.get(music_segments_entity_id) state = hass.states.get(music_segments_entity_id)
assert state.state == "4" assert state.state == "4"
await hass.async_block_till_done(wait_background_tasks=True)
with pytest.raises(ValueError): with pytest.raises(ValueError):
await hass.services.async_call( await hass.services.async_call(
@ -306,7 +307,11 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None:
{ATTR_ENTITY_ID: pixels_per_segment_entity_id, ATTR_VALUE: 100}, {ATTR_ENTITY_ID: pixels_per_segment_entity_id, ATTR_VALUE: 100},
blocking=True, blocking=True,
) )
await hass.async_block_till_done() async_fire_time_changed(
hass, dt_util.utcnow() + timedelta(seconds=flux_number.DEBOUNCE_TIME)
)
await hass.async_block_till_done(wait_background_tasks=True)
bulb.async_set_device_config.assert_called_with(pixels_per_segment=100) bulb.async_set_device_config.assert_called_with(pixels_per_segment=100)
bulb.async_set_device_config.reset_mock() bulb.async_set_device_config.reset_mock()
@ -324,7 +329,10 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None:
{ATTR_ENTITY_ID: music_pixels_per_segment_entity_id, ATTR_VALUE: 100}, {ATTR_ENTITY_ID: music_pixels_per_segment_entity_id, ATTR_VALUE: 100},
blocking=True, blocking=True,
) )
await hass.async_block_till_done() async_fire_time_changed(
hass, dt_util.utcnow() + timedelta(seconds=flux_number.DEBOUNCE_TIME)
)
await hass.async_block_till_done(wait_background_tasks=True)
bulb.async_set_device_config.assert_called_with(music_pixels_per_segment=100) bulb.async_set_device_config.assert_called_with(music_pixels_per_segment=100)
bulb.async_set_device_config.reset_mock() bulb.async_set_device_config.reset_mock()
@ -342,7 +350,10 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None:
{ATTR_ENTITY_ID: segments_entity_id, ATTR_VALUE: 5}, {ATTR_ENTITY_ID: segments_entity_id, ATTR_VALUE: 5},
blocking=True, blocking=True,
) )
await hass.async_block_till_done() async_fire_time_changed(
hass, dt_util.utcnow() + timedelta(seconds=flux_number.DEBOUNCE_TIME)
)
await hass.async_block_till_done(wait_background_tasks=True)
bulb.async_set_device_config.assert_called_with(segments=5) bulb.async_set_device_config.assert_called_with(segments=5)
bulb.async_set_device_config.reset_mock() bulb.async_set_device_config.reset_mock()
@ -360,7 +371,10 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None:
{ATTR_ENTITY_ID: music_segments_entity_id, ATTR_VALUE: 5}, {ATTR_ENTITY_ID: music_segments_entity_id, ATTR_VALUE: 5},
blocking=True, blocking=True,
) )
await hass.async_block_till_done() async_fire_time_changed(
hass, dt_util.utcnow() + timedelta(seconds=flux_number.DEBOUNCE_TIME)
)
await hass.async_block_till_done(wait_background_tasks=True)
bulb.async_set_device_config.assert_called_with(music_segments=5) bulb.async_set_device_config.assert_called_with(music_segments=5)
bulb.async_set_device_config.reset_mock() bulb.async_set_device_config.reset_mock()
@ -385,9 +399,7 @@ async def test_addressable_light_pixel_config_music_disabled(
) # Original addressable model ) # Original addressable model
bulb.color_modes = {FLUX_COLOR_MODE_RGB} bulb.color_modes = {FLUX_COLOR_MODE_RGB}
bulb.color_mode = FLUX_COLOR_MODE_RGB bulb.color_mode = FLUX_COLOR_MODE_RGB
with patch.object( with _patch_discovery(), _patch_wifibulb(device=bulb):
flux_number, "DEBOUNCE_TIME", 0
), _patch_discovery(), _patch_wifibulb(device=bulb):
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
await hass.async_block_till_done() await hass.async_block_till_done()

View file

@ -273,6 +273,7 @@ async def test_thread_provision(
}, },
blocking=True, blocking=True,
) )
await hass.async_block_till_done(wait_background_tasks=True)
assert config_entry.data["Connection"] == "CoAP" assert config_entry.data["Connection"] == "CoAP"

View file

@ -253,6 +253,7 @@ async def test_exposure_register(hass: HomeAssistant, knx: KNXTestKit) -> None:
hass.states.async_set(test_entity, STATE_ON, {test_attribute: 25}) hass.states.async_set(test_entity, STATE_ON, {test_attribute: 25})
hass.states.async_set(test_entity, STATE_ON, {test_attribute: 25, "unrelated": 2}) hass.states.async_set(test_entity, STATE_ON, {test_attribute: 25, "unrelated": 2})
hass.states.async_set(test_entity, STATE_OFF, {test_attribute: 25}) hass.states.async_set(test_entity, STATE_OFF, {test_attribute: 25})
await hass.async_block_till_done()
await knx.assert_telegram_count(1) await knx.assert_telegram_count(1)
await knx.assert_write(test_address, (25,)) await knx.assert_write(test_address, (25,))

View file

@ -174,6 +174,7 @@ async def test_switch_device(
await hass.services.async_call( await hass.services.async_call(
"switch", "turn_off", {"entity_id": "switch.switch_1"}, blocking=True "switch", "turn_off", {"entity_id": "switch.switch_1"}, blocking=True
) )
await hass.async_block_till_done()
assert ( assert (
"GET", "GET",
URL("http://127.0.0.1:2020/@a00001=0"), URL("http://127.0.0.1:2020/@a00001=0"),
@ -254,6 +255,7 @@ async def test_light_device(
await hass.services.async_call( await hass.services.async_call(
"light", "turn_on", {"entity_id": "light.dim_3"}, blocking=True "light", "turn_on", {"entity_id": "light.dim_3"}, blocking=True
) )
await hass.async_block_till_done()
assert ( assert (
"GET", "GET",
URL("http://127.0.0.1:2020/@a00003=100"), URL("http://127.0.0.1:2020/@a00003=100"),

View file

@ -148,6 +148,8 @@ async def test_turn_off(hass: HomeAssistant, light_devices) -> None:
await hass.services.async_call( await hass.services.async_call(
"light", "turn_off", {"entity_id": "light.color_dimmer_2"}, blocking=True "light", "turn_off", {"entity_id": "light.color_dimmer_2"}, blocking=True
) )
# This test schedules and update right after the call
await hass.async_block_till_done()
# Assert # Assert
state = hass.states.get("light.color_dimmer_2") state = hass.states.get("light.color_dimmer_2")
assert state is not None assert state is not None
@ -165,6 +167,8 @@ async def test_turn_off_with_transition(hass: HomeAssistant, light_devices) -> N
{ATTR_ENTITY_ID: "light.color_dimmer_2", ATTR_TRANSITION: 2}, {ATTR_ENTITY_ID: "light.color_dimmer_2", ATTR_TRANSITION: 2},
blocking=True, blocking=True,
) )
# This test schedules and update right after the call
await hass.async_block_till_done()
# Assert # Assert
state = hass.states.get("light.color_dimmer_2") state = hass.states.get("light.color_dimmer_2")
assert state is not None assert state is not None
@ -179,6 +183,8 @@ async def test_turn_on(hass: HomeAssistant, light_devices) -> None:
await hass.services.async_call( await hass.services.async_call(
"light", "turn_on", {ATTR_ENTITY_ID: "light.color_dimmer_1"}, blocking=True "light", "turn_on", {ATTR_ENTITY_ID: "light.color_dimmer_1"}, blocking=True
) )
# This test schedules and update right after the call
await hass.async_block_till_done()
# Assert # Assert
state = hass.states.get("light.color_dimmer_1") state = hass.states.get("light.color_dimmer_1")
assert state is not None assert state is not None
@ -200,6 +206,8 @@ async def test_turn_on_with_brightness(hass: HomeAssistant, light_devices) -> No
}, },
blocking=True, blocking=True,
) )
# This test schedules and update right after the call
await hass.async_block_till_done()
# Assert # Assert
state = hass.states.get("light.color_dimmer_1") state = hass.states.get("light.color_dimmer_1")
assert state is not None assert state is not None
@ -226,6 +234,8 @@ async def test_turn_on_with_minimal_brightness(
{ATTR_ENTITY_ID: "light.color_dimmer_1", ATTR_BRIGHTNESS: 2}, {ATTR_ENTITY_ID: "light.color_dimmer_1", ATTR_BRIGHTNESS: 2},
blocking=True, blocking=True,
) )
# This test schedules and update right after the call
await hass.async_block_till_done()
# Assert # Assert
state = hass.states.get("light.color_dimmer_1") state = hass.states.get("light.color_dimmer_1")
assert state is not None assert state is not None
@ -245,6 +255,8 @@ async def test_turn_on_with_color(hass: HomeAssistant, light_devices) -> None:
{ATTR_ENTITY_ID: "light.color_dimmer_2", ATTR_HS_COLOR: (180, 50)}, {ATTR_ENTITY_ID: "light.color_dimmer_2", ATTR_HS_COLOR: (180, 50)},
blocking=True, blocking=True,
) )
# This test schedules and update right after the call
await hass.async_block_till_done()
# Assert # Assert
state = hass.states.get("light.color_dimmer_2") state = hass.states.get("light.color_dimmer_2")
assert state is not None assert state is not None
@ -263,6 +275,8 @@ async def test_turn_on_with_color_temp(hass: HomeAssistant, light_devices) -> No
{ATTR_ENTITY_ID: "light.color_dimmer_2", ATTR_COLOR_TEMP: 300}, {ATTR_ENTITY_ID: "light.color_dimmer_2", ATTR_COLOR_TEMP: 300},
blocking=True, blocking=True,
) )
# This test schedules and update right after the call
await hass.async_block_till_done()
# Assert # Assert
state = hass.states.get("light.color_dimmer_2") state = hass.states.get("light.color_dimmer_2")
assert state is not None assert state is not None

View file

@ -507,6 +507,7 @@ async def test_forecast_format_error(
} }
}, },
) )
await hass.async_block_till_done()
await hass.services.async_call( await hass.services.async_call(
WEATHER_DOMAIN, WEATHER_DOMAIN,

View file

@ -647,6 +647,8 @@ async def test_entity_without_progress_support_raising(
blocking=True, blocking=True,
) )
await hass.async_block_till_done()
assert len(events) == 2 assert len(events) == 2
assert events[0].data.get("old_state").attributes[ATTR_IN_PROGRESS] is False assert events[0].data.get("old_state").attributes[ATTR_IN_PROGRESS] is False
assert events[0].data.get("old_state").attributes[ATTR_INSTALLED_VERSION] == "1.0.0" assert events[0].data.get("old_state").attributes[ATTR_INSTALLED_VERSION] == "1.0.0"