From a50883d9751bc11e3ed3120c8778588e6246afa0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 8 Mar 2024 20:37:21 -1000 Subject: [PATCH] Run service call tasks eagerly (#112791) --- homeassistant/helpers/service.py | 9 +++-- tests/components/cloud/test_init.py | 2 ++ tests/components/demo/test_geo_location.py | 2 +- tests/components/flux_led/test_number.py | 36 ++++++++++++------- .../homekit_controller/test_connection.py | 1 + tests/components/knx/test_services.py | 1 + tests/components/qwikswitch/test_init.py | 2 ++ tests/components/smartthings/test_light.py | 14 ++++++++ tests/components/template/test_weather.py | 1 + tests/components/update/test_init.py | 2 ++ 10 files changed, 52 insertions(+), 18 deletions(-) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index c4238c0d116..223833fc5a5 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -963,11 +963,10 @@ async def _handle_entity_call( task: asyncio.Future[ServiceResponse] | None if isinstance(func, str): - task = hass.async_run_hass_job( - HassJob(partial(getattr(entity, func), **data)) # type: ignore[arg-type] - ) + job = HassJob(partial(getattr(entity, func), **data)) # type: ignore[arg-type] + task = hass.async_run_hass_job(job, eager_start=True) 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 # async_run_job. @@ -1002,7 +1001,7 @@ async def _async_admin_handler( if not user.is_admin: 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: await result diff --git a/tests/components/cloud/test_init.py b/tests/components/cloud/test_init.py index 4733d5e907c..9fb8b383501 100644 --- a/tests/components/cloud/test_init.py +++ b/tests/components/cloud/test_init.py @@ -72,12 +72,14 @@ async def test_remote_services( with patch("hass_nabucasa.remote.RemoteUI.connect") as mock_connect: await hass.services.async_call(DOMAIN, "remote_connect", blocking=True) + await hass.async_block_till_done() assert mock_connect.called assert cloud.client.remote_autostart with patch("hass_nabucasa.remote.RemoteUI.disconnect") as mock_disconnect: await hass.services.async_call(DOMAIN, "remote_disconnect", blocking=True) + await hass.async_block_till_done() assert mock_disconnect.called assert not cloud.client.remote_autostart diff --git a/tests/components/demo/test_geo_location.py b/tests/components/demo/test_geo_location.py index 13657c88ac8..d3c2937d12b 100644 --- a/tests/components/demo/test_geo_location.py +++ b/tests/components/demo/test_geo_location.py @@ -50,7 +50,7 @@ async def test_setup_platform(hass: HomeAssistant, disable_platforms) -> None: # Update (replaces 1 device). 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 # the same, but the lists are different. all_states_updated = [ diff --git a/tests/components/flux_led/test_number.py b/tests/components/flux_led/test_number.py index 5e16e4c0c2c..8858e7c7c93 100644 --- a/tests/components/flux_led/test_number.py +++ b/tests/components/flux_led/test_number.py @@ -1,6 +1,7 @@ """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 import pytest @@ -24,6 +25,7 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component +import homeassistant.util.dt as dt_util from . import ( DEFAULT_ENTRY_TITLE, @@ -37,7 +39,7 @@ from . import ( 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( @@ -268,9 +270,7 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None: ) # Original addressable model bulb.color_modes = {FLUX_COLOR_MODE_RGB} bulb.color_mode = FLUX_COLOR_MODE_RGB - with patch.object( - flux_number, "DEBOUNCE_TIME", 0 - ), _patch_discovery(), _patch_wifibulb(device=bulb): + with _patch_discovery(), _patch_wifibulb(device=bulb): await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) 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" state = hass.states.get(music_segments_entity_id) assert state.state == "4" + await hass.async_block_till_done(wait_background_tasks=True) with pytest.raises(ValueError): 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}, 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.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}, 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.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}, 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.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}, 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.reset_mock() @@ -385,9 +399,7 @@ async def test_addressable_light_pixel_config_music_disabled( ) # Original addressable model bulb.color_modes = {FLUX_COLOR_MODE_RGB} bulb.color_mode = FLUX_COLOR_MODE_RGB - with patch.object( - flux_number, "DEBOUNCE_TIME", 0 - ), _patch_discovery(), _patch_wifibulb(device=bulb): + with _patch_discovery(), _patch_wifibulb(device=bulb): await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() diff --git a/tests/components/homekit_controller/test_connection.py b/tests/components/homekit_controller/test_connection.py index d8382bdde86..0a77509d675 100644 --- a/tests/components/homekit_controller/test_connection.py +++ b/tests/components/homekit_controller/test_connection.py @@ -273,6 +273,7 @@ async def test_thread_provision( }, blocking=True, ) + await hass.async_block_till_done(wait_background_tasks=True) assert config_entry.data["Connection"] == "CoAP" diff --git a/tests/components/knx/test_services.py b/tests/components/knx/test_services.py index 0bfb2141ab8..95cf60f1e68 100644 --- a/tests/components/knx/test_services.py +++ b/tests/components/knx/test_services.py @@ -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, "unrelated": 2}) 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_write(test_address, (25,)) diff --git a/tests/components/qwikswitch/test_init.py b/tests/components/qwikswitch/test_init.py index 08e27ec42bd..32a0d0d20db 100644 --- a/tests/components/qwikswitch/test_init.py +++ b/tests/components/qwikswitch/test_init.py @@ -174,6 +174,7 @@ async def test_switch_device( await hass.services.async_call( "switch", "turn_off", {"entity_id": "switch.switch_1"}, blocking=True ) + await hass.async_block_till_done() assert ( "GET", URL("http://127.0.0.1:2020/@a00001=0"), @@ -254,6 +255,7 @@ async def test_light_device( await hass.services.async_call( "light", "turn_on", {"entity_id": "light.dim_3"}, blocking=True ) + await hass.async_block_till_done() assert ( "GET", URL("http://127.0.0.1:2020/@a00003=100"), diff --git a/tests/components/smartthings/test_light.py b/tests/components/smartthings/test_light.py index 5a92023cc6c..53de2273707 100644 --- a/tests/components/smartthings/test_light.py +++ b/tests/components/smartthings/test_light.py @@ -148,6 +148,8 @@ async def test_turn_off(hass: HomeAssistant, light_devices) -> None: await hass.services.async_call( "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 state = hass.states.get("light.color_dimmer_2") 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}, blocking=True, ) + # This test schedules and update right after the call + await hass.async_block_till_done() # Assert state = hass.states.get("light.color_dimmer_2") assert state is not None @@ -179,6 +183,8 @@ async def test_turn_on(hass: HomeAssistant, light_devices) -> None: await hass.services.async_call( "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 state = hass.states.get("light.color_dimmer_1") assert state is not None @@ -200,6 +206,8 @@ async def test_turn_on_with_brightness(hass: HomeAssistant, light_devices) -> No }, blocking=True, ) + # This test schedules and update right after the call + await hass.async_block_till_done() # Assert state = hass.states.get("light.color_dimmer_1") 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}, blocking=True, ) + # This test schedules and update right after the call + await hass.async_block_till_done() # Assert state = hass.states.get("light.color_dimmer_1") 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)}, blocking=True, ) + # This test schedules and update right after the call + await hass.async_block_till_done() # Assert state = hass.states.get("light.color_dimmer_2") 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}, blocking=True, ) + # This test schedules and update right after the call + await hass.async_block_till_done() # Assert state = hass.states.get("light.color_dimmer_2") assert state is not None diff --git a/tests/components/template/test_weather.py b/tests/components/template/test_weather.py index 3941194c586..4165a509ee4 100644 --- a/tests/components/template/test_weather.py +++ b/tests/components/template/test_weather.py @@ -507,6 +507,7 @@ async def test_forecast_format_error( } }, ) + await hass.async_block_till_done() await hass.services.async_call( WEATHER_DOMAIN, diff --git a/tests/components/update/test_init.py b/tests/components/update/test_init.py index 3a923c8ed18..106273a3b0e 100644 --- a/tests/components/update/test_init.py +++ b/tests/components/update/test_init.py @@ -647,6 +647,8 @@ async def test_entity_without_progress_support_raising( blocking=True, ) + await hass.async_block_till_done() + 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_INSTALLED_VERSION] == "1.0.0"