2016-03-09 09:25:50 +00:00
|
|
|
"""Test to verify that Home Assistant core works."""
|
2016-10-30 21:18:53 +00:00
|
|
|
# pylint: disable=protected-access
|
2016-09-30 19:57:24 +00:00
|
|
|
import asyncio
|
2019-12-09 15:52:24 +00:00
|
|
|
from datetime import datetime, timedelta
|
2019-01-14 23:08:44 +00:00
|
|
|
import functools
|
2017-06-25 22:10:30 +00:00
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
from tempfile import TemporaryDirectory
|
2019-12-09 15:52:24 +00:00
|
|
|
import unittest
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2016-12-16 05:30:09 +00:00
|
|
|
import pytest
|
2019-12-09 15:52:24 +00:00
|
|
|
import pytz
|
|
|
|
import voluptuous as vol
|
2015-08-04 16:16:10 +00:00
|
|
|
|
|
|
|
from homeassistant.const import (
|
2019-07-31 19:25:30 +00:00
|
|
|
ATTR_FRIENDLY_NAME,
|
|
|
|
ATTR_NOW,
|
|
|
|
ATTR_SECONDS,
|
2019-12-09 15:52:24 +00:00
|
|
|
CONF_UNIT_SYSTEM,
|
|
|
|
EVENT_CALL_SERVICE,
|
|
|
|
EVENT_CORE_CONFIG_UPDATE,
|
2019-07-31 19:25:30 +00:00
|
|
|
EVENT_HOMEASSISTANT_CLOSE,
|
2020-03-30 17:18:39 +00:00
|
|
|
EVENT_HOMEASSISTANT_FINAL_WRITE,
|
2020-05-08 00:29:47 +00:00
|
|
|
EVENT_HOMEASSISTANT_START,
|
2020-06-15 22:22:53 +00:00
|
|
|
EVENT_HOMEASSISTANT_STARTED,
|
2019-12-09 15:52:24 +00:00
|
|
|
EVENT_HOMEASSISTANT_STOP,
|
2019-07-31 19:25:30 +00:00
|
|
|
EVENT_SERVICE_REGISTERED,
|
|
|
|
EVENT_SERVICE_REMOVED,
|
2019-12-09 15:52:24 +00:00
|
|
|
EVENT_STATE_CHANGED,
|
|
|
|
EVENT_TIME_CHANGED,
|
|
|
|
EVENT_TIMER_OUT_OF_SYNC,
|
2020-06-15 22:22:53 +00:00
|
|
|
MATCH_ALL,
|
2019-12-09 15:52:24 +00:00
|
|
|
__version__,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2019-12-09 15:52:24 +00:00
|
|
|
import homeassistant.core as ha
|
|
|
|
from homeassistant.exceptions import InvalidEntityFormatError, InvalidStateError
|
|
|
|
import homeassistant.util.dt as dt_util
|
|
|
|
from homeassistant.util.unit_system import METRIC_SYSTEM
|
2015-08-04 16:16:10 +00:00
|
|
|
|
2020-06-02 18:54:11 +00:00
|
|
|
from tests.async_mock import MagicMock, Mock, PropertyMock, patch
|
2019-12-09 15:52:24 +00:00
|
|
|
from tests.common import async_mock_service, get_test_home_assistant
|
2016-02-14 23:08:23 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
PST = pytz.timezone("America/Los_Angeles")
|
2014-11-23 17:51:16 +00:00
|
|
|
|
|
|
|
|
2016-10-05 03:44:32 +00:00
|
|
|
def test_split_entity_id():
|
|
|
|
"""Test split_entity_id."""
|
2019-07-31 19:25:30 +00:00
|
|
|
assert ha.split_entity_id("domain.object_id") == ["domain", "object_id"]
|
2016-08-09 03:21:40 +00:00
|
|
|
|
2016-10-05 03:44:32 +00:00
|
|
|
|
|
|
|
def test_async_add_job_schedule_callback():
|
|
|
|
"""Test that we schedule coroutines and add jobs to the job pool."""
|
|
|
|
hass = MagicMock()
|
|
|
|
job = MagicMock()
|
|
|
|
|
|
|
|
ha.HomeAssistant.async_add_job(hass, ha.callback(job))
|
|
|
|
assert len(hass.loop.call_soon.mock_calls) == 1
|
|
|
|
assert len(hass.loop.create_task.mock_calls) == 0
|
|
|
|
assert len(hass.add_job.mock_calls) == 0
|
|
|
|
|
|
|
|
|
2019-01-14 23:08:44 +00:00
|
|
|
def test_async_add_job_schedule_partial_callback():
|
|
|
|
"""Test that we schedule partial coros and add jobs to the job pool."""
|
2016-10-05 03:44:32 +00:00
|
|
|
hass = MagicMock()
|
|
|
|
job = MagicMock()
|
2019-01-14 23:08:44 +00:00
|
|
|
partial = functools.partial(ha.callback(job))
|
|
|
|
|
|
|
|
ha.HomeAssistant.async_add_job(hass, partial)
|
|
|
|
assert len(hass.loop.call_soon.mock_calls) == 1
|
|
|
|
assert len(hass.loop.create_task.mock_calls) == 0
|
|
|
|
assert len(hass.add_job.mock_calls) == 0
|
|
|
|
|
|
|
|
|
2019-02-09 21:13:12 +00:00
|
|
|
def test_async_add_job_schedule_coroutinefunction(loop):
|
2019-01-14 23:08:44 +00:00
|
|
|
"""Test that we schedule coroutines and add jobs to the job pool."""
|
2019-02-09 21:13:12 +00:00
|
|
|
hass = MagicMock(loop=MagicMock(wraps=loop))
|
2019-01-14 23:08:44 +00:00
|
|
|
|
|
|
|
async def job():
|
|
|
|
pass
|
2016-10-05 03:44:32 +00:00
|
|
|
|
|
|
|
ha.HomeAssistant.async_add_job(hass, job)
|
|
|
|
assert len(hass.loop.call_soon.mock_calls) == 0
|
|
|
|
assert len(hass.loop.create_task.mock_calls) == 1
|
|
|
|
assert len(hass.add_job.mock_calls) == 0
|
|
|
|
|
|
|
|
|
2019-02-09 21:13:12 +00:00
|
|
|
def test_async_add_job_schedule_partial_coroutinefunction(loop):
|
2019-01-14 23:08:44 +00:00
|
|
|
"""Test that we schedule partial coros and add jobs to the job pool."""
|
2019-02-09 21:13:12 +00:00
|
|
|
hass = MagicMock(loop=MagicMock(wraps=loop))
|
2019-01-14 23:08:44 +00:00
|
|
|
|
|
|
|
async def job():
|
|
|
|
pass
|
2019-07-31 19:25:30 +00:00
|
|
|
|
2019-01-14 23:08:44 +00:00
|
|
|
partial = functools.partial(job)
|
|
|
|
|
|
|
|
ha.HomeAssistant.async_add_job(hass, partial)
|
|
|
|
assert len(hass.loop.call_soon.mock_calls) == 0
|
|
|
|
assert len(hass.loop.create_task.mock_calls) == 1
|
|
|
|
assert len(hass.add_job.mock_calls) == 0
|
|
|
|
|
|
|
|
|
|
|
|
def test_async_add_job_add_threaded_job_to_pool():
|
2016-10-05 03:44:32 +00:00
|
|
|
"""Test that we schedule coroutines and add jobs to the job pool."""
|
|
|
|
hass = MagicMock()
|
2019-01-14 23:08:44 +00:00
|
|
|
|
|
|
|
def job():
|
|
|
|
pass
|
2016-10-05 03:44:32 +00:00
|
|
|
|
|
|
|
ha.HomeAssistant.async_add_job(hass, job)
|
|
|
|
assert len(hass.loop.call_soon.mock_calls) == 0
|
|
|
|
assert len(hass.loop.create_task.mock_calls) == 0
|
2016-11-05 16:27:55 +00:00
|
|
|
assert len(hass.loop.run_in_executor.mock_calls) == 1
|
2016-10-05 03:44:32 +00:00
|
|
|
|
|
|
|
|
2019-02-09 21:13:12 +00:00
|
|
|
def test_async_create_task_schedule_coroutine(loop):
|
2018-07-13 10:24:51 +00:00
|
|
|
"""Test that we schedule coroutines and add jobs to the job pool."""
|
2019-02-09 21:13:12 +00:00
|
|
|
hass = MagicMock(loop=MagicMock(wraps=loop))
|
2018-07-13 10:24:51 +00:00
|
|
|
|
2019-01-14 23:08:44 +00:00
|
|
|
async def job():
|
|
|
|
pass
|
|
|
|
|
|
|
|
ha.HomeAssistant.async_create_task(hass, job())
|
2018-07-13 10:24:51 +00:00
|
|
|
assert len(hass.loop.call_soon.mock_calls) == 0
|
|
|
|
assert len(hass.loop.create_task.mock_calls) == 1
|
|
|
|
assert len(hass.add_job.mock_calls) == 0
|
|
|
|
|
|
|
|
|
2016-10-05 03:44:32 +00:00
|
|
|
def test_async_run_job_calls_callback():
|
|
|
|
"""Test that the callback annotation is respected."""
|
|
|
|
hass = MagicMock()
|
|
|
|
calls = []
|
|
|
|
|
|
|
|
def job():
|
|
|
|
calls.append(1)
|
|
|
|
|
|
|
|
ha.HomeAssistant.async_run_job(hass, ha.callback(job))
|
|
|
|
assert len(calls) == 1
|
|
|
|
assert len(hass.async_add_job.mock_calls) == 0
|
|
|
|
|
|
|
|
|
|
|
|
def test_async_run_job_delegates_non_async():
|
|
|
|
"""Test that the callback annotation is respected."""
|
|
|
|
hass = MagicMock()
|
|
|
|
calls = []
|
|
|
|
|
|
|
|
def job():
|
|
|
|
calls.append(1)
|
|
|
|
|
|
|
|
ha.HomeAssistant.async_run_job(hass, job)
|
|
|
|
assert len(calls) == 0
|
|
|
|
assert len(hass.async_add_job.mock_calls) == 1
|
2016-08-09 03:21:40 +00:00
|
|
|
|
|
|
|
|
2017-02-13 05:24:07 +00:00
|
|
|
def test_stage_shutdown():
|
|
|
|
"""Simulate a shutdown, test calling stuff."""
|
|
|
|
hass = get_test_home_assistant()
|
|
|
|
test_stop = []
|
2020-03-30 17:18:39 +00:00
|
|
|
test_final_write = []
|
2017-02-13 05:24:07 +00:00
|
|
|
test_close = []
|
|
|
|
test_all = []
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.bus.listen(EVENT_HOMEASSISTANT_STOP, lambda event: test_stop.append(event))
|
2020-03-30 17:18:39 +00:00
|
|
|
hass.bus.listen(
|
|
|
|
EVENT_HOMEASSISTANT_FINAL_WRITE, lambda event: test_final_write.append(event)
|
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.bus.listen(EVENT_HOMEASSISTANT_CLOSE, lambda event: test_close.append(event))
|
|
|
|
hass.bus.listen("*", lambda event: test_all.append(event))
|
2017-02-13 05:24:07 +00:00
|
|
|
|
|
|
|
hass.stop()
|
|
|
|
|
|
|
|
assert len(test_stop) == 1
|
|
|
|
assert len(test_close) == 1
|
2020-03-30 17:18:39 +00:00
|
|
|
assert len(test_final_write) == 1
|
|
|
|
assert len(test_all) == 2
|
2017-02-13 05:24:07 +00:00
|
|
|
|
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def test_pending_sheduler(hass):
|
|
|
|
"""Add a coro to pending tasks."""
|
|
|
|
call_count = []
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def test_coro():
|
|
|
|
"""Test Coro."""
|
|
|
|
call_count.append("call")
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
for _ in range(3):
|
|
|
|
hass.async_add_job(test_coro())
|
2014-11-23 20:57:29 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
await asyncio.wait(hass._pending_tasks)
|
2016-11-08 09:24:50 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
assert len(hass._pending_tasks) == 3
|
|
|
|
assert len(call_count) == 3
|
2016-11-08 09:24:50 +00:00
|
|
|
|
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def test_async_add_job_pending_tasks_coro(hass):
|
|
|
|
"""Add a coro to pending tasks."""
|
|
|
|
call_count = []
|
2016-11-09 16:41:17 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def test_coro():
|
|
|
|
"""Test Coro."""
|
|
|
|
call_count.append("call")
|
2016-11-08 09:24:50 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
for _ in range(2):
|
|
|
|
hass.add_job(test_coro())
|
2016-11-08 09:24:50 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def wait_finish_callback():
|
|
|
|
"""Wait until all stuff is scheduled."""
|
|
|
|
await asyncio.sleep(0)
|
|
|
|
await asyncio.sleep(0)
|
2016-11-08 09:24:50 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
await wait_finish_callback()
|
2016-11-08 09:24:50 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
assert len(hass._pending_tasks) == 2
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(call_count) == 2
|
2016-11-17 04:00:08 +00:00
|
|
|
|
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def test_async_add_job_pending_tasks_executor(hass):
|
|
|
|
"""Run an executor in pending tasks."""
|
|
|
|
call_count = []
|
2016-11-08 09:24:50 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
def test_executor():
|
|
|
|
"""Test executor."""
|
|
|
|
call_count.append("call")
|
2016-11-09 04:01:05 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def wait_finish_callback():
|
|
|
|
"""Wait until all stuff is scheduled."""
|
|
|
|
await asyncio.sleep(0)
|
|
|
|
await asyncio.sleep(0)
|
2016-11-09 04:01:05 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
for _ in range(2):
|
|
|
|
hass.async_add_job(test_executor)
|
2016-11-17 04:00:08 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
await wait_finish_callback()
|
2016-11-09 04:01:05 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
assert len(hass._pending_tasks) == 2
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(call_count) == 2
|
2016-11-17 04:00:08 +00:00
|
|
|
|
2016-11-09 04:01:05 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def test_async_add_job_pending_tasks_callback(hass):
|
|
|
|
"""Run a callback in pending tasks."""
|
|
|
|
call_count = []
|
2016-11-09 04:01:05 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
@ha.callback
|
|
|
|
def test_callback():
|
|
|
|
"""Test callback."""
|
|
|
|
call_count.append("call")
|
2016-11-09 04:01:05 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def wait_finish_callback():
|
|
|
|
"""Wait until all stuff is scheduled."""
|
|
|
|
await asyncio.sleep(0)
|
|
|
|
await asyncio.sleep(0)
|
2016-11-17 04:00:08 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
for _ in range(2):
|
|
|
|
hass.async_add_job(test_callback)
|
2016-11-09 04:01:05 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
await wait_finish_callback()
|
2016-11-17 04:00:08 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(hass._pending_tasks) == 0
|
|
|
|
assert len(call_count) == 2
|
2016-11-17 04:00:08 +00:00
|
|
|
|
2016-11-09 04:01:05 +00:00
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def test_add_job_with_none(hass):
|
|
|
|
"""Try to add a job with None as function."""
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
hass.async_add_job(None, "test_arg")
|
2016-12-16 05:30:09 +00:00
|
|
|
|
2014-11-23 17:51:16 +00:00
|
|
|
|
|
|
|
class TestEvent(unittest.TestCase):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""A Test Event class."""
|
|
|
|
|
2015-08-04 16:16:10 +00:00
|
|
|
def test_eq(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test events."""
|
2015-08-04 16:16:10 +00:00
|
|
|
now = dt_util.utcnow()
|
2019-07-31 19:25:30 +00:00
|
|
|
data = {"some": "attr"}
|
2018-08-10 16:09:01 +00:00
|
|
|
context = ha.Context()
|
2015-08-04 16:16:10 +00:00
|
|
|
event1, event2 = [
|
2019-07-31 19:25:30 +00:00
|
|
|
ha.Event("some_type", data, time_fired=now, context=context)
|
2015-08-04 16:16:10 +00:00
|
|
|
for _ in range(2)
|
|
|
|
]
|
|
|
|
|
2018-10-24 10:10:05 +00:00
|
|
|
assert event1 == event2
|
2015-08-04 16:16:10 +00:00
|
|
|
|
2014-11-23 17:51:16 +00:00
|
|
|
def test_repr(self):
|
2016-03-09 10:15:04 +00:00
|
|
|
"""Test that repr method works."""
|
2020-04-06 10:51:48 +00:00
|
|
|
assert str(ha.Event("TestEvent")) == "<Event TestEvent[L]>"
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2020-04-06 10:51:48 +00:00
|
|
|
assert (
|
|
|
|
str(ha.Event("TestEvent", {"beer": "nice"}, ha.EventOrigin.remote))
|
|
|
|
== "<Event TestEvent[R]: beer=nice>"
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2015-08-04 16:16:10 +00:00
|
|
|
def test_as_dict(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test as dictionary."""
|
2019-07-31 19:25:30 +00:00
|
|
|
event_type = "some_type"
|
2015-08-04 16:16:10 +00:00
|
|
|
now = dt_util.utcnow()
|
2019-07-31 19:25:30 +00:00
|
|
|
data = {"some": "attr"}
|
2015-08-04 16:16:10 +00:00
|
|
|
|
|
|
|
event = ha.Event(event_type, data, ha.EventOrigin.local, now)
|
|
|
|
expected = {
|
2019-07-31 19:25:30 +00:00
|
|
|
"event_type": event_type,
|
|
|
|
"data": data,
|
|
|
|
"origin": "LOCAL",
|
|
|
|
"time_fired": now,
|
|
|
|
"context": {
|
|
|
|
"id": event.context.id,
|
|
|
|
"parent_id": None,
|
|
|
|
"user_id": event.context.user_id,
|
2018-07-29 00:53:37 +00:00
|
|
|
},
|
2015-08-04 16:16:10 +00:00
|
|
|
}
|
2018-10-24 10:10:05 +00:00
|
|
|
assert expected == event.as_dict()
|
2015-08-04 16:16:10 +00:00
|
|
|
|
2014-11-23 17:51:16 +00:00
|
|
|
|
|
|
|
class TestEventBus(unittest.TestCase):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test EventBus methods."""
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2016-10-30 21:18:53 +00:00
|
|
|
# pylint: disable=invalid-name
|
|
|
|
def setUp(self):
|
2018-08-19 20:29:08 +00:00
|
|
|
"""Set up things to be run when tests are started."""
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass = get_test_home_assistant()
|
|
|
|
self.bus = self.hass.bus
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2016-10-30 21:18:53 +00:00
|
|
|
# pylint: disable=invalid-name
|
|
|
|
def tearDown(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Stop down stuff we started."""
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.stop()
|
2014-11-23 20:57:29 +00:00
|
|
|
|
2014-11-23 17:51:16 +00:00
|
|
|
def test_add_remove_listener(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test remove_listener method."""
|
2016-10-31 15:47:29 +00:00
|
|
|
self.hass.allow_pool = False
|
2014-11-23 17:51:16 +00:00
|
|
|
old_count = len(self.bus.listeners)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
def listener(_):
|
|
|
|
pass
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
unsub = self.bus.listen("test", listener)
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2018-10-24 10:10:05 +00:00
|
|
|
assert old_count + 1 == len(self.bus.listeners)
|
2014-11-23 17:51:16 +00:00
|
|
|
|
|
|
|
# Remove listener
|
2016-10-18 02:38:41 +00:00
|
|
|
unsub()
|
2018-10-24 10:10:05 +00:00
|
|
|
assert old_count == len(self.bus.listeners)
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2016-10-18 02:38:41 +00:00
|
|
|
# Should do nothing now
|
|
|
|
unsub()
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2016-08-26 06:25:35 +00:00
|
|
|
def test_unsubscribe_listener(self):
|
|
|
|
"""Test unsubscribe listener from returned function."""
|
|
|
|
calls = []
|
|
|
|
|
2016-10-31 15:47:29 +00:00
|
|
|
@ha.callback
|
2016-08-26 06:25:35 +00:00
|
|
|
def listener(event):
|
|
|
|
"""Mock listener."""
|
|
|
|
calls.append(event)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
unsub = self.bus.listen("test", listener)
|
2016-08-26 06:25:35 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.fire("test")
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2016-08-26 06:25:35 +00:00
|
|
|
|
|
|
|
assert len(calls) == 1
|
|
|
|
|
|
|
|
unsub()
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.fire("event")
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2016-08-26 06:25:35 +00:00
|
|
|
|
|
|
|
assert len(calls) == 1
|
|
|
|
|
2016-10-18 02:38:41 +00:00
|
|
|
def test_listen_once_event_with_callback(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test listen_once_event method."""
|
2014-11-29 07:19:59 +00:00
|
|
|
runs = []
|
|
|
|
|
2016-10-18 02:38:41 +00:00
|
|
|
@ha.callback
|
|
|
|
def event_handler(event):
|
|
|
|
runs.append(event)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.listen_once("test_event", event_handler)
|
2016-10-18 02:38:41 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.fire("test_event")
|
2016-10-18 02:38:41 +00:00
|
|
|
# Second time it should not increase runs
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.fire("test_event")
|
2016-10-18 02:38:41 +00:00
|
|
|
|
|
|
|
self.hass.block_till_done()
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(runs) == 1
|
2016-10-18 02:38:41 +00:00
|
|
|
|
|
|
|
def test_listen_once_event_with_coroutine(self):
|
|
|
|
"""Test listen_once_event method."""
|
|
|
|
runs = []
|
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def event_handler(event):
|
2016-10-18 02:38:41 +00:00
|
|
|
runs.append(event)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.listen_once("test_event", event_handler)
|
2016-10-18 02:38:41 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.fire("test_event")
|
2016-10-18 02:38:41 +00:00
|
|
|
# Second time it should not increase runs
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.fire("test_event")
|
2016-10-18 02:38:41 +00:00
|
|
|
|
|
|
|
self.hass.block_till_done()
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(runs) == 1
|
2016-10-18 02:38:41 +00:00
|
|
|
|
|
|
|
def test_listen_once_event_with_thread(self):
|
|
|
|
"""Test listen_once_event method."""
|
|
|
|
runs = []
|
|
|
|
|
|
|
|
def event_handler(event):
|
|
|
|
runs.append(event)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.listen_once("test_event", event_handler)
|
2014-11-29 07:19:59 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.fire("test_event")
|
2014-11-29 07:19:59 +00:00
|
|
|
# Second time it should not increase runs
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.fire("test_event")
|
2015-08-04 16:16:10 +00:00
|
|
|
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(runs) == 1
|
2014-11-29 07:19:59 +00:00
|
|
|
|
2016-10-05 03:44:32 +00:00
|
|
|
def test_thread_event_listener(self):
|
2018-05-13 10:09:28 +00:00
|
|
|
"""Test thread event listener."""
|
2016-10-05 03:44:32 +00:00
|
|
|
thread_calls = []
|
|
|
|
|
|
|
|
def thread_listener(event):
|
|
|
|
thread_calls.append(event)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.listen("test_thread", thread_listener)
|
|
|
|
self.bus.fire("test_thread")
|
2016-10-05 03:44:32 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
assert len(thread_calls) == 1
|
|
|
|
|
|
|
|
def test_callback_event_listener(self):
|
2018-05-13 10:09:28 +00:00
|
|
|
"""Test callback event listener."""
|
2016-10-05 03:44:32 +00:00
|
|
|
callback_calls = []
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def callback_listener(event):
|
|
|
|
callback_calls.append(event)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.listen("test_callback", callback_listener)
|
|
|
|
self.bus.fire("test_callback")
|
2016-10-05 03:44:32 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
assert len(callback_calls) == 1
|
|
|
|
|
|
|
|
def test_coroutine_event_listener(self):
|
2018-05-13 10:09:28 +00:00
|
|
|
"""Test coroutine event listener."""
|
2016-10-05 03:44:32 +00:00
|
|
|
coroutine_calls = []
|
|
|
|
|
2020-08-26 14:57:52 +00:00
|
|
|
async def coroutine_listener(event):
|
2016-10-05 03:44:32 +00:00
|
|
|
coroutine_calls.append(event)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.bus.listen("test_coroutine", coroutine_listener)
|
|
|
|
self.bus.fire("test_coroutine")
|
2016-10-05 03:44:32 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
assert len(coroutine_calls) == 1
|
|
|
|
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2019-02-20 07:02:56 +00:00
|
|
|
def test_state_init():
|
|
|
|
"""Test state.init."""
|
|
|
|
with pytest.raises(InvalidEntityFormatError):
|
2019-07-31 19:25:30 +00:00
|
|
|
ha.State("invalid_entity_format", "test_state")
|
2015-08-04 16:16:10 +00:00
|
|
|
|
2019-02-20 07:02:56 +00:00
|
|
|
with pytest.raises(InvalidStateError):
|
2019-07-31 19:25:30 +00:00
|
|
|
ha.State("domain.long_state", "t" * 256)
|
2019-02-20 07:02:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_state_domain():
|
|
|
|
"""Test domain."""
|
2019-07-31 19:25:30 +00:00
|
|
|
state = ha.State("some_domain.hello", "world")
|
2020-04-06 10:51:48 +00:00
|
|
|
assert state.domain == "some_domain"
|
2019-02-20 07:02:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_state_object_id():
|
|
|
|
"""Test object ID."""
|
2019-07-31 19:25:30 +00:00
|
|
|
state = ha.State("domain.hello", "world")
|
2020-04-06 10:51:48 +00:00
|
|
|
assert state.object_id == "hello"
|
2019-02-20 07:02:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_state_name_if_no_friendly_name_attr():
|
|
|
|
"""Test if there is no friendly name."""
|
2019-07-31 19:25:30 +00:00
|
|
|
state = ha.State("domain.hello_world", "world")
|
2020-04-06 10:51:48 +00:00
|
|
|
assert state.name == "hello world"
|
2019-02-20 07:02:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_state_name_if_friendly_name_attr():
|
|
|
|
"""Test if there is a friendly name."""
|
2019-07-31 19:25:30 +00:00
|
|
|
name = "Some Unique Name"
|
|
|
|
state = ha.State("domain.hello_world", "world", {ATTR_FRIENDLY_NAME: name})
|
2020-04-06 10:51:48 +00:00
|
|
|
assert state.name == name
|
2019-02-20 07:02:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_state_dict_conversion():
|
|
|
|
"""Test conversion of dict."""
|
2019-07-31 19:25:30 +00:00
|
|
|
state = ha.State("domain.hello", "world", {"some": "attr"})
|
2019-02-20 07:02:56 +00:00
|
|
|
assert state == ha.State.from_dict(state.as_dict())
|
|
|
|
|
|
|
|
|
|
|
|
def test_state_dict_conversion_with_wrong_data():
|
|
|
|
"""Test conversion with wrong data."""
|
|
|
|
assert ha.State.from_dict(None) is None
|
2019-07-31 19:25:30 +00:00
|
|
|
assert ha.State.from_dict({"state": "yes"}) is None
|
|
|
|
assert ha.State.from_dict({"entity_id": "yes"}) is None
|
2019-02-20 07:02:56 +00:00
|
|
|
# Make sure invalid context data doesn't crash
|
2019-07-31 19:25:30 +00:00
|
|
|
wrong_context = ha.State.from_dict(
|
|
|
|
{
|
|
|
|
"entity_id": "light.kitchen",
|
|
|
|
"state": "on",
|
|
|
|
"context": {"id": "123", "non-existing": "crash"},
|
2019-02-20 07:02:56 +00:00
|
|
|
}
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2019-02-20 07:02:56 +00:00
|
|
|
assert wrong_context is not None
|
2019-07-31 19:25:30 +00:00
|
|
|
assert wrong_context.context.id == "123"
|
2019-02-20 07:02:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_state_repr():
|
|
|
|
"""Test state.repr."""
|
2020-04-06 10:51:48 +00:00
|
|
|
assert (
|
|
|
|
str(ha.State("happy.happy", "on", last_changed=datetime(1984, 12, 8, 12, 0, 0)))
|
|
|
|
== "<state happy.happy=on @ 1984-12-08T12:00:00+00:00>"
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
assert (
|
2020-04-06 10:51:48 +00:00
|
|
|
str(
|
2019-07-31 19:25:30 +00:00
|
|
|
ha.State(
|
|
|
|
"happy.happy",
|
|
|
|
"on",
|
|
|
|
{"brightness": 144},
|
|
|
|
datetime(1984, 12, 8, 12, 0, 0),
|
|
|
|
)
|
|
|
|
)
|
2020-04-06 10:51:48 +00:00
|
|
|
== "<state happy.happy=on; brightness=144 @ "
|
|
|
|
"1984-12-08T12:00:00+00:00>"
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2014-11-23 17:51:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TestStateMachine(unittest.TestCase):
|
2016-03-09 10:15:04 +00:00
|
|
|
"""Test State machine methods."""
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2016-10-30 21:18:53 +00:00
|
|
|
# pylint: disable=invalid-name
|
|
|
|
def setUp(self):
|
2018-08-19 20:29:08 +00:00
|
|
|
"""Set up things to be run when tests are started."""
|
2016-10-31 15:47:29 +00:00
|
|
|
self.hass = get_test_home_assistant()
|
2016-09-13 02:16:14 +00:00
|
|
|
self.states = self.hass.states
|
2014-11-23 17:51:16 +00:00
|
|
|
self.states.set("light.Bowl", "on")
|
|
|
|
self.states.set("switch.AC", "off")
|
|
|
|
|
2016-10-30 21:18:53 +00:00
|
|
|
# pylint: disable=invalid-name
|
|
|
|
def tearDown(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Stop down stuff we started."""
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.stop()
|
2014-11-23 20:57:29 +00:00
|
|
|
|
2014-11-23 17:51:16 +00:00
|
|
|
def test_is_state(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test is_state method."""
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.states.is_state("light.Bowl", "on")
|
|
|
|
assert not self.states.is_state("light.Bowl", "off")
|
|
|
|
assert not self.states.is_state("light.Non_existing", "on")
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2014-11-29 07:19:59 +00:00
|
|
|
def test_entity_ids(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test get_entity_ids method."""
|
2014-11-29 07:19:59 +00:00
|
|
|
ent_ids = self.states.entity_ids()
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(ent_ids) == 2
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "light.bowl" in ent_ids
|
|
|
|
assert "switch.ac" in ent_ids
|
2014-11-29 07:19:59 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
ent_ids = self.states.entity_ids("light")
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(ent_ids) == 1
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "light.bowl" in ent_ids
|
2014-11-29 07:19:59 +00:00
|
|
|
|
2015-08-04 16:16:10 +00:00
|
|
|
def test_all(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test everything."""
|
2015-08-04 16:16:10 +00:00
|
|
|
states = sorted(state.entity_id for state in self.states.all())
|
2019-07-31 19:25:30 +00:00
|
|
|
assert ["light.bowl", "switch.ac"] == states
|
2015-08-04 16:16:10 +00:00
|
|
|
|
2014-11-23 17:51:16 +00:00
|
|
|
def test_remove(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test remove method."""
|
2016-02-14 06:57:40 +00:00
|
|
|
events = []
|
2016-10-31 15:47:29 +00:00
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def callback(event):
|
|
|
|
events.append(event)
|
|
|
|
|
|
|
|
self.hass.bus.listen(EVENT_STATE_CHANGED, callback)
|
2016-02-14 06:57:40 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "light.bowl" in self.states.entity_ids()
|
|
|
|
assert self.states.remove("light.bowl")
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2016-02-14 06:57:40 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "light.bowl" not in self.states.entity_ids()
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(events) == 1
|
|
|
|
assert events[0].data.get("entity_id") == "light.bowl"
|
2019-07-31 19:25:30 +00:00
|
|
|
assert events[0].data.get("old_state") is not None
|
2020-04-06 10:51:48 +00:00
|
|
|
assert events[0].data["old_state"].entity_id == "light.bowl"
|
2019-07-31 19:25:30 +00:00
|
|
|
assert events[0].data.get("new_state") is None
|
2014-11-23 17:51:16 +00:00
|
|
|
|
|
|
|
# If it does not exist, we should get False
|
2019-07-31 19:25:30 +00:00
|
|
|
assert not self.states.remove("light.Bowl")
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(events) == 1
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2014-12-27 07:26:39 +00:00
|
|
|
def test_case_insensitivty(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test insensitivty."""
|
2014-12-27 07:26:39 +00:00
|
|
|
runs = []
|
|
|
|
|
2016-10-31 15:47:29 +00:00
|
|
|
@ha.callback
|
|
|
|
def callback(event):
|
|
|
|
runs.append(event)
|
|
|
|
|
|
|
|
self.hass.bus.listen(EVENT_STATE_CHANGED, callback)
|
2014-12-27 07:26:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.states.set("light.BOWL", "off")
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2014-12-27 07:26:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.states.is_state("light.bowl", "off")
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(runs) == 1
|
2014-12-27 07:26:39 +00:00
|
|
|
|
2015-01-02 16:48:20 +00:00
|
|
|
def test_last_changed_not_updated_on_same_state(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test to not update the existing, same state."""
|
2019-07-31 19:25:30 +00:00
|
|
|
state = self.states.get("light.Bowl")
|
2015-01-02 16:48:20 +00:00
|
|
|
|
2015-09-13 05:56:49 +00:00
|
|
|
future = dt_util.utcnow() + timedelta(hours=10)
|
2015-01-02 16:48:20 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
with patch("homeassistant.util.dt.utcnow", return_value=future):
|
|
|
|
self.states.set("light.Bowl", "on", {"attr": "triggers_change"})
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2015-01-02 16:48:20 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
state2 = self.states.get("light.Bowl")
|
2016-09-13 02:16:14 +00:00
|
|
|
assert state2 is not None
|
|
|
|
assert state.last_changed == state2.last_changed
|
2015-01-02 16:48:20 +00:00
|
|
|
|
2016-06-26 07:33:23 +00:00
|
|
|
def test_force_update(self):
|
|
|
|
"""Test force update option."""
|
|
|
|
events = []
|
2016-10-31 15:47:29 +00:00
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def callback(event):
|
|
|
|
events.append(event)
|
|
|
|
|
|
|
|
self.hass.bus.listen(EVENT_STATE_CHANGED, callback)
|
2016-06-26 07:33:23 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.states.set("light.bowl", "on")
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(events) == 0
|
2016-06-26 07:33:23 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.states.set("light.bowl", "on", None, True)
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(events) == 1
|
2016-06-26 07:33:23 +00:00
|
|
|
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2018-07-29 00:53:37 +00:00
|
|
|
def test_service_call_repr():
|
|
|
|
"""Test ServiceCall repr."""
|
2019-07-31 19:25:30 +00:00
|
|
|
call = ha.ServiceCall("homeassistant", "start")
|
2020-01-03 13:47:06 +00:00
|
|
|
assert str(call) == f"<ServiceCall homeassistant.start (c:{call.context.id})>"
|
2016-03-09 09:25:50 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
call2 = ha.ServiceCall("homeassistant", "start", {"fast": "yes"})
|
2020-01-03 13:47:06 +00:00
|
|
|
assert (
|
|
|
|
str(call2)
|
|
|
|
== f"<ServiceCall homeassistant.start (c:{call2.context.id}): fast=yes>"
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2014-11-23 17:51:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TestServiceRegistry(unittest.TestCase):
|
2016-03-09 10:15:04 +00:00
|
|
|
"""Test ServicerRegistry methods."""
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2016-10-30 21:18:53 +00:00
|
|
|
# pylint: disable=invalid-name
|
|
|
|
def setUp(self):
|
2018-08-19 20:29:08 +00:00
|
|
|
"""Set up things to be run when tests are started."""
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass = get_test_home_assistant()
|
|
|
|
self.services = self.hass.services
|
2016-11-05 23:36:20 +00:00
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def mock_service(call):
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.services.register("Test_Domain", "TEST_SERVICE", mock_service)
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2017-03-08 06:51:34 +00:00
|
|
|
self.calls_register = []
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def mock_event_register(event):
|
|
|
|
"""Mock register event."""
|
|
|
|
self.calls_register.append(event)
|
|
|
|
|
|
|
|
self.hass.bus.listen(EVENT_SERVICE_REGISTERED, mock_event_register)
|
|
|
|
|
2016-10-30 21:18:53 +00:00
|
|
|
# pylint: disable=invalid-name
|
|
|
|
def tearDown(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Stop down stuff we started."""
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.stop()
|
2014-11-23 20:57:29 +00:00
|
|
|
|
2014-11-23 17:51:16 +00:00
|
|
|
def test_has_service(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test has_service method."""
|
2018-10-24 10:10:05 +00:00
|
|
|
assert self.services.has_service("tesT_domaiN", "tesT_servicE")
|
|
|
|
assert not self.services.has_service("test_domain", "non_existing")
|
|
|
|
assert not self.services.has_service("non_existing", "test_service")
|
2015-08-04 16:16:10 +00:00
|
|
|
|
|
|
|
def test_services(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test services."""
|
2018-01-07 22:54:16 +00:00
|
|
|
assert len(self.services.services) == 1
|
2015-08-04 16:16:10 +00:00
|
|
|
|
|
|
|
def test_call_with_blocking_done_in_time(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test call with blocking."""
|
2015-08-04 16:16:10 +00:00
|
|
|
calls = []
|
2016-09-30 19:57:24 +00:00
|
|
|
|
2016-11-05 23:36:20 +00:00
|
|
|
@ha.callback
|
2016-09-30 19:57:24 +00:00
|
|
|
def service_handler(call):
|
|
|
|
"""Service handler."""
|
|
|
|
calls.append(call)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.services.register("test_domain", "register_calls", service_handler)
|
2017-03-08 06:51:34 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
|
|
|
assert len(self.calls_register) == 1
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.calls_register[-1].data["domain"] == "test_domain"
|
|
|
|
assert self.calls_register[-1].data["service"] == "register_calls"
|
2015-08-04 16:16:10 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.services.call("test_domain", "REGISTER_CALLS", blocking=True)
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(calls) == 1
|
2015-08-04 16:16:10 +00:00
|
|
|
|
|
|
|
def test_call_non_existing_with_blocking(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test non-existing with blocking."""
|
2018-11-30 20:28:35 +00:00
|
|
|
with pytest.raises(ha.ServiceNotFound):
|
2019-07-31 19:25:30 +00:00
|
|
|
self.services.call("test_domain", "i_do_not_exist", blocking=True)
|
2015-08-04 16:16:10 +00:00
|
|
|
|
2016-09-30 19:57:24 +00:00
|
|
|
def test_async_service(self):
|
|
|
|
"""Test registering and calling an async service."""
|
|
|
|
calls = []
|
|
|
|
|
2019-03-02 07:09:31 +00:00
|
|
|
async def service_handler(call):
|
2016-09-30 19:57:24 +00:00
|
|
|
"""Service handler coroutine."""
|
|
|
|
calls.append(call)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.services.register("test_domain", "register_calls", service_handler)
|
2017-03-08 06:51:34 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
|
|
|
assert len(self.calls_register) == 1
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.calls_register[-1].data["domain"] == "test_domain"
|
|
|
|
assert self.calls_register[-1].data["service"] == "register_calls"
|
2017-03-08 06:51:34 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.services.call("test_domain", "REGISTER_CALLS", blocking=True)
|
2016-09-30 19:57:24 +00:00
|
|
|
self.hass.block_till_done()
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(calls) == 1
|
2016-09-30 19:57:24 +00:00
|
|
|
|
2019-05-07 16:39:42 +00:00
|
|
|
def test_async_service_partial(self):
|
|
|
|
"""Test registering and calling an wrapped async service."""
|
|
|
|
calls = []
|
|
|
|
|
|
|
|
async def service_handler(call):
|
|
|
|
"""Service handler coroutine."""
|
|
|
|
calls.append(call)
|
|
|
|
|
|
|
|
self.services.register(
|
2019-07-31 19:25:30 +00:00
|
|
|
"test_domain", "register_calls", functools.partial(service_handler)
|
|
|
|
)
|
2019-05-07 16:39:42 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
|
|
|
assert len(self.calls_register) == 1
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.calls_register[-1].data["domain"] == "test_domain"
|
|
|
|
assert self.calls_register[-1].data["service"] == "register_calls"
|
2019-05-07 16:39:42 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.services.call("test_domain", "REGISTER_CALLS", blocking=True)
|
2019-05-07 16:39:42 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
assert len(calls) == 1
|
|
|
|
|
2016-10-05 03:44:32 +00:00
|
|
|
def test_callback_service(self):
|
|
|
|
"""Test registering and calling an async service."""
|
|
|
|
calls = []
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def service_handler(call):
|
|
|
|
"""Service handler coroutine."""
|
|
|
|
calls.append(call)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.services.register("test_domain", "register_calls", service_handler)
|
2017-03-08 06:51:34 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
|
|
|
assert len(self.calls_register) == 1
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.calls_register[-1].data["domain"] == "test_domain"
|
|
|
|
assert self.calls_register[-1].data["service"] == "register_calls"
|
2017-03-08 06:51:34 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.services.call("test_domain", "REGISTER_CALLS", blocking=True)
|
2016-10-05 03:44:32 +00:00
|
|
|
self.hass.block_till_done()
|
2020-04-06 10:51:48 +00:00
|
|
|
assert len(calls) == 1
|
2016-10-05 03:44:32 +00:00
|
|
|
|
2017-03-08 06:51:34 +00:00
|
|
|
def test_remove_service(self):
|
|
|
|
"""Test remove service."""
|
|
|
|
calls_remove = []
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def mock_event_remove(event):
|
|
|
|
"""Mock register event."""
|
|
|
|
calls_remove.append(event)
|
|
|
|
|
|
|
|
self.hass.bus.listen(EVENT_SERVICE_REMOVED, mock_event_remove)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.services.has_service("test_Domain", "test_Service")
|
2017-03-08 06:51:34 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.services.remove("test_Domain", "test_Service")
|
2017-03-08 06:51:34 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert not self.services.has_service("test_Domain", "test_Service")
|
2017-03-08 06:51:34 +00:00
|
|
|
assert len(calls_remove) == 1
|
2019-07-31 19:25:30 +00:00
|
|
|
assert calls_remove[-1].data["domain"] == "test_domain"
|
|
|
|
assert calls_remove[-1].data["service"] == "test_service"
|
2017-03-08 06:51:34 +00:00
|
|
|
|
|
|
|
def test_remove_service_that_not_exists(self):
|
|
|
|
"""Test remove service that not exists."""
|
|
|
|
calls_remove = []
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def mock_event_remove(event):
|
|
|
|
"""Mock register event."""
|
|
|
|
calls_remove.append(event)
|
|
|
|
|
|
|
|
self.hass.bus.listen(EVENT_SERVICE_REMOVED, mock_event_remove)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert not self.services.has_service("test_xxx", "test_yyy")
|
|
|
|
self.services.remove("test_xxx", "test_yyy")
|
2017-03-08 06:51:34 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
assert len(calls_remove) == 0
|
|
|
|
|
2019-03-02 07:09:31 +00:00
|
|
|
def test_async_service_raise_exception(self):
|
|
|
|
"""Test registering and calling an async service raise exception."""
|
2019-07-31 19:25:30 +00:00
|
|
|
|
2019-03-02 07:09:31 +00:00
|
|
|
async def service_handler(_):
|
|
|
|
"""Service handler coroutine."""
|
|
|
|
raise ValueError
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.services.register("test_domain", "register_calls", service_handler)
|
2019-03-02 07:09:31 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.services.call("test_domain", "REGISTER_CALLS", blocking=True)
|
2019-03-02 07:09:31 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
|
|
|
# Non-blocking service call never throw exception
|
2019-07-31 19:25:30 +00:00
|
|
|
self.services.call("test_domain", "REGISTER_CALLS", blocking=False)
|
2019-03-02 07:09:31 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
|
|
|
def test_callback_service_raise_exception(self):
|
|
|
|
"""Test registering and calling an callback service raise exception."""
|
2019-07-31 19:25:30 +00:00
|
|
|
|
2019-03-02 07:09:31 +00:00
|
|
|
@ha.callback
|
|
|
|
def service_handler(_):
|
|
|
|
"""Service handler coroutine."""
|
|
|
|
raise ValueError
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
self.services.register("test_domain", "register_calls", service_handler)
|
2019-03-02 07:09:31 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
2019-07-31 19:25:30 +00:00
|
|
|
assert self.services.call("test_domain", "REGISTER_CALLS", blocking=True)
|
2019-03-02 07:09:31 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
|
|
|
# Non-blocking service call never throw exception
|
2019-07-31 19:25:30 +00:00
|
|
|
self.services.call("test_domain", "REGISTER_CALLS", blocking=False)
|
2019-03-02 07:09:31 +00:00
|
|
|
self.hass.block_till_done()
|
|
|
|
|
2015-08-04 16:16:10 +00:00
|
|
|
|
|
|
|
class TestConfig(unittest.TestCase):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test configuration methods."""
|
|
|
|
|
2016-10-30 21:18:53 +00:00
|
|
|
# pylint: disable=invalid-name
|
|
|
|
def setUp(self):
|
2018-08-19 20:29:08 +00:00
|
|
|
"""Set up things to be run when tests are started."""
|
2019-05-20 18:02:36 +00:00
|
|
|
self.config = ha.Config(None)
|
2018-10-24 10:10:05 +00:00
|
|
|
assert self.config.config_dir is None
|
2015-08-04 16:16:10 +00:00
|
|
|
|
|
|
|
def test_path_with_file(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test get_config_path method."""
|
2020-01-20 16:44:55 +00:00
|
|
|
self.config.config_dir = "/test/ha-config"
|
2020-04-06 10:51:48 +00:00
|
|
|
assert self.config.path("test.conf") == "/test/ha-config/test.conf"
|
2015-08-04 16:16:10 +00:00
|
|
|
|
|
|
|
def test_path_with_dir_and_file(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test get_config_path method."""
|
2020-01-20 16:44:55 +00:00
|
|
|
self.config.config_dir = "/test/ha-config"
|
2020-04-06 10:51:48 +00:00
|
|
|
assert self.config.path("dir", "test.conf") == "/test/ha-config/dir/test.conf"
|
2015-08-04 16:16:10 +00:00
|
|
|
|
|
|
|
def test_as_dict(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test as dict."""
|
2020-01-20 16:44:55 +00:00
|
|
|
self.config.config_dir = "/test/ha-config"
|
2020-06-02 18:54:11 +00:00
|
|
|
self.config.hass = MagicMock()
|
|
|
|
type(self.config.hass.state).value = PropertyMock(return_value="RUNNING")
|
2015-08-04 16:16:10 +00:00
|
|
|
expected = {
|
2019-07-31 19:25:30 +00:00
|
|
|
"latitude": 0,
|
|
|
|
"longitude": 0,
|
|
|
|
"elevation": 0,
|
2016-07-31 20:24:49 +00:00
|
|
|
CONF_UNIT_SYSTEM: METRIC_SYSTEM.as_dict(),
|
2019-07-31 19:25:30 +00:00
|
|
|
"location_name": "Home",
|
|
|
|
"time_zone": "UTC",
|
|
|
|
"components": set(),
|
2020-01-20 16:44:55 +00:00
|
|
|
"config_dir": "/test/ha-config",
|
2019-07-31 19:25:30 +00:00
|
|
|
"whitelist_external_dirs": set(),
|
2020-07-13 15:43:11 +00:00
|
|
|
"allowlist_external_dirs": set(),
|
2020-06-25 00:37:01 +00:00
|
|
|
"allowlist_external_urls": set(),
|
2019-07-31 19:25:30 +00:00
|
|
|
"version": __version__,
|
|
|
|
"config_source": "default",
|
2020-02-18 19:52:38 +00:00
|
|
|
"safe_mode": False,
|
2020-06-02 18:54:11 +00:00
|
|
|
"state": "RUNNING",
|
2020-05-08 00:29:47 +00:00
|
|
|
"external_url": None,
|
|
|
|
"internal_url": None,
|
2015-08-04 16:16:10 +00:00
|
|
|
}
|
|
|
|
|
2018-10-24 10:10:05 +00:00
|
|
|
assert expected == self.config.as_dict()
|
2015-08-04 16:16:10 +00:00
|
|
|
|
2017-06-25 22:10:30 +00:00
|
|
|
def test_is_allowed_path(self):
|
|
|
|
"""Test is_allowed_path method."""
|
|
|
|
with TemporaryDirectory() as tmp_dir:
|
2018-01-10 09:48:17 +00:00
|
|
|
# The created dir is in /tmp. This is a symlink on OS X
|
|
|
|
# causing this test to fail unless we resolve path first.
|
2020-07-13 15:43:11 +00:00
|
|
|
self.config.allowlist_external_dirs = {os.path.realpath(tmp_dir)}
|
2017-06-25 22:10:30 +00:00
|
|
|
|
|
|
|
test_file = os.path.join(tmp_dir, "test.jpg")
|
|
|
|
with open(test_file, "w") as tmp_file:
|
|
|
|
tmp_file.write("test")
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
valid = [test_file, tmp_dir, os.path.join(tmp_dir, "notfound321")]
|
2017-06-25 22:10:30 +00:00
|
|
|
for path in valid:
|
|
|
|
assert self.config.is_allowed_path(path)
|
|
|
|
|
2020-07-13 15:43:11 +00:00
|
|
|
self.config.allowlist_external_dirs = {"/home", "/var"}
|
2017-06-25 22:10:30 +00:00
|
|
|
|
2020-08-08 12:41:02 +00:00
|
|
|
invalid = [
|
2017-06-25 22:10:30 +00:00
|
|
|
"/hass/config/secure",
|
|
|
|
"/etc/passwd",
|
|
|
|
"/root/secure_file",
|
2017-07-03 05:24:08 +00:00
|
|
|
"/var/../etc/passwd",
|
2017-06-25 22:10:30 +00:00
|
|
|
test_file,
|
|
|
|
]
|
2020-08-08 12:41:02 +00:00
|
|
|
for path in invalid:
|
2017-06-25 22:10:30 +00:00
|
|
|
assert not self.config.is_allowed_path(path)
|
|
|
|
|
2018-10-24 10:10:05 +00:00
|
|
|
with pytest.raises(AssertionError):
|
2017-08-15 13:41:37 +00:00
|
|
|
self.config.is_allowed_path(None)
|
|
|
|
|
2020-06-25 00:37:01 +00:00
|
|
|
def test_is_allowed_external_url(self):
|
|
|
|
"""Test is_allowed_external_url method."""
|
|
|
|
self.config.allowlist_external_urls = [
|
|
|
|
"http://x.com/",
|
|
|
|
"https://y.com/bla/",
|
|
|
|
"https://z.com/images/1.jpg/",
|
|
|
|
]
|
|
|
|
|
|
|
|
valid = [
|
|
|
|
"http://x.com/1.jpg",
|
|
|
|
"http://x.com",
|
|
|
|
"https://y.com/bla/",
|
|
|
|
"https://y.com/bla/2.png",
|
|
|
|
"https://z.com/images/1.jpg",
|
|
|
|
]
|
|
|
|
for url in valid:
|
|
|
|
assert self.config.is_allowed_external_url(url)
|
|
|
|
|
|
|
|
invalid = [
|
|
|
|
"https://a.co",
|
|
|
|
"https://y.com/bla_wrong",
|
|
|
|
"https://y.com/bla/../image.jpg",
|
|
|
|
"https://z.com/images",
|
|
|
|
]
|
|
|
|
for url in invalid:
|
|
|
|
assert not self.config.is_allowed_external_url(url)
|
|
|
|
|
2015-08-04 16:16:10 +00:00
|
|
|
|
2020-05-08 00:29:47 +00:00
|
|
|
async def test_event_on_update(hass):
|
2019-05-20 18:02:36 +00:00
|
|
|
"""Test that event is fired on update."""
|
|
|
|
events = []
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def callback(event):
|
|
|
|
events.append(event)
|
|
|
|
|
|
|
|
hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, callback)
|
|
|
|
|
|
|
|
assert hass.config.latitude != 12
|
|
|
|
|
2019-06-01 06:03:45 +00:00
|
|
|
await hass.config.async_update(latitude=12)
|
2019-05-20 18:02:36 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert hass.config.latitude == 12
|
|
|
|
assert len(events) == 1
|
2019-07-31 19:25:30 +00:00
|
|
|
assert events[0].data == {"latitude": 12}
|
2019-05-20 18:02:36 +00:00
|
|
|
|
|
|
|
|
2019-06-01 06:03:45 +00:00
|
|
|
async def test_bad_timezone_raises_value_error(hass):
|
2019-05-20 18:02:36 +00:00
|
|
|
"""Test bad timezone raises ValueError."""
|
|
|
|
with pytest.raises(ValueError):
|
2019-07-31 19:25:30 +00:00
|
|
|
await hass.config.async_update(time_zone="not_a_timezone")
|
2019-05-20 18:02:36 +00:00
|
|
|
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
@patch("homeassistant.core.monotonic")
|
2017-02-10 17:00:17 +00:00
|
|
|
def test_create_timer(mock_monotonic, loop):
|
2016-09-20 06:39:49 +00:00
|
|
|
"""Test create timer."""
|
2017-02-10 17:00:17 +00:00
|
|
|
hass = MagicMock()
|
|
|
|
funcs = []
|
|
|
|
orig_callback = ha.callback
|
|
|
|
|
|
|
|
def mock_callback(func):
|
|
|
|
funcs.append(func)
|
|
|
|
return orig_callback(func)
|
|
|
|
|
2018-09-17 08:10:50 +00:00
|
|
|
mock_monotonic.side_effect = 10.2, 10.8, 11.3
|
2017-04-07 04:00:58 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
with patch.object(ha, "callback", mock_callback), patch(
|
|
|
|
"homeassistant.core.dt_util.utcnow",
|
|
|
|
return_value=datetime(2018, 12, 31, 3, 4, 5, 333333),
|
|
|
|
):
|
2017-02-10 17:00:17 +00:00
|
|
|
ha._async_create_timer(hass)
|
|
|
|
|
2018-09-17 08:10:50 +00:00
|
|
|
assert len(funcs) == 2
|
|
|
|
fire_time_event, stop_timer = funcs
|
|
|
|
|
2018-09-14 10:28:09 +00:00
|
|
|
assert len(hass.loop.call_later.mock_calls) == 1
|
2018-09-17 08:10:50 +00:00
|
|
|
delay, callback, target = hass.loop.call_later.mock_calls[0][1]
|
|
|
|
assert abs(delay - 0.666667) < 0.001
|
|
|
|
assert callback is fire_time_event
|
|
|
|
assert abs(target - 10.866667) < 0.001
|
2018-09-14 10:28:09 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
with patch(
|
|
|
|
"homeassistant.core.dt_util.utcnow",
|
|
|
|
return_value=datetime(2018, 12, 31, 3, 4, 6, 100000),
|
|
|
|
):
|
2018-09-17 08:10:50 +00:00
|
|
|
callback(target)
|
2017-02-10 17:00:17 +00:00
|
|
|
|
|
|
|
assert len(hass.bus.async_listen_once.mock_calls) == 1
|
|
|
|
assert len(hass.bus.async_fire.mock_calls) == 1
|
2018-09-14 10:28:09 +00:00
|
|
|
assert len(hass.loop.call_later.mock_calls) == 2
|
2017-02-10 17:00:17 +00:00
|
|
|
|
2017-04-07 04:00:58 +00:00
|
|
|
event_type, callback = hass.bus.async_listen_once.mock_calls[0][1]
|
2017-02-10 17:00:17 +00:00
|
|
|
assert event_type == EVENT_HOMEASSISTANT_STOP
|
|
|
|
assert callback is stop_timer
|
|
|
|
|
2018-09-17 08:10:50 +00:00
|
|
|
delay, callback, target = hass.loop.call_later.mock_calls[1][1]
|
|
|
|
assert abs(delay - 0.9) < 0.001
|
2017-02-10 17:00:17 +00:00
|
|
|
assert callback is fire_time_event
|
2018-09-17 08:10:50 +00:00
|
|
|
assert abs(target - 12.2) < 0.001
|
2017-02-10 17:00:17 +00:00
|
|
|
|
|
|
|
event_type, event_data = hass.bus.async_fire.mock_calls[0][1]
|
|
|
|
assert event_type == EVENT_TIME_CHANGED
|
2018-09-17 08:10:50 +00:00
|
|
|
assert event_data[ATTR_NOW] == datetime(2018, 12, 31, 3, 4, 6, 100000)
|
2017-02-10 17:00:17 +00:00
|
|
|
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
@patch("homeassistant.core.monotonic")
|
2017-02-10 17:00:17 +00:00
|
|
|
def test_timer_out_of_sync(mock_monotonic, loop):
|
|
|
|
"""Test create timer."""
|
|
|
|
hass = MagicMock()
|
|
|
|
funcs = []
|
|
|
|
orig_callback = ha.callback
|
2016-09-20 06:39:49 +00:00
|
|
|
|
2017-02-10 17:00:17 +00:00
|
|
|
def mock_callback(func):
|
|
|
|
funcs.append(func)
|
|
|
|
return orig_callback(func)
|
2016-09-20 06:39:49 +00:00
|
|
|
|
2018-09-17 08:10:50 +00:00
|
|
|
mock_monotonic.side_effect = 10.2, 13.3, 13.4
|
2016-09-20 06:39:49 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
with patch.object(ha, "callback", mock_callback), patch(
|
|
|
|
"homeassistant.core.dt_util.utcnow",
|
|
|
|
return_value=datetime(2018, 12, 31, 3, 4, 5, 333333),
|
|
|
|
):
|
2017-04-07 04:00:58 +00:00
|
|
|
ha._async_create_timer(hass)
|
|
|
|
|
2018-09-17 08:10:50 +00:00
|
|
|
delay, callback, target = hass.loop.call_later.mock_calls[0][1]
|
2018-09-14 10:28:09 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
with patch(
|
|
|
|
"homeassistant.core.dt_util.utcnow",
|
|
|
|
return_value=datetime(2018, 12, 31, 3, 4, 8, 200000),
|
|
|
|
):
|
2018-09-17 08:10:50 +00:00
|
|
|
callback(target)
|
|
|
|
|
2020-07-23 04:52:10 +00:00
|
|
|
_, event_0_args, event_0_kwargs = hass.bus.async_fire.mock_calls[0]
|
|
|
|
event_context_0 = event_0_kwargs["context"]
|
|
|
|
|
|
|
|
event_type_0, _ = event_0_args
|
|
|
|
assert event_type_0 == EVENT_TIME_CHANGED
|
|
|
|
|
|
|
|
_, event_1_args, event_1_kwargs = hass.bus.async_fire.mock_calls[1]
|
|
|
|
event_type_1, event_data_1 = event_1_args
|
|
|
|
event_context_1 = event_1_kwargs["context"]
|
|
|
|
|
|
|
|
assert event_type_1 == EVENT_TIMER_OUT_OF_SYNC
|
|
|
|
assert abs(event_data_1[ATTR_SECONDS] - 2.433333) < 0.001
|
|
|
|
|
|
|
|
assert event_context_0 == event_context_1
|
2018-09-14 10:28:09 +00:00
|
|
|
|
2017-04-07 04:00:58 +00:00
|
|
|
assert len(funcs) == 2
|
2020-04-06 10:51:48 +00:00
|
|
|
fire_time_event, _ = funcs
|
2016-09-20 06:39:49 +00:00
|
|
|
|
2018-09-14 10:28:09 +00:00
|
|
|
assert len(hass.loop.call_later.mock_calls) == 2
|
2016-09-20 06:39:49 +00:00
|
|
|
|
2018-09-17 08:10:50 +00:00
|
|
|
delay, callback, target = hass.loop.call_later.mock_calls[1][1]
|
|
|
|
assert abs(delay - 0.8) < 0.001
|
2017-02-10 17:00:17 +00:00
|
|
|
assert callback is fire_time_event
|
2018-09-17 08:10:50 +00:00
|
|
|
assert abs(target - 14.2) < 0.001
|
2017-04-08 21:53:32 +00:00
|
|
|
|
|
|
|
|
2020-07-06 22:58:53 +00:00
|
|
|
async def test_hass_start_starts_the_timer(loop):
|
2017-04-08 21:53:32 +00:00
|
|
|
"""Test when hass starts, it starts the timer."""
|
2020-07-06 22:58:53 +00:00
|
|
|
hass = ha.HomeAssistant()
|
2017-04-08 21:53:32 +00:00
|
|
|
|
|
|
|
try:
|
2019-07-31 19:25:30 +00:00
|
|
|
with patch("homeassistant.core._async_create_timer") as mock_timer:
|
2020-07-06 22:58:53 +00:00
|
|
|
await hass.async_start()
|
2017-04-08 21:53:32 +00:00
|
|
|
|
|
|
|
assert hass.state == ha.CoreState.running
|
|
|
|
assert not hass._track_task
|
|
|
|
assert len(mock_timer.mock_calls) == 1
|
|
|
|
assert mock_timer.mock_calls[0][1][0] is hass
|
|
|
|
|
|
|
|
finally:
|
2020-07-06 22:58:53 +00:00
|
|
|
await hass.async_stop()
|
2020-07-09 14:15:14 +00:00
|
|
|
assert hass.state == ha.CoreState.stopped
|
2017-04-08 21:53:32 +00:00
|
|
|
|
|
|
|
|
2020-07-06 22:58:53 +00:00
|
|
|
async def test_start_taking_too_long(loop, caplog):
|
2017-04-08 21:53:32 +00:00
|
|
|
"""Test when async_start takes too long."""
|
2020-07-06 22:58:53 +00:00
|
|
|
hass = ha.HomeAssistant()
|
2017-04-08 21:53:32 +00:00
|
|
|
caplog.set_level(logging.WARNING)
|
|
|
|
|
|
|
|
try:
|
2020-08-05 12:58:19 +00:00
|
|
|
with patch.object(
|
|
|
|
hass, "async_block_till_done", side_effect=asyncio.TimeoutError
|
2019-07-31 19:25:30 +00:00
|
|
|
), patch("homeassistant.core._async_create_timer") as mock_timer:
|
2020-07-06 22:58:53 +00:00
|
|
|
await hass.async_start()
|
2017-04-08 21:53:32 +00:00
|
|
|
|
|
|
|
assert hass.state == ha.CoreState.running
|
|
|
|
assert len(mock_timer.mock_calls) == 1
|
|
|
|
assert mock_timer.mock_calls[0][1][0] is hass
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "Something is blocking Home Assistant" in caplog.text
|
2017-04-08 21:53:32 +00:00
|
|
|
|
|
|
|
finally:
|
2020-07-06 22:58:53 +00:00
|
|
|
await hass.async_stop()
|
2020-07-09 14:15:14 +00:00
|
|
|
assert hass.state == ha.CoreState.stopped
|
2017-04-11 16:09:31 +00:00
|
|
|
|
|
|
|
|
2020-07-06 22:58:53 +00:00
|
|
|
async def test_track_task_functions(loop):
|
2017-04-11 16:09:31 +00:00
|
|
|
"""Test function to start/stop track task and initial state."""
|
2020-07-06 22:58:53 +00:00
|
|
|
hass = ha.HomeAssistant()
|
2017-04-11 16:09:31 +00:00
|
|
|
try:
|
|
|
|
assert hass._track_task
|
|
|
|
|
|
|
|
hass.async_stop_track_tasks()
|
|
|
|
assert not hass._track_task
|
|
|
|
|
|
|
|
hass.async_track_tasks()
|
|
|
|
assert hass._track_task
|
|
|
|
finally:
|
2020-07-06 22:58:53 +00:00
|
|
|
await hass.async_stop()
|
2018-09-11 19:40:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_service_executed_with_subservices(hass):
|
|
|
|
"""Test we block correctly till all services done."""
|
2019-07-31 19:25:30 +00:00
|
|
|
calls = async_mock_service(hass, "test", "inner")
|
2019-02-18 21:07:44 +00:00
|
|
|
context = ha.Context()
|
2018-09-11 19:40:35 +00:00
|
|
|
|
|
|
|
async def handle_outer(call):
|
|
|
|
"""Handle outer service call."""
|
|
|
|
calls.append(call)
|
2019-07-31 19:25:30 +00:00
|
|
|
call1 = hass.services.async_call(
|
|
|
|
"test", "inner", blocking=True, context=call.context
|
|
|
|
)
|
|
|
|
call2 = hass.services.async_call(
|
|
|
|
"test", "inner", blocking=True, context=call.context
|
|
|
|
)
|
2018-09-11 19:40:35 +00:00
|
|
|
await asyncio.wait([call1, call2])
|
|
|
|
calls.append(call)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.services.async_register("test", "outer", handle_outer)
|
2018-09-11 19:40:35 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
await hass.services.async_call("test", "outer", blocking=True, context=context)
|
2018-09-11 19:40:35 +00:00
|
|
|
|
|
|
|
assert len(calls) == 4
|
2019-07-31 19:25:30 +00:00
|
|
|
assert [call.service for call in calls] == ["outer", "inner", "inner", "outer"]
|
2019-02-18 21:07:44 +00:00
|
|
|
assert all(call.context is context for call in calls)
|
2018-12-10 11:58:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_service_call_event_contains_original_data(hass):
|
|
|
|
"""Test that service call event contains original data."""
|
|
|
|
events = []
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def callback(event):
|
|
|
|
events.append(event)
|
|
|
|
|
|
|
|
hass.bus.async_listen(EVENT_CALL_SERVICE, callback)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
calls = async_mock_service(
|
|
|
|
hass, "test", "service", vol.Schema({"number": vol.Coerce(int)})
|
|
|
|
)
|
2018-12-10 11:58:51 +00:00
|
|
|
|
2019-02-18 21:07:44 +00:00
|
|
|
context = ha.Context()
|
2019-07-31 19:25:30 +00:00
|
|
|
await hass.services.async_call(
|
|
|
|
"test", "service", {"number": "23"}, blocking=True, context=context
|
|
|
|
)
|
2018-12-10 11:58:51 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(events) == 1
|
2019-07-31 19:25:30 +00:00
|
|
|
assert events[0].data["service_data"]["number"] == "23"
|
2019-02-18 21:07:44 +00:00
|
|
|
assert events[0].context is context
|
2018-12-10 11:58:51 +00:00
|
|
|
assert len(calls) == 1
|
2019-07-31 19:25:30 +00:00
|
|
|
assert calls[0].data["number"] == 23
|
2019-02-18 21:07:44 +00:00
|
|
|
assert calls[0].context is context
|
2019-03-01 18:08:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_context():
|
|
|
|
"""Test context init."""
|
|
|
|
c = ha.Context()
|
|
|
|
assert c.user_id is None
|
|
|
|
assert c.parent_id is None
|
|
|
|
assert c.id is not None
|
|
|
|
|
|
|
|
c = ha.Context(23, 100)
|
|
|
|
assert c.user_id == 23
|
|
|
|
assert c.parent_id == 100
|
|
|
|
assert c.id is not None
|
2020-02-10 03:47:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_async_functions_with_callback(hass):
|
|
|
|
"""Test we deal with async functions accidentally marked as callback."""
|
|
|
|
runs = []
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
async def test():
|
|
|
|
runs.append(True)
|
|
|
|
|
|
|
|
await hass.async_add_job(test)
|
|
|
|
assert len(runs) == 1
|
|
|
|
|
|
|
|
hass.async_run_job(test)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(runs) == 2
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
async def service_handler(call):
|
|
|
|
runs.append(True)
|
|
|
|
|
|
|
|
hass.services.async_register("test_domain", "test_service", service_handler)
|
|
|
|
|
|
|
|
await hass.services.async_call("test_domain", "test_service", blocking=True)
|
|
|
|
assert len(runs) == 3
|
2020-02-24 16:35:02 +00:00
|
|
|
|
|
|
|
|
2020-04-04 22:36:33 +00:00
|
|
|
@pytest.mark.parametrize("cancel_call", [True, False])
|
|
|
|
async def test_cancel_service_task(hass, cancel_call):
|
|
|
|
"""Test cancellation."""
|
|
|
|
service_called = asyncio.Event()
|
|
|
|
service_cancelled = False
|
|
|
|
|
|
|
|
async def service_handler(call):
|
|
|
|
nonlocal service_cancelled
|
|
|
|
service_called.set()
|
|
|
|
try:
|
|
|
|
await asyncio.sleep(10)
|
|
|
|
except asyncio.CancelledError:
|
|
|
|
service_cancelled = True
|
|
|
|
raise
|
|
|
|
|
|
|
|
hass.services.async_register("test_domain", "test_service", service_handler)
|
|
|
|
call_task = hass.async_create_task(
|
|
|
|
hass.services.async_call("test_domain", "test_service", blocking=True)
|
|
|
|
)
|
|
|
|
|
|
|
|
tasks_1 = asyncio.all_tasks()
|
|
|
|
await asyncio.wait_for(service_called.wait(), timeout=1)
|
|
|
|
tasks_2 = asyncio.all_tasks() - tasks_1
|
|
|
|
assert len(tasks_2) == 1
|
|
|
|
service_task = tasks_2.pop()
|
|
|
|
|
|
|
|
if cancel_call:
|
|
|
|
call_task.cancel()
|
|
|
|
else:
|
|
|
|
service_task.cancel()
|
|
|
|
with pytest.raises(asyncio.CancelledError):
|
|
|
|
await call_task
|
|
|
|
|
|
|
|
assert service_cancelled
|
|
|
|
|
|
|
|
|
2020-02-24 16:35:02 +00:00
|
|
|
def test_valid_entity_id():
|
|
|
|
"""Test valid entity ID."""
|
|
|
|
for invalid in [
|
|
|
|
"_light.kitchen",
|
|
|
|
".kitchen",
|
|
|
|
".light.kitchen",
|
|
|
|
"light_.kitchen",
|
|
|
|
"light._kitchen",
|
|
|
|
"light.",
|
|
|
|
"light.kitchen__ceiling",
|
|
|
|
"light.kitchen_yo_",
|
|
|
|
"light.kitchen.",
|
|
|
|
"Light.kitchen",
|
|
|
|
"light.Kitchen",
|
|
|
|
"lightkitchen",
|
|
|
|
]:
|
|
|
|
assert not ha.valid_entity_id(invalid), invalid
|
|
|
|
|
|
|
|
for valid in [
|
|
|
|
"1.a",
|
|
|
|
"1light.kitchen",
|
|
|
|
"a.1",
|
|
|
|
"a.a",
|
|
|
|
"input_boolean.hello_world_0123",
|
|
|
|
"light.1kitchen",
|
|
|
|
"light.kitchen",
|
|
|
|
"light.something_yoo",
|
|
|
|
]:
|
|
|
|
assert ha.valid_entity_id(valid), valid
|
2020-05-08 00:29:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_migration_base_url(hass, hass_storage):
|
|
|
|
"""Test that we migrate base url to internal/external url."""
|
|
|
|
config = ha.Config(hass)
|
|
|
|
stored = {"version": 1, "data": {}}
|
|
|
|
hass_storage[ha.CORE_STORAGE_KEY] = stored
|
|
|
|
with patch.object(hass.bus, "async_listen_once") as mock_listen:
|
|
|
|
# Empty config
|
|
|
|
await config.async_load()
|
2020-05-16 11:31:15 +00:00
|
|
|
assert len(mock_listen.mock_calls) == 0
|
2020-05-08 00:29:47 +00:00
|
|
|
|
|
|
|
# With just a name
|
|
|
|
stored["data"] = {"location_name": "Test Name"}
|
|
|
|
await config.async_load()
|
2020-05-16 11:31:15 +00:00
|
|
|
assert len(mock_listen.mock_calls) == 1
|
2020-05-08 00:29:47 +00:00
|
|
|
|
|
|
|
# With external url
|
|
|
|
stored["data"]["external_url"] = "https://example.com"
|
|
|
|
await config.async_load()
|
2020-05-16 11:31:15 +00:00
|
|
|
assert len(mock_listen.mock_calls) == 1
|
2020-05-08 00:29:47 +00:00
|
|
|
|
|
|
|
# Test that the event listener works
|
|
|
|
assert mock_listen.mock_calls[0][1][0] == EVENT_HOMEASSISTANT_START
|
|
|
|
|
|
|
|
# External
|
2020-05-08 15:52:32 +00:00
|
|
|
hass.config.api = Mock(deprecated_base_url="https://loaded-example.com")
|
2020-05-08 00:29:47 +00:00
|
|
|
await mock_listen.mock_calls[0][1][1](None)
|
|
|
|
assert config.external_url == "https://loaded-example.com"
|
|
|
|
|
|
|
|
# Internal
|
|
|
|
for internal in ("http://hass.local", "http://192.168.1.100:8123"):
|
2020-05-08 15:52:32 +00:00
|
|
|
hass.config.api = Mock(deprecated_base_url=internal)
|
2020-05-08 00:29:47 +00:00
|
|
|
await mock_listen.mock_calls[0][1][1](None)
|
|
|
|
assert config.internal_url == internal
|
2020-05-16 11:31:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_additional_data_in_core_config(hass, hass_storage):
|
|
|
|
"""Test that we can handle additional data in core configuration."""
|
|
|
|
config = ha.Config(hass)
|
|
|
|
hass_storage[ha.CORE_STORAGE_KEY] = {
|
|
|
|
"version": 1,
|
|
|
|
"data": {"location_name": "Test Name", "additional_valid_key": "value"},
|
|
|
|
}
|
|
|
|
await config.async_load()
|
|
|
|
assert config.location_name == "Test Name"
|
2020-06-15 22:22:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_start_events(hass):
|
|
|
|
"""Test events fired when starting Home Assistant."""
|
|
|
|
hass.state = ha.CoreState.not_running
|
|
|
|
|
|
|
|
all_events = []
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def capture_events(ev):
|
|
|
|
all_events.append(ev.event_type)
|
|
|
|
|
|
|
|
hass.bus.async_listen(MATCH_ALL, capture_events)
|
|
|
|
|
|
|
|
core_states = []
|
|
|
|
|
|
|
|
@ha.callback
|
|
|
|
def capture_core_state(_):
|
|
|
|
core_states.append(hass.state)
|
|
|
|
|
|
|
|
hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, capture_core_state)
|
|
|
|
|
|
|
|
await hass.async_start()
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert all_events == [
|
|
|
|
EVENT_CORE_CONFIG_UPDATE,
|
|
|
|
EVENT_HOMEASSISTANT_START,
|
|
|
|
EVENT_CORE_CONFIG_UPDATE,
|
|
|
|
EVENT_HOMEASSISTANT_STARTED,
|
|
|
|
]
|
|
|
|
assert core_states == [ha.CoreState.starting, ha.CoreState.running]
|
2020-07-24 02:03:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_log_blocking_events(hass, caplog):
|
|
|
|
"""Ensure we log which task is blocking startup when debug logging is on."""
|
|
|
|
caplog.set_level(logging.DEBUG)
|
|
|
|
|
|
|
|
async def _wait_a_bit_1():
|
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
|
|
|
async def _wait_a_bit_2():
|
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
|
|
|
hass.async_create_task(_wait_a_bit_1())
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2020-07-28 16:24:29 +00:00
|
|
|
with patch.object(ha, "BLOCK_LOG_TIMEOUT", 0.0001):
|
2020-07-24 02:03:42 +00:00
|
|
|
hass.async_create_task(_wait_a_bit_2())
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert "_wait_a_bit_2" in caplog.text
|
|
|
|
assert "_wait_a_bit_1" not in caplog.text
|
2020-07-28 16:24:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_chained_logging_hits_log_timeout(hass, caplog):
|
|
|
|
"""Ensure we log which task is blocking startup when there is a task chain and debug logging is on."""
|
|
|
|
caplog.set_level(logging.DEBUG)
|
|
|
|
|
|
|
|
created = 0
|
|
|
|
|
|
|
|
async def _task_chain_1():
|
|
|
|
nonlocal created
|
|
|
|
created += 1
|
2020-08-03 13:01:15 +00:00
|
|
|
if created > 1000:
|
2020-07-28 16:24:29 +00:00
|
|
|
return
|
|
|
|
hass.async_create_task(_task_chain_2())
|
|
|
|
|
|
|
|
async def _task_chain_2():
|
|
|
|
nonlocal created
|
|
|
|
created += 1
|
2020-08-03 13:01:15 +00:00
|
|
|
if created > 1000:
|
2020-07-28 16:24:29 +00:00
|
|
|
return
|
|
|
|
hass.async_create_task(_task_chain_1())
|
|
|
|
|
|
|
|
with patch.object(ha, "BLOCK_LOG_TIMEOUT", 0.0001):
|
|
|
|
hass.async_create_task(_task_chain_1())
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert "_task_chain_" in caplog.text
|
|
|
|
|
|
|
|
|
|
|
|
async def test_chained_logging_misses_log_timeout(hass, caplog):
|
|
|
|
"""Ensure we do not log which task is blocking startup if we do not hit the timeout."""
|
|
|
|
caplog.set_level(logging.DEBUG)
|
|
|
|
|
|
|
|
created = 0
|
|
|
|
|
|
|
|
async def _task_chain_1():
|
|
|
|
nonlocal created
|
|
|
|
created += 1
|
|
|
|
if created > 10:
|
|
|
|
return
|
|
|
|
hass.async_create_task(_task_chain_2())
|
|
|
|
|
|
|
|
async def _task_chain_2():
|
|
|
|
nonlocal created
|
|
|
|
created += 1
|
|
|
|
if created > 10:
|
|
|
|
return
|
|
|
|
hass.async_create_task(_task_chain_1())
|
|
|
|
|
|
|
|
hass.async_create_task(_task_chain_1())
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert "_task_chain_" not in caplog.text
|
2020-09-06 21:20:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_async_all(hass):
|
|
|
|
"""Test async_all."""
|
|
|
|
|
|
|
|
hass.states.async_set("switch.link", "on")
|
|
|
|
hass.states.async_set("light.bowl", "on")
|
|
|
|
hass.states.async_set("light.frog", "on")
|
|
|
|
hass.states.async_set("vacuum.floor", "on")
|
|
|
|
|
|
|
|
assert {state.entity_id for state in hass.states.async_all()} == {
|
|
|
|
"switch.link",
|
|
|
|
"light.bowl",
|
|
|
|
"light.frog",
|
|
|
|
"vacuum.floor",
|
|
|
|
}
|
|
|
|
assert {state.entity_id for state in hass.states.async_all("light")} == {
|
|
|
|
"light.bowl",
|
|
|
|
"light.frog",
|
|
|
|
}
|
|
|
|
assert {
|
|
|
|
state.entity_id for state in hass.states.async_all(["light", "switch"])
|
|
|
|
} == {"light.bowl", "light.frog", "switch.link"}
|
2020-09-26 16:36:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_async_entity_ids_count(hass):
|
|
|
|
"""Test async_entity_ids_count."""
|
|
|
|
|
|
|
|
hass.states.async_set("switch.link", "on")
|
|
|
|
hass.states.async_set("light.bowl", "on")
|
|
|
|
hass.states.async_set("light.frog", "on")
|
|
|
|
hass.states.async_set("vacuum.floor", "on")
|
|
|
|
|
|
|
|
assert hass.states.async_entity_ids_count() == 4
|
|
|
|
assert hass.states.async_entity_ids_count("light") == 2
|
|
|
|
|
|
|
|
hass.states.async_set("light.cow", "on")
|
|
|
|
|
|
|
|
assert hass.states.async_entity_ids_count() == 5
|
|
|
|
assert hass.states.async_entity_ids_count("light") == 3
|