core/tests/components/script/test_init.py

470 lines
14 KiB
Python
Raw Normal View History

2016-03-09 09:25:50 +00:00
"""The tests for the Script component."""
# pylint: disable=protected-access
2015-10-15 06:09:52 +00:00
import unittest
from unittest.mock import Mock, patch
2015-10-15 06:09:52 +00:00
import pytest
2015-10-15 06:09:52 +00:00
from homeassistant.components import script
from homeassistant.components.script import DOMAIN
from homeassistant.const import (
2019-07-31 19:25:30 +00:00
ATTR_ENTITY_ID,
ATTR_NAME,
EVENT_SCRIPT_STARTED,
2019-07-31 19:25:30 +00:00
SERVICE_RELOAD,
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
from homeassistant.core import Context, callback, split_entity_id
from homeassistant.exceptions import ServiceNotFound
from homeassistant.helpers.service import async_get_all_descriptions
from homeassistant.loader import bind_hass
from homeassistant.setup import async_setup_component, setup_component
2015-10-15 06:09:52 +00:00
from tests.common import get_test_home_assistant
2015-10-15 06:09:52 +00:00
2019-07-31 19:25:30 +00:00
ENTITY_ID = "script.test"
2015-10-15 06:09:52 +00:00
@bind_hass
def turn_on(hass, entity_id, variables=None, context=None):
"""Turn script on.
This is a legacy helper method. Do not use it for new tests.
"""
_, object_id = split_entity_id(entity_id)
hass.services.call(DOMAIN, object_id, variables, context=context)
@bind_hass
def turn_off(hass, entity_id):
"""Turn script on.
This is a legacy helper method. Do not use it for new tests.
"""
hass.services.call(DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id})
@bind_hass
def toggle(hass, entity_id):
"""Toggle the script.
This is a legacy helper method. Do not use it for new tests.
"""
hass.services.call(DOMAIN, SERVICE_TOGGLE, {ATTR_ENTITY_ID: entity_id})
@bind_hass
def reload(hass):
"""Reload script component.
This is a legacy helper method. Do not use it for new tests.
"""
hass.services.call(DOMAIN, SERVICE_RELOAD)
class TestScriptComponent(unittest.TestCase):
2016-03-09 09:25:50 +00:00
"""Test the Script component."""
2015-10-15 06:09:52 +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."""
2015-10-15 06:09:52 +00:00
self.hass = get_test_home_assistant()
# pylint: disable=invalid-name
def tearDown(self):
2016-03-09 09:25:50 +00:00
"""Stop down everything that was started."""
2015-10-15 06:09:52 +00:00
self.hass.stop()
def test_setup_with_invalid_configs(self):
"""Test setup with invalid configs."""
for value in (
2019-07-31 19:25:30 +00:00
{"test": {}},
{"test hello world": {"sequence": [{"event": "bla"}]}},
{
2019-07-31 19:25:30 +00:00
"test": {
"sequence": {
"event": "test_event",
"service": "homeassistant.turn_on",
}
}
},
):
2019-07-31 19:25:30 +00:00
assert not setup_component(
self.hass, "script", {"script": value}
), f"Script loaded with wrong config {value}"
2015-10-28 19:24:33 +00:00
2019-07-31 19:25:30 +00:00
assert 0 == len(self.hass.states.entity_ids("script"))
2015-10-15 06:09:52 +00:00
def test_turn_on_service(self):
2016-03-09 09:25:50 +00:00
"""Verify that the turn_on service."""
2019-07-31 19:25:30 +00:00
event = "test_event"
events = []
2016-11-04 04:58:18 +00:00
@callback
def record_event(event):
2016-03-09 09:25:50 +00:00
"""Add recorded event to set."""
events.append(event)
self.hass.bus.listen(event, record_event)
2019-07-31 19:25:30 +00:00
assert setup_component(
self.hass,
"script",
{
"script": {
"test": {"sequence": [{"delay": {"seconds": 5}}, {"event": event}]}
}
2019-07-31 19:25:30 +00:00
},
)
turn_on(self.hass, ENTITY_ID)
self.hass.block_till_done()
assert script.is_on(self.hass, ENTITY_ID)
assert 0 == len(events)
2016-03-09 09:25:50 +00:00
# Calling turn_on a second time should not advance the script
turn_on(self.hass, ENTITY_ID)
self.hass.block_till_done()
assert 0 == len(events)
2016-02-21 17:22:38 +00:00
turn_off(self.hass, ENTITY_ID)
self.hass.block_till_done()
assert not script.is_on(self.hass, ENTITY_ID)
assert 0 == len(events)
2016-02-21 17:22:38 +00:00
def test_toggle_service(self):
2016-03-09 09:25:50 +00:00
"""Test the toggling of a service."""
2019-07-31 19:25:30 +00:00
event = "test_event"
events = []
2016-02-21 17:22:38 +00:00
2016-11-04 04:58:18 +00:00
@callback
2016-02-21 17:22:38 +00:00
def record_event(event):
2016-03-09 09:25:50 +00:00
"""Add recorded event to set."""
events.append(event)
2016-02-21 17:22:38 +00:00
self.hass.bus.listen(event, record_event)
2019-07-31 19:25:30 +00:00
assert setup_component(
self.hass,
"script",
{
"script": {
"test": {"sequence": [{"delay": {"seconds": 5}}, {"event": event}]}
2016-02-21 17:22:38 +00:00
}
2019-07-31 19:25:30 +00:00
},
)
2016-02-21 17:22:38 +00:00
toggle(self.hass, ENTITY_ID)
self.hass.block_till_done()
assert script.is_on(self.hass, ENTITY_ID)
assert 0 == len(events)
2016-02-21 17:22:38 +00:00
toggle(self.hass, ENTITY_ID)
self.hass.block_till_done()
assert not script.is_on(self.hass, ENTITY_ID)
assert 0 == len(events)
def test_passing_variables(self):
"""Test different ways of passing in variables."""
calls = []
context = Context()
@callback
def record_call(service):
"""Add recorded event to set."""
calls.append(service)
2019-07-31 19:25:30 +00:00
self.hass.services.register("test", "script", record_call)
assert setup_component(
self.hass,
"script",
{
"script": {
"test": {
"sequence": {
"service": "test.script",
"data_template": {"hello": "{{ greeting }}"},
}
}
}
},
2019-07-31 19:25:30 +00:00
)
2019-07-31 19:25:30 +00:00
turn_on(self.hass, ENTITY_ID, {"greeting": "world"}, context=context)
self.hass.block_till_done()
assert len(calls) == 1
assert calls[0].context is context
2019-07-31 19:25:30 +00:00
assert calls[0].data["hello"] == "world"
2019-07-31 19:25:30 +00:00
self.hass.services.call(
"script", "test", {"greeting": "universe"}, context=context
)
self.hass.block_till_done()
assert len(calls) == 2
assert calls[1].context is context
2019-07-31 19:25:30 +00:00
assert calls[1].data["hello"] == "universe"
def test_reload_service(self):
"""Verify that the turn_on service."""
2019-07-31 19:25:30 +00:00
assert setup_component(
self.hass,
"script",
{"script": {"test": {"sequence": [{"delay": {"seconds": 5}}]}}},
)
assert self.hass.states.get(ENTITY_ID) is not None
2019-07-31 19:25:30 +00:00
assert self.hass.services.has_service(script.DOMAIN, "test")
with patch(
"homeassistant.config.load_yaml_config_file",
return_value={
"script": {"test2": {"sequence": [{"delay": {"seconds": 5}}]}}
},
):
reload(self.hass)
self.hass.block_till_done()
assert self.hass.states.get(ENTITY_ID) is None
2019-07-31 19:25:30 +00:00
assert not self.hass.services.has_service(script.DOMAIN, "test")
assert self.hass.states.get("script.test2") is not None
2019-07-31 19:25:30 +00:00
assert self.hass.services.has_service(script.DOMAIN, "test2")
async def test_service_descriptions(hass):
"""Test that service descriptions are loaded and reloaded correctly."""
# Test 1: has "description" but no "fields"
assert await async_setup_component(
hass,
"script",
{
"script": {
"test": {
"description": "test description",
"sequence": [{"delay": {"seconds": 5}}],
}
}
},
)
descriptions = await async_get_all_descriptions(hass)
assert descriptions[DOMAIN]["test"]["description"] == "test description"
assert not descriptions[DOMAIN]["test"]["fields"]
# Test 2: has "fields" but no "description"
with patch(
"homeassistant.config.load_yaml_config_file",
return_value={
"script": {
"test": {
"fields": {
"test_param": {
"description": "test_param description",
"example": "test_param example",
}
},
"sequence": [{"delay": {"seconds": 5}}],
}
}
},
):
await hass.services.async_call(DOMAIN, SERVICE_RELOAD, blocking=True)
descriptions = await async_get_all_descriptions(hass)
assert descriptions[script.DOMAIN]["test"]["description"] == ""
assert (
descriptions[script.DOMAIN]["test"]["fields"]["test_param"]["description"]
== "test_param description"
)
assert (
descriptions[script.DOMAIN]["test"]["fields"]["test_param"]["example"]
== "test_param example"
)
async def test_shared_context(hass):
"""Test that the shared context is passed down the chain."""
2019-07-31 19:25:30 +00:00
event = "test_event"
context = Context()
event_mock = Mock()
run_mock = Mock()
hass.bus.async_listen(event, event_mock)
hass.bus.async_listen(EVENT_SCRIPT_STARTED, run_mock)
2019-07-31 19:25:30 +00:00
assert await async_setup_component(
hass, "script", {"script": {"test": {"sequence": [{"event": event}]}}}
)
await hass.services.async_call(
DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID}, context=context
)
await hass.async_block_till_done()
assert event_mock.call_count == 1
assert run_mock.call_count == 1
args, kwargs = run_mock.call_args
assert args[0].context == context
# Ensure event data has all attributes set
2019-07-31 19:25:30 +00:00
assert args[0].data.get(ATTR_NAME) == "test"
assert args[0].data.get(ATTR_ENTITY_ID) == "script.test"
# Ensure context carries through the event
args, kwargs = event_mock.call_args
assert args[0].context == context
# Ensure the script state shares the same context
2019-07-31 19:25:30 +00:00
state = hass.states.get("script.test")
assert state is not None
assert state.context == context
async def test_logging_script_error(hass, caplog):
"""Test logging script error."""
2019-07-31 19:25:30 +00:00
assert await async_setup_component(
hass,
"script",
{"script": {"hello": {"sequence": [{"service": "non.existing"}]}}},
)
with pytest.raises(ServiceNotFound) as err:
2019-07-31 19:25:30 +00:00
await hass.services.async_call("script", "hello", blocking=True)
2019-07-31 19:25:30 +00:00
assert err.value.domain == "non"
assert err.value.service == "existing"
assert "Error executing script" in caplog.text
async def test_turning_no_scripts_off(hass):
"""Test it is possible to turn two scripts off."""
2019-07-31 19:25:30 +00:00
assert await async_setup_component(hass, "script", {})
# Testing it doesn't raise
await hass.services.async_call(
2019-07-31 19:25:30 +00:00
DOMAIN, SERVICE_TURN_OFF, {"entity_id": []}, blocking=True
)
async def test_async_get_descriptions_script(hass):
"""Test async_set_service_schema for the script integration."""
script_config = {
DOMAIN: {
"test1": {"sequence": [{"service": "homeassistant.restart"}]},
"test2": {
"description": "test2",
"fields": {
"param": {
"description": "param_description",
"example": "param_example",
}
},
"sequence": [{"service": "homeassistant.restart"}],
},
}
}
await async_setup_component(hass, DOMAIN, script_config)
descriptions = await hass.helpers.service.async_get_all_descriptions()
assert descriptions[DOMAIN]["test1"]["description"] == ""
assert not descriptions[DOMAIN]["test1"]["fields"]
assert descriptions[DOMAIN]["test2"]["description"] == "test2"
assert (
descriptions[DOMAIN]["test2"]["fields"]["param"]["description"]
== "param_description"
)
assert (
descriptions[DOMAIN]["test2"]["fields"]["param"]["example"] == "param_example"
)
async def test_extraction_functions(hass):
"""Test extraction functions."""
assert await async_setup_component(
hass,
DOMAIN,
{
DOMAIN: {
"test1": {
"sequence": [
{
"service": "test.script",
"data": {"entity_id": "light.in_both"},
},
{
"service": "test.script",
"data": {"entity_id": "light.in_first"},
},
{"domain": "light", "device_id": "device-in-both"},
]
},
"test2": {
"sequence": [
{
"service": "test.script",
"data": {"entity_id": "light.in_both"},
},
{
"condition": "state",
"entity_id": "sensor.condition",
"state": "100",
},
{"scene": "scene.hello"},
{"domain": "light", "device_id": "device-in-both"},
{"domain": "light", "device_id": "device-in-last"},
],
},
}
},
)
assert set(script.scripts_with_entity(hass, "light.in_both")) == {
"script.test1",
"script.test2",
}
assert set(script.entities_in_script(hass, "script.test1")) == {
"light.in_both",
"light.in_first",
}
assert set(script.scripts_with_device(hass, "device-in-both")) == {
"script.test1",
"script.test2",
}
assert set(script.devices_in_script(hass, "script.test2")) == {
"device-in-both",
"device-in-last",
}
2020-02-17 16:44:36 +00:00
async def test_config(hass):
"""Test passing info in config."""
assert await async_setup_component(
hass,
"script",
{
"script": {
"test_script": {
"alias": "Script Name",
"icon": "mdi:party",
"sequence": [],
}
}
},
)
test_script = hass.states.get("script.test_script")
assert test_script.name == "Script Name"
assert test_script.attributes["icon"] == "mdi:party"