More async tests (#4223)

* Annotate test callbacks to be async

* Convert device_sun_light_trigger to be async
This commit is contained in:
Paulus Schoutsen 2016-11-05 16:36:20 -07:00 committed by GitHub
parent 22c3d014aa
commit 62785c2431
21 changed files with 110 additions and 21 deletions

View File

@ -9,6 +9,7 @@ from datetime import timedelta
import voluptuous as vol import voluptuous as vol
from homeassistant.core import callback
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.const import STATE_HOME, STATE_NOT_HOME from homeassistant.const import STATE_HOME, STATE_NOT_HOME
from homeassistant.helpers.event import track_point_in_time from homeassistant.helpers.event import track_point_in_time
@ -79,21 +80,22 @@ def setup(hass, config):
return None return None
return next_setting - LIGHT_TRANSITION_TIME * len(light_ids) return next_setting - LIGHT_TRANSITION_TIME * len(light_ids)
def turn_light_on_before_sunset(light_id): def async_turn_on_before_sunset(light_id):
"""Helper function to turn on lights. """Helper function to turn on lights.
Speed is slow if there are devices home and the light is not on yet. Speed is slow if there are devices home and the light is not on yet.
""" """
if not device_tracker.is_on(hass) or light.is_on(hass, light_id): if not device_tracker.is_on(hass) or light.is_on(hass, light_id):
return return
light.turn_on(hass, light_id, light.async_turn_on(hass, light_id,
transition=LIGHT_TRANSITION_TIME.seconds, transition=LIGHT_TRANSITION_TIME.seconds,
profile=light_profile) profile=light_profile)
# Track every time sun rises so we can schedule a time-based # Track every time sun rises so we can schedule a time-based
# pre-sun set event # pre-sun set event
@track_state_change(sun.ENTITY_ID, sun.STATE_BELOW_HORIZON, @track_state_change(sun.ENTITY_ID, sun.STATE_BELOW_HORIZON,
sun.STATE_ABOVE_HORIZON) sun.STATE_ABOVE_HORIZON)
@callback
def schedule_lights_at_sun_set(hass, entity, old_state, new_state): def schedule_lights_at_sun_set(hass, entity, old_state, new_state):
"""The moment sun sets we want to have all the lights on. """The moment sun sets we want to have all the lights on.
@ -104,16 +106,21 @@ def setup(hass, config):
if not start_point: if not start_point:
return return
def turn_on(light_id): def async_turn_on_factory(light_id):
"""Lambda can keep track of function parameters. """Lambda can keep track of function parameters.
No local parameters. If we put the lambda directly in the below No local parameters. If we put the lambda directly in the below
statement only the last light will be turned on. statement only the last light will be turned on.
""" """
return lambda now: turn_light_on_before_sunset(light_id) @callback
def async_turn_on_light(now):
"""Turn on specific light."""
async_turn_on_before_sunset(light_id)
return async_turn_on_light
for index, light_id in enumerate(light_ids): for index, light_id in enumerate(light_ids):
track_point_in_time(hass, turn_on(light_id), track_point_in_time(hass, async_turn_on_factory(light_id),
start_point + index * LIGHT_TRANSITION_TIME) start_point + index * LIGHT_TRANSITION_TIME)
# If the sun is already above horizon schedule the time-based pre-sun set # If the sun is already above horizon schedule the time-based pre-sun set
@ -122,6 +129,7 @@ def setup(hass, config):
schedule_lights_at_sun_set(hass, None, None, None) schedule_lights_at_sun_set(hass, None, None, None)
@track_state_change(device_entity_ids, STATE_NOT_HOME, STATE_HOME) @track_state_change(device_entity_ids, STATE_NOT_HOME, STATE_HOME)
@callback
def check_light_on_dev_state_change(hass, entity, old_state, new_state): def check_light_on_dev_state_change(hass, entity, old_state, new_state):
"""Handle tracked device state changes.""" """Handle tracked device state changes."""
# pylint: disable=unused-variable # pylint: disable=unused-variable
@ -136,7 +144,7 @@ def setup(hass, config):
# Do we need lights? # Do we need lights?
if light_needed: if light_needed:
logger.info("Home coming event for %s. Turning lights on", entity) logger.info("Home coming event for %s. Turning lights on", entity)
light.turn_on(hass, light_ids, profile=light_profile) light.async_turn_on(hass, light_ids, profile=light_profile)
# Are we in the time span were we would turn on the lights # Are we in the time span were we would turn on the lights
# if someone would be home? # if someone would be home?
@ -149,7 +157,7 @@ def setup(hass, config):
# when the fading in started and turn it on if so # when the fading in started and turn it on if so
for index, light_id in enumerate(light_ids): for index, light_id in enumerate(light_ids):
if now > start_point + index * LIGHT_TRANSITION_TIME: if now > start_point + index * LIGHT_TRANSITION_TIME:
light.turn_on(hass, light_id) light.async_turn_on(hass, light_id)
else: else:
# If this light didn't happen to be turned on yet so # If this light didn't happen to be turned on yet so
@ -158,6 +166,7 @@ def setup(hass, config):
if not disable_turn_off: if not disable_turn_off:
@track_state_change(device_group, STATE_HOME, STATE_NOT_HOME) @track_state_change(device_group, STATE_HOME, STATE_NOT_HOME)
@callback
def turn_off_lights_when_all_leave(hass, entity, old_state, new_state): def turn_off_lights_when_all_leave(hass, entity, old_state, new_state):
"""Handle device group state change.""" """Handle device group state change."""
# pylint: disable=unused-variable # pylint: disable=unused-variable
@ -166,6 +175,6 @@ def setup(hass, config):
logger.info( logger.info(
"Everyone has left but there are lights on. Turning them off") "Everyone has left but there are lights on. Turning them off")
light.turn_off(hass, light_ids) light.async_turn_off(hass, light_ids)
return True return True

View File

@ -10,6 +10,7 @@ import csv
import voluptuous as vol import voluptuous as vol
from homeassistant.core import callback
from homeassistant.components import group from homeassistant.components import group
from homeassistant.config import load_yaml_config_file from homeassistant.config import load_yaml_config_file
from homeassistant.const import ( from homeassistant.const import (
@ -20,6 +21,7 @@ from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
import homeassistant.util.color as color_util import homeassistant.util.color as color_util
from homeassistant.util.async import run_callback_threadsafe
DOMAIN = "light" DOMAIN = "light"
@ -128,6 +130,18 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None,
rgb_color=None, xy_color=None, color_temp=None, white_value=None, rgb_color=None, xy_color=None, color_temp=None, white_value=None,
profile=None, flash=None, effect=None, color_name=None): profile=None, flash=None, effect=None, color_name=None):
"""Turn all or specified light on.""" """Turn all or specified light on."""
run_callback_threadsafe(
hass.loop, async_turn_on, hass, entity_id, transition, brightness,
rgb_color, xy_color, color_temp, white_value,
profile, flash, effect, color_name).result()
@callback
def async_turn_on(hass, entity_id=None, transition=None, brightness=None,
rgb_color=None, xy_color=None, color_temp=None,
white_value=None, profile=None, flash=None, effect=None,
color_name=None):
"""Turn all or specified light on."""
data = { data = {
key: value for key, value in [ key: value for key, value in [
(ATTR_ENTITY_ID, entity_id), (ATTR_ENTITY_ID, entity_id),
@ -144,10 +158,17 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None,
] if value is not None ] if value is not None
} }
hass.services.call(DOMAIN, SERVICE_TURN_ON, data) hass.async_add_job(hass.services.async_call, DOMAIN, SERVICE_TURN_ON, data)
def turn_off(hass, entity_id=None, transition=None): def turn_off(hass, entity_id=None, transition=None):
"""Turn all or specified light off."""
run_callback_threadsafe(
hass.loop, async_turn_off, hass, entity_id, transition).result()
@callback
def async_turn_off(hass, entity_id=None, transition=None):
"""Turn all or specified light off.""" """Turn all or specified light off."""
data = { data = {
key: value for key, value in [ key: value for key, value in [
@ -156,7 +177,8 @@ def turn_off(hass, entity_id=None, transition=None):
] if value is not None ] if value is not None
} }
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data) hass.async_add_job(hass.services.async_call, DOMAIN, SERVICE_TURN_OFF,
data)
def toggle(hass, entity_id=None, transition=None): def toggle(hass, entity_id=None, transition=None):

View File

@ -49,14 +49,20 @@ def is_on(hass, entity_id=None):
def next_setting(hass, entity_id=None): def next_setting(hass, entity_id=None):
"""Local datetime object of the next sun setting.""" """Local datetime object of the next sun setting.
Async friendly.
"""
utc_next = next_setting_utc(hass, entity_id) utc_next = next_setting_utc(hass, entity_id)
return dt_util.as_local(utc_next) if utc_next else None return dt_util.as_local(utc_next) if utc_next else None
def next_setting_utc(hass, entity_id=None): def next_setting_utc(hass, entity_id=None):
"""UTC datetime object of the next sun setting.""" """UTC datetime object of the next sun setting.
Async friendly.
"""
entity_id = entity_id or ENTITY_ID entity_id = entity_id or ENTITY_ID
state = hass.states.get(ENTITY_ID) state = hass.states.get(ENTITY_ID)
@ -71,14 +77,20 @@ def next_setting_utc(hass, entity_id=None):
def next_rising(hass, entity_id=None): def next_rising(hass, entity_id=None):
"""Local datetime object of the next sun rising.""" """Local datetime object of the next sun rising.
Async friendly.
"""
utc_next = next_rising_utc(hass, entity_id) utc_next = next_rising_utc(hass, entity_id)
return dt_util.as_local(utc_next) if utc_next else None return dt_util.as_local(utc_next) if utc_next else None
def next_rising_utc(hass, entity_id=None): def next_rising_utc(hass, entity_id=None):
"""UTC datetime object of the next sun rising.""" """UTC datetime object of the next sun rising.
Async friendly.
"""
entity_id = entity_id or ENTITY_ID entity_id = entity_id or ENTITY_ID
state = hass.states.get(ENTITY_ID) state = hass.states.get(ENTITY_ID)

View File

@ -129,8 +129,12 @@ def mock_service(hass, domain, service):
""" """
calls = [] calls = []
@ha.callback
def mock_service(call):
calls.append(call)
# pylint: disable=unnecessary-lambda # pylint: disable=unnecessary-lambda
hass.services.register(domain, service, lambda call: calls.append(call)) hass.services.register(domain, service, mock_service)
return calls return calls

View File

@ -1,6 +1,7 @@
"""The tests for the Event automation.""" """The tests for the Event automation."""
import unittest import unittest
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
@ -16,6 +17,7 @@ class TestAutomationEvent(unittest.TestCase):
self.hass.config.components.append('group') self.hass.config.components.append('group')
self.calls = [] self.calls = []
@callback
def record_call(service): def record_call(service):
"""Helper for recording the call.""" """Helper for recording the call."""
self.calls.append(service) self.calls.append(service)

View File

@ -2,6 +2,7 @@
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
from homeassistant.const import ATTR_ENTITY_ID from homeassistant.const import ATTR_ENTITY_ID
@ -22,6 +23,7 @@ class TestAutomation(unittest.TestCase):
self.hass.config.components.append('group') self.hass.config.components.append('group')
self.calls = [] self.calls = []
@callback
def record_call(service): def record_call(service):
"""Record call.""" """Record call."""
self.calls.append(service) self.calls.append(service)

View File

@ -1,6 +1,7 @@
"""The tests for the MQTT automation.""" """The tests for the MQTT automation."""
import unittest import unittest
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
from tests.common import ( from tests.common import (
@ -17,6 +18,7 @@ class TestAutomationMQTT(unittest.TestCase):
mock_mqtt_component(self.hass) mock_mqtt_component(self.hass)
self.calls = [] self.calls = []
@callback
def record_call(service): def record_call(service):
self.calls.append(service) self.calls.append(service)

View File

@ -1,6 +1,7 @@
"""The tests for numeric state automation.""" """The tests for numeric state automation."""
import unittest import unittest
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
@ -16,6 +17,7 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.config.components.append('group') self.hass.config.components.append('group')
self.calls = [] self.calls = []
@callback
def record_call(service): def record_call(service):
"""Helper to record calls.""" """Helper to record calls."""
self.calls.append(service) self.calls.append(service)

View File

@ -3,6 +3,7 @@ import unittest
from datetime import timedelta from datetime import timedelta
from unittest.mock import patch from unittest.mock import patch
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
@ -21,6 +22,7 @@ class TestAutomationState(unittest.TestCase):
self.hass.states.set('test.entity', 'hello') self.hass.states.set('test.entity', 'hello')
self.calls = [] self.calls = []
@callback
def record_call(service): def record_call(service):
self.calls.append(service) self.calls.append(service)

View File

@ -3,6 +3,7 @@ from datetime import datetime
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
from homeassistant.components import sun from homeassistant.components import sun
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
@ -22,6 +23,7 @@ class TestAutomationSun(unittest.TestCase):
self.calls = [] self.calls = []
@callback
def record_call(service): def record_call(service):
self.calls.append(service) self.calls.append(service)

View File

@ -1,6 +1,7 @@
"""The tests for the Template automation.""" """The tests for the Template automation."""
import unittest import unittest
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
@ -17,6 +18,7 @@ class TestAutomationTemplate(unittest.TestCase):
self.hass.states.set('test.entity', 'hello') self.hass.states.set('test.entity', 'hello')
self.calls = [] self.calls = []
@callback
def record_call(service): def record_call(service):
"""helper for recording calls.""" """helper for recording calls."""
self.calls.append(service) self.calls.append(service)

View File

@ -3,6 +3,7 @@ from datetime import timedelta
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
@ -20,6 +21,7 @@ class TestAutomationTime(unittest.TestCase):
self.hass.config.components.append('group') self.hass.config.components.append('group')
self.calls = [] self.calls = []
@callback
def record_call(service): def record_call(service):
self.calls.append(service) self.calls.append(service)

View File

@ -1,6 +1,7 @@
"""The tests for the location automation.""" """The tests for the location automation."""
import unittest import unittest
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
from homeassistant.components import automation, zone from homeassistant.components import automation, zone
@ -25,6 +26,7 @@ class TestAutomationZone(unittest.TestCase):
self.calls = [] self.calls = []
@callback
def record_call(service): def record_call(service):
self.calls.append(service) self.calls.append(service)

View File

@ -3,7 +3,7 @@ import datetime
import unittest import unittest
from unittest import mock from unittest import mock
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
from homeassistant.const import ( from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT, ATTR_UNIT_OF_MEASUREMENT,
@ -216,6 +216,7 @@ class TestClimateGenericThermostat(unittest.TestCase):
self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF)
self.calls = [] self.calls = []
@callback
def log_call(call): def log_call(call):
"""Log service calls.""" """Log service calls."""
self.calls.append(call) self.calls.append(call)
@ -306,6 +307,7 @@ class TestClimateGenericThermostatACMode(unittest.TestCase):
self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF)
self.calls = [] self.calls = []
@callback
def log_call(call): def log_call(call):
"""Log service calls.""" """Log service calls."""
self.calls.append(call) self.calls.append(call)
@ -397,6 +399,7 @@ class TestClimateGenericThermostatACModeMinCycle(unittest.TestCase):
self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF)
self.calls = [] self.calls = []
@callback
def log_call(call): def log_call(call):
"""Log service calls.""" """Log service calls."""
self.calls.append(call) self.calls.append(call)
@ -487,6 +490,7 @@ class TestClimateGenericThermostatMinCycle(unittest.TestCase):
self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF)
self.calls = [] self.calls = []
@callback
def log_call(call): def log_call(call):
"""Log service calls.""" """Log service calls."""
self.calls.append(call) self.calls.append(call)

View File

@ -1,7 +1,7 @@
"""The tests for the Template switch platform.""" """The tests for the Template switch platform."""
from homeassistant.core import callback
import homeassistant.bootstrap as bootstrap import homeassistant.bootstrap as bootstrap
import homeassistant.components as core import homeassistant.components as core
from homeassistant.const import ( from homeassistant.const import (
STATE_ON, STATE_ON,
STATE_OFF) STATE_OFF)
@ -21,6 +21,7 @@ class TestTemplateSwitch:
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.calls = [] self.calls = []
@callback
def record_call(service): def record_call(service):
"""Track function calls..""" """Track function calls.."""
self.calls.append(service) self.calls.append(service)

View File

@ -6,6 +6,7 @@ import unittest
import requests import requests
from homeassistant.core import callback
from homeassistant import bootstrap, const from homeassistant import bootstrap, const
from homeassistant.components import alexa, http from homeassistant.components import alexa, http
@ -47,7 +48,11 @@ def setUpModule():
{http.DOMAIN: {http.CONF_API_PASSWORD: API_PASSWORD, {http.DOMAIN: {http.CONF_API_PASSWORD: API_PASSWORD,
http.CONF_SERVER_PORT: SERVER_PORT}}) http.CONF_SERVER_PORT: SERVER_PORT}})
hass.services.register("test", "alexa", lambda call: calls.append(call)) @callback
def mock_service(call):
calls.append(call)
hass.services.register("test", "alexa", mock_service)
bootstrap.setup_component(hass, alexa.DOMAIN, { bootstrap.setup_component(hass, alexa.DOMAIN, {
# Key is here to verify we allow other keys in config too # Key is here to verify we allow other keys in config too

View File

@ -288,6 +288,7 @@ class TestAPI(unittest.TestCase):
"""Test if the API allows us to call a service.""" """Test if the API allows us to call a service."""
test_value = [] test_value = []
@ha.callback
def listener(service_call): def listener(service_call):
"""Helper method that will verify that our service got called.""" """Helper method that will verify that our service got called."""
test_value.append(1) test_value.append(1)
@ -307,6 +308,7 @@ class TestAPI(unittest.TestCase):
"""Test if the API allows us to call a service.""" """Test if the API allows us to call a service."""
test_value = [] test_value = []
@ha.callback
def listener(service_call): def listener(service_call):
"""Helper method that will verify that our service got called. """Helper method that will verify that our service got called.

View File

@ -3,6 +3,7 @@
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component from homeassistant.bootstrap import setup_component
import homeassistant.components as core_components import homeassistant.components as core_components
from homeassistant.components import conversation from homeassistant.components import conversation
@ -38,6 +39,7 @@ class TestConversation(unittest.TestCase):
"""Setup and perform good turn on requests.""" """Setup and perform good turn on requests."""
calls = [] calls = []
@callback
def record_call(service): def record_call(service):
calls.append(service) calls.append(service)
@ -56,6 +58,7 @@ class TestConversation(unittest.TestCase):
"""Setup and perform good turn off requests.""" """Setup and perform good turn off requests."""
calls = [] calls = []
@callback
def record_call(service): def record_call(service):
calls.append(service) calls.append(service)

View File

@ -130,6 +130,7 @@ class TestScriptComponent(unittest.TestCase):
"""Test different ways of passing in variables.""" """Test different ways of passing in variables."""
calls = [] calls = []
@callback
def record_call(service): def record_call(service):
"""Add recorded event to set.""" """Add recorded event to set."""
calls.append(service) calls.append(service)

View File

@ -508,7 +508,12 @@ class TestServiceRegistry(unittest.TestCase):
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.services = self.hass.services self.services = self.hass.services
self.services.register("Test_Domain", "TEST_SERVICE", lambda x: None)
@ha.callback
def mock_service(call):
pass
self.services.register("Test_Domain", "TEST_SERVICE", mock_service)
# pylint: disable=invalid-name # pylint: disable=invalid-name
def tearDown(self): def tearDown(self):
@ -535,6 +540,7 @@ class TestServiceRegistry(unittest.TestCase):
"""Test call with blocking.""" """Test call with blocking."""
calls = [] calls = []
@ha.callback
def service_handler(call): def service_handler(call):
"""Service handler.""" """Service handler."""
calls.append(call) calls.append(call)

View File

@ -116,6 +116,7 @@ class TestRemoteMethods(unittest.TestCase):
"""Test Python API fire_event.""" """Test Python API fire_event."""
test_value = [] test_value = []
@ha.callback
def listener(event): def listener(event):
"""Helper method that will verify our event got called.""" """Helper method that will verify our event got called."""
test_value.append(1) test_value.append(1)
@ -200,6 +201,7 @@ class TestRemoteMethods(unittest.TestCase):
"""Test Python API services.call.""" """Test Python API services.call."""
test_value = [] test_value = []
@ha.callback
def listener(service_call): def listener(service_call):
"""Helper method that will verify that our service got called.""" """Helper method that will verify that our service got called."""
test_value.append(1) test_value.append(1)