Fix wait_template incorrectly matching falsey values (#44938)

pull/44949/head
J. Nick Koston 2021-01-08 02:07:50 -10:00 committed by GitHub
parent c457ea854c
commit 7c93a11aba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 20 deletions

View File

@ -62,11 +62,7 @@ from homeassistant.core import (
callback,
)
from homeassistant.helpers import condition, config_validation as cv, service, template
from homeassistant.helpers.event import (
TrackTemplate,
async_call_later,
async_track_template_result,
)
from homeassistant.helpers.event import async_call_later, async_track_template
from homeassistant.helpers.script_variables import ScriptVariables
from homeassistant.helpers.trigger import (
async_initialize_triggers,
@ -359,7 +355,7 @@ class _ScriptRun:
return
@callback
def _async_script_wait(event, updates):
def async_script_wait(entity_id, from_s, to_s):
"""Handle script after template condition is true."""
self._variables["wait"] = {
"remaining": to_context.remaining if to_context else delay,
@ -368,12 +364,9 @@ class _ScriptRun:
done.set()
to_context = None
info = async_track_template_result(
self._hass,
[TrackTemplate(wait_template, self._variables)],
_async_script_wait,
unsub = async_track_template(
self._hass, wait_template, async_script_wait, self._variables
)
unsub = info.async_remove
self._changed()
done = asyncio.Event()

View File

@ -8,6 +8,7 @@ from types import MappingProxyType
from unittest import mock
from unittest.mock import patch
from async_timeout import timeout
import pytest
import voluptuous as vol
@ -544,6 +545,41 @@ async def test_wait_basic(hass, action_type):
assert script_obj.last_action is None
@pytest.mark.parametrize("action_type", ["template", "trigger"])
async def test_wait_basic_times_out(hass, action_type):
"""Test wait actions times out when the action does not happen."""
wait_alias = "wait step"
action = {"alias": wait_alias}
if action_type == "template":
action["wait_template"] = "{{ states.switch.test.state == 'off' }}"
else:
action["wait_for_trigger"] = {
"platform": "state",
"entity_id": "switch.test",
"to": "off",
}
sequence = cv.SCRIPT_SCHEMA(action)
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
wait_started_flag = async_watch_for_action(script_obj, wait_alias)
timed_out = False
try:
hass.states.async_set("switch.test", "on")
hass.async_create_task(script_obj.async_run(context=Context()))
await asyncio.wait_for(wait_started_flag.wait(), 1)
assert script_obj.is_running
assert script_obj.last_action == wait_alias
hass.states.async_set("switch.test", "not_on")
with timeout(0.1):
await hass.async_block_till_done()
except asyncio.TimeoutError:
timed_out = True
await script_obj.async_stop()
assert timed_out
@pytest.mark.parametrize("action_type", ["template", "trigger"])
async def test_multiple_runs_wait(hass, action_type):
"""Test multiple runs with wait in script."""
@ -782,30 +818,53 @@ async def test_wait_template_variables_in(hass):
async def test_wait_template_with_utcnow(hass):
"""Test the wait template with utcnow."""
sequence = cv.SCRIPT_SCHEMA({"wait_template": "{{ utcnow().hours == 12 }}"})
sequence = cv.SCRIPT_SCHEMA({"wait_template": "{{ utcnow().hour == 12 }}"})
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
wait_started_flag = async_watch_for_action(script_obj, "wait")
start_time = dt_util.utcnow() + timedelta(hours=24)
start_time = dt_util.utcnow().replace(minute=1) + timedelta(hours=48)
try:
hass.async_create_task(script_obj.async_run(context=Context()))
async_fire_time_changed(hass, start_time.replace(hour=5))
assert not script_obj.is_running
async_fire_time_changed(hass, start_time.replace(hour=12))
await asyncio.wait_for(wait_started_flag.wait(), 1)
assert script_obj.is_running
match_time = start_time.replace(hour=12)
with patch("homeassistant.util.dt.utcnow", return_value=match_time):
async_fire_time_changed(hass, match_time)
except (AssertionError, asyncio.TimeoutError):
await script_obj.async_stop()
raise
else:
async_fire_time_changed(hass, start_time.replace(hour=3))
await hass.async_block_till_done()
assert not script_obj.is_running
async def test_wait_template_with_utcnow_no_match(hass):
"""Test the wait template with utcnow that does not match."""
sequence = cv.SCRIPT_SCHEMA({"wait_template": "{{ utcnow().hour == 12 }}"})
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
wait_started_flag = async_watch_for_action(script_obj, "wait")
start_time = dt_util.utcnow().replace(minute=1) + timedelta(hours=48)
timed_out = False
try:
hass.async_create_task(script_obj.async_run(context=Context()))
await asyncio.wait_for(wait_started_flag.wait(), 1)
assert script_obj.is_running
non_maching_time = start_time.replace(hour=3)
with patch("homeassistant.util.dt.utcnow", return_value=non_maching_time):
async_fire_time_changed(hass, non_maching_time)
with timeout(0.1):
await hass.async_block_till_done()
except asyncio.TimeoutError:
timed_out = True
await script_obj.async_stop()
assert timed_out
@pytest.mark.parametrize("mode", ["no_timeout", "timeout_finish", "timeout_not_finish"])
@pytest.mark.parametrize("action_type", ["template", "trigger"])
async def test_wait_variables_out(hass, mode, action_type):