Reorganize trigger code (#38655)

This commit is contained in:
Phil Bruckner 2020-08-17 11:54:56 -05:00 committed by GitHub
parent fca071742d
commit ca9dd0c833
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 306 additions and 194 deletions

View file

@ -0,0 +1 @@
"""Test core triggers."""

View file

@ -0,0 +1,168 @@
"""The tests for the Event automation."""
import pytest
import homeassistant.components.automation as automation
from homeassistant.core import Context
from homeassistant.setup import async_setup_component
from tests.common import async_mock_service, mock_component
from tests.components.automation import common
@pytest.fixture
def calls(hass):
"""Track calls to a mock service."""
return async_mock_service(hass, "test", "automation")
@pytest.fixture(autouse=True)
def setup_comp(hass):
"""Initialize components."""
mock_component(hass, "group")
async def test_if_fires_on_event(hass, calls):
"""Test the firing of events."""
context = Context()
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation"},
}
},
)
hass.bus.async_fire("test_event", context=context)
await hass.async_block_till_done()
assert 1 == len(calls)
assert calls[0].context.parent_id == context.id
await common.async_turn_off(hass)
await hass.async_block_till_done()
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_fires_on_event_extra_data(hass, calls):
"""Test the firing of events still matches with event data."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation"},
}
},
)
hass.bus.async_fire("test_event", {"extra_key": "extra_data"})
await hass.async_block_till_done()
assert len(calls) == 1
await common.async_turn_off(hass)
await hass.async_block_till_done()
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_fires_on_event_with_data(hass, calls):
"""Test the firing of events with data."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "event",
"event_type": "test_event",
"event_data": {"some_attr": "some_value"},
},
"action": {"service": "test.automation"},
}
},
)
hass.bus.async_fire("test_event", {"some_attr": "some_value", "another": "value"})
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_fires_on_event_with_empty_data_config(hass, calls):
"""Test the firing of events with empty data config.
The frontend automation editor can produce configurations with an
empty dict for event_data instead of no key.
"""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "event",
"event_type": "test_event",
"event_data": {},
},
"action": {"service": "test.automation"},
}
},
)
hass.bus.async_fire("test_event", {"some_attr": "some_value", "another": "value"})
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_fires_on_event_with_nested_data(hass, calls):
"""Test the firing of events with nested data."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "event",
"event_type": "test_event",
"event_data": {"parent_attr": {"some_attr": "some_value"}},
},
"action": {"service": "test.automation"},
}
},
)
hass.bus.async_fire(
"test_event", {"parent_attr": {"some_attr": "some_value", "another": "value"}}
)
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_not_fires_if_event_data_not_matches(hass, calls):
"""Test firing of event if no match."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "event",
"event_type": "test_event",
"event_data": {"some_attr": "some_value"},
},
"action": {"service": "test.automation"},
}
},
)
hass.bus.async_fire("test_event", {"some_attr": "some_other_value"})
await hass.async_block_till_done()
assert len(calls) == 0

View file

@ -0,0 +1,68 @@
"""The tests for the Event automation."""
import homeassistant.components.automation as automation
from homeassistant.core import CoreState
from homeassistant.setup import async_setup_component
from tests.async_mock import AsyncMock, patch
from tests.common import async_mock_service
async def test_if_fires_on_hass_start(hass):
"""Test the firing when Home Assistant starts."""
calls = async_mock_service(hass, "test", "automation")
hass.state = CoreState.not_running
config = {
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "homeassistant", "event": "start"},
"action": {"service": "test.automation"},
}
}
assert await async_setup_component(hass, automation.DOMAIN, config)
assert automation.is_on(hass, "automation.hello")
assert len(calls) == 0
await hass.async_start()
await hass.async_block_till_done()
assert automation.is_on(hass, "automation.hello")
assert len(calls) == 1
with patch(
"homeassistant.config.async_hass_config_yaml", AsyncMock(return_value=config),
):
await hass.services.async_call(
automation.DOMAIN, automation.SERVICE_RELOAD, blocking=True
)
assert automation.is_on(hass, "automation.hello")
assert len(calls) == 1
async def test_if_fires_on_hass_shutdown(hass):
"""Test the firing when Home Assistant shuts down."""
calls = async_mock_service(hass, "test", "automation")
hass.state = CoreState.not_running
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "homeassistant", "event": "shutdown"},
"action": {"service": "test.automation"},
}
},
)
assert automation.is_on(hass, "automation.hello")
assert len(calls) == 0
await hass.async_start()
assert automation.is_on(hass, "automation.hello")
await hass.async_block_till_done()
assert len(calls) == 0
with patch.object(hass.loop, "stop"):
await hass.async_stop()
assert len(calls) == 1

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,389 @@
"""The tests for the time automation."""
from datetime import timedelta
import pytest
import homeassistant.components.automation as automation
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from tests.async_mock import Mock, patch
from tests.common import (
assert_setup_component,
async_fire_time_changed,
async_mock_service,
mock_component,
)
@pytest.fixture
def calls(hass):
"""Track calls to a mock service."""
return async_mock_service(hass, "test", "automation")
@pytest.fixture(autouse=True)
def setup_comp(hass):
"""Initialize components."""
mock_component(hass, "group")
async def test_if_fires_using_at(hass, calls):
"""Test for firing at."""
now = dt_util.utcnow()
time_that_will_not_match_right_away = now.replace(
hour=4, minute=59, second=0
) + timedelta(2)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "time", "at": "5:00:00"},
"action": {
"service": "test.automation",
"data_template": {
"some": "{{ trigger.platform }} - {{ trigger.now.hour }}"
},
},
}
},
)
now = dt_util.utcnow()
async_fire_time_changed(
hass, now.replace(hour=5, minute=0, second=0) + timedelta(2)
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["some"] == "time - 5"
@pytest.mark.parametrize(
"has_date,has_time", [(True, True), (True, False), (False, True)]
)
async def test_if_fires_using_at_input_datetime(hass, calls, has_date, has_time):
"""Test for firing at input_datetime."""
await async_setup_component(
hass,
"input_datetime",
{"input_datetime": {"trigger": {"has_date": has_date, "has_time": has_time}}},
)
now = dt_util.now()
trigger_dt = now.replace(
hour=5 if has_time else 0, minute=0, second=0, microsecond=0
) + timedelta(2)
await hass.services.async_call(
"input_datetime",
"set_datetime",
{
ATTR_ENTITY_ID: "input_datetime.trigger",
"datetime": str(trigger_dt.replace(tzinfo=None)),
},
blocking=True,
)
time_that_will_not_match_right_away = trigger_dt - timedelta(minutes=1)
some_data = "{{ trigger.platform }}-{{ trigger.now.day }}-{{ trigger.now.hour }}"
with patch(
"homeassistant.util.dt.utcnow",
return_value=dt_util.as_utc(time_that_will_not_match_right_away),
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "time", "at": "input_datetime.trigger"},
"action": {
"service": "test.automation",
"data_template": {"some": some_data},
},
}
},
)
async_fire_time_changed(hass, trigger_dt)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["some"] == f"time-{trigger_dt.day}-{trigger_dt.hour}"
if has_date:
trigger_dt += timedelta(days=1)
if has_time:
trigger_dt += timedelta(hours=1)
await hass.services.async_call(
"input_datetime",
"set_datetime",
{
ATTR_ENTITY_ID: "input_datetime.trigger",
"datetime": str(trigger_dt.replace(tzinfo=None)),
},
blocking=True,
)
async_fire_time_changed(hass, trigger_dt)
await hass.async_block_till_done()
assert len(calls) == 2
assert calls[1].data["some"] == f"time-{trigger_dt.day}-{trigger_dt.hour}"
async def test_if_fires_using_multiple_at(hass, calls):
"""Test for firing at."""
now = dt_util.utcnow()
time_that_will_not_match_right_away = now.replace(
hour=4, minute=59, second=0
) + timedelta(2)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "time", "at": ["5:00:00", "6:00:00"]},
"action": {
"service": "test.automation",
"data_template": {
"some": "{{ trigger.platform }} - {{ trigger.now.hour }}"
},
},
}
},
)
now = dt_util.utcnow()
async_fire_time_changed(
hass, now.replace(hour=5, minute=0, second=0) + timedelta(2)
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["some"] == "time - 5"
async_fire_time_changed(
hass, now.replace(hour=6, minute=0, second=0) + timedelta(2)
)
await hass.async_block_till_done()
assert len(calls) == 2
assert calls[1].data["some"] == "time - 6"
async def test_if_not_fires_using_wrong_at(hass, calls):
"""YAML translates time values to total seconds.
This should break the before rule.
"""
now = dt_util.utcnow()
time_that_will_not_match_right_away = now.replace(
year=now.year + 1, hour=1, minute=0, second=0
)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
with assert_setup_component(0, automation.DOMAIN):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "time",
"at": 3605,
# Total seconds. Hour = 3600 second
},
"action": {"service": "test.automation"},
}
},
)
async_fire_time_changed(
hass, now.replace(year=now.year + 1, hour=1, minute=0, second=5)
)
await hass.async_block_till_done()
assert len(calls) == 0
async def test_if_action_before(hass, calls):
"""Test for if action before."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"condition": {"condition": "time", "before": "10:00"},
"action": {"service": "test.automation"},
}
},
)
before_10 = dt_util.now().replace(hour=8)
after_10 = dt_util.now().replace(hour=14)
with patch("homeassistant.helpers.condition.dt_util.now", return_value=before_10):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 1
with patch("homeassistant.helpers.condition.dt_util.now", return_value=after_10):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_action_after(hass, calls):
"""Test for if action after."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"condition": {"condition": "time", "after": "10:00"},
"action": {"service": "test.automation"},
}
},
)
before_10 = dt_util.now().replace(hour=8)
after_10 = dt_util.now().replace(hour=14)
with patch("homeassistant.helpers.condition.dt_util.now", return_value=before_10):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 0
with patch("homeassistant.helpers.condition.dt_util.now", return_value=after_10):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_action_one_weekday(hass, calls):
"""Test for if action with one weekday."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"condition": {"condition": "time", "weekday": "mon"},
"action": {"service": "test.automation"},
}
},
)
days_past_monday = dt_util.now().weekday()
monday = dt_util.now() - timedelta(days=days_past_monday)
tuesday = monday + timedelta(days=1)
with patch("homeassistant.helpers.condition.dt_util.now", return_value=monday):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 1
with patch("homeassistant.helpers.condition.dt_util.now", return_value=tuesday):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_action_list_weekday(hass, calls):
"""Test for action with a list of weekdays."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"condition": {"condition": "time", "weekday": ["mon", "tue"]},
"action": {"service": "test.automation"},
}
},
)
days_past_monday = dt_util.now().weekday()
monday = dt_util.now() - timedelta(days=days_past_monday)
tuesday = monday + timedelta(days=1)
wednesday = tuesday + timedelta(days=1)
with patch("homeassistant.helpers.condition.dt_util.now", return_value=monday):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 1
with patch("homeassistant.helpers.condition.dt_util.now", return_value=tuesday):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 2
with patch("homeassistant.helpers.condition.dt_util.now", return_value=wednesday):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 2
async def test_untrack_time_change(hass):
"""Test for removing tracked time changes."""
mock_track_time_change = Mock()
with patch(
"homeassistant.components.homeassistant.triggers.time.async_track_time_change",
return_value=mock_track_time_change,
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"alias": "test",
"trigger": {
"platform": "time",
"at": ["5:00:00", "6:00:00", "7:00:00"],
},
"action": {"service": "test.automation", "data": {"test": "test"}},
}
},
)
await hass.services.async_call(
automation.DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "automation.test"},
blocking=True,
)
assert len(mock_track_time_change.mock_calls) == 3

View file

@ -0,0 +1,296 @@
"""The tests for the time_pattern automation."""
from asynctest.mock import patch
import pytest
import homeassistant.components.automation as automation
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from tests.common import async_fire_time_changed, async_mock_service, mock_component
from tests.components.automation import common
@pytest.fixture
def calls(hass):
"""Track calls to a mock service."""
return async_mock_service(hass, "test", "automation")
@pytest.fixture(autouse=True)
def setup_comp(hass):
"""Initialize components."""
mock_component(hass, "group")
async def test_if_fires_when_hour_matches(hass, calls):
"""Test for firing if hour is matching."""
now = dt_util.utcnow()
time_that_will_not_match_right_away = dt_util.utcnow().replace(
year=now.year + 1, hour=3
)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "time_pattern",
"hours": 0,
"minutes": "*",
"seconds": "*",
},
"action": {"service": "test.automation"},
}
},
)
async_fire_time_changed(hass, now.replace(year=now.year + 2, hour=0))
await hass.async_block_till_done()
assert len(calls) == 1
await common.async_turn_off(hass)
await hass.async_block_till_done()
async_fire_time_changed(hass, now.replace(year=now.year + 1, hour=0))
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_fires_when_minute_matches(hass, calls):
"""Test for firing if minutes are matching."""
now = dt_util.utcnow()
time_that_will_not_match_right_away = dt_util.utcnow().replace(
year=now.year + 1, minute=30
)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "time_pattern",
"hours": "*",
"minutes": 0,
"seconds": "*",
},
"action": {"service": "test.automation"},
}
},
)
async_fire_time_changed(hass, now.replace(year=now.year + 2, minute=0))
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_fires_when_second_matches(hass, calls):
"""Test for firing if seconds are matching."""
now = dt_util.utcnow()
time_that_will_not_match_right_away = dt_util.utcnow().replace(
year=now.year + 1, second=30
)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "time_pattern",
"hours": "*",
"minutes": "*",
"seconds": 0,
},
"action": {"service": "test.automation"},
}
},
)
async_fire_time_changed(hass, now.replace(year=now.year + 2, second=0))
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_fires_when_all_matches(hass, calls):
"""Test for firing if everything matches."""
now = dt_util.utcnow()
time_that_will_not_match_right_away = dt_util.utcnow().replace(
year=now.year + 1, hour=4
)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "time_pattern",
"hours": 1,
"minutes": 2,
"seconds": 3,
},
"action": {"service": "test.automation"},
}
},
)
async_fire_time_changed(
hass, now.replace(year=now.year + 2, hour=1, minute=2, second=3)
)
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_fires_periodic_seconds(hass, calls):
"""Test for firing periodically every second."""
now = dt_util.utcnow()
time_that_will_not_match_right_away = dt_util.utcnow().replace(
year=now.year + 1, second=1
)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "time_pattern",
"hours": "*",
"minutes": "*",
"seconds": "/10",
},
"action": {"service": "test.automation"},
}
},
)
async_fire_time_changed(
hass, now.replace(year=now.year + 2, hour=0, minute=0, second=10)
)
await hass.async_block_till_done()
assert len(calls) >= 1
async def test_if_fires_periodic_minutes(hass, calls):
"""Test for firing periodically every minute."""
now = dt_util.utcnow()
time_that_will_not_match_right_away = dt_util.utcnow().replace(
year=now.year + 1, minute=1
)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "time_pattern",
"hours": "*",
"minutes": "/2",
"seconds": "*",
},
"action": {"service": "test.automation"},
}
},
)
async_fire_time_changed(
hass, now.replace(year=now.year + 2, hour=0, minute=2, second=0)
)
await hass.async_block_till_done()
assert len(calls) == 1
async def test_if_fires_periodic_hours(hass, calls):
"""Test for firing periodically every hour."""
now = dt_util.utcnow()
time_that_will_not_match_right_away = dt_util.utcnow().replace(
year=now.year + 1, hour=1
)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "time_pattern",
"hours": "/2",
"minutes": "*",
"seconds": "*",
},
"action": {"service": "test.automation"},
}
},
)
async_fire_time_changed(
hass, now.replace(year=now.year + 2, hour=2, minute=0, second=0)
)
await hass.async_block_till_done()
assert len(calls) == 1
async def test_default_values(hass, calls):
"""Test for firing at 2 minutes every hour."""
now = dt_util.utcnow()
time_that_will_not_match_right_away = dt_util.utcnow().replace(
year=now.year + 1, minute=1
)
with patch(
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
):
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "time_pattern", "minutes": "2"},
"action": {"service": "test.automation"},
}
},
)
async_fire_time_changed(
hass, now.replace(year=now.year + 2, hour=1, minute=2, second=0)
)
await hass.async_block_till_done()
assert len(calls) == 1
async_fire_time_changed(
hass, now.replace(year=now.year + 2, hour=1, minute=2, second=1)
)
await hass.async_block_till_done()
assert len(calls) == 1
async_fire_time_changed(
hass, now.replace(year=now.year + 2, hour=2, minute=2, second=0)
)
await hass.async_block_till_done()
assert len(calls) == 2