Improved Homekit tests (#12800)
* Added test for temperature fahrenheit * Restructured tests to use more mocks * Rearanged homekit constants * Improved 'test_homekit_class' * Added import statements * Fix Pylint Test errors
This commit is contained in:
parent
d3386907a4
commit
168e1f0e2d
11 changed files with 420 additions and 94 deletions
|
@ -64,12 +64,10 @@ def async_setup(hass, config):
|
||||||
|
|
||||||
|
|
||||||
def import_types():
|
def import_types():
|
||||||
"""Import all types from files in the HomeKit dir."""
|
"""Import all types from files in the HomeKit directory."""
|
||||||
_LOGGER.debug("Import type files.")
|
_LOGGER.debug("Import type files.")
|
||||||
# pylint: disable=unused-variable
|
# pylint: disable=unused-variable
|
||||||
from .covers import Window # noqa F401
|
from . import covers, sensors # noqa F401
|
||||||
# pylint: disable=unused-variable
|
|
||||||
from .sensors import TemperatureSensor # noqa F401
|
|
||||||
|
|
||||||
|
|
||||||
def get_accessory(hass, state):
|
def get_accessory(hass, state):
|
||||||
|
|
|
@ -4,7 +4,7 @@ import logging
|
||||||
from pyhap.accessory import Accessory, Bridge, Category
|
from pyhap.accessory import Accessory, Bridge, Category
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
SERV_ACCESSORY_INFO, MANUFACTURER,
|
SERV_ACCESSORY_INFO, SERV_BRIDGING_STATE, MANUFACTURER,
|
||||||
CHAR_MODEL, CHAR_MANUFACTURER, CHAR_SERIAL_NUMBER)
|
CHAR_MODEL, CHAR_MANUFACTURER, CHAR_SERIAL_NUMBER)
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,17 +46,24 @@ def override_properties(char, new_properties):
|
||||||
class HomeAccessory(Accessory):
|
class HomeAccessory(Accessory):
|
||||||
"""Class to extend the Accessory class."""
|
"""Class to extend the Accessory class."""
|
||||||
|
|
||||||
def __init__(self, display_name, model, category='OTHER'):
|
def __init__(self, display_name, model, category='OTHER', **kwargs):
|
||||||
"""Initialize a Accessory object."""
|
"""Initialize a Accessory object."""
|
||||||
super().__init__(display_name)
|
super().__init__(display_name, **kwargs)
|
||||||
set_accessory_info(self, model)
|
set_accessory_info(self, model)
|
||||||
self.category = getattr(Category, category, Category.OTHER)
|
self.category = getattr(Category, category, Category.OTHER)
|
||||||
|
|
||||||
|
def _set_services(self):
|
||||||
|
add_preload_service(self, SERV_ACCESSORY_INFO)
|
||||||
|
|
||||||
|
|
||||||
class HomeBridge(Bridge):
|
class HomeBridge(Bridge):
|
||||||
"""Class to extend the Bridge class."""
|
"""Class to extend the Bridge class."""
|
||||||
|
|
||||||
def __init__(self, display_name, model, pincode):
|
def __init__(self, display_name, model, pincode, **kwargs):
|
||||||
"""Initialize a Bridge object."""
|
"""Initialize a Bridge object."""
|
||||||
super().__init__(display_name, pincode=pincode)
|
super().__init__(display_name, pincode=pincode, **kwargs)
|
||||||
set_accessory_info(self, model)
|
set_accessory_info(self, model)
|
||||||
|
|
||||||
|
def _set_services(self):
|
||||||
|
add_preload_service(self, SERV_ACCESSORY_INFO)
|
||||||
|
add_preload_service(self, SERV_BRIDGING_STATE)
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
"""Constants used be the HomeKit component."""
|
"""Constants used be the HomeKit component."""
|
||||||
MANUFACTURER = 'HomeAssistant'
|
MANUFACTURER = 'HomeAssistant'
|
||||||
|
|
||||||
# Service: AccessoryInfomation
|
# Services
|
||||||
SERV_ACCESSORY_INFO = 'AccessoryInformation'
|
SERV_ACCESSORY_INFO = 'AccessoryInformation'
|
||||||
CHAR_MODEL = 'Model'
|
SERV_BRIDGING_STATE = 'BridgingState'
|
||||||
CHAR_MANUFACTURER = 'Manufacturer'
|
|
||||||
CHAR_SERIAL_NUMBER = 'SerialNumber'
|
|
||||||
|
|
||||||
# Service: TemperatureSensor
|
|
||||||
SERV_TEMPERATURE_SENSOR = 'TemperatureSensor'
|
SERV_TEMPERATURE_SENSOR = 'TemperatureSensor'
|
||||||
CHAR_CURRENT_TEMPERATURE = 'CurrentTemperature'
|
|
||||||
|
|
||||||
# Service: WindowCovering
|
|
||||||
SERV_WINDOW_COVERING = 'WindowCovering'
|
SERV_WINDOW_COVERING = 'WindowCovering'
|
||||||
|
|
||||||
|
# Characteristics
|
||||||
|
CHAR_ACC_IDENTIFIER = 'AccessoryIdentifier'
|
||||||
|
CHAR_CATEGORY = 'Category'
|
||||||
CHAR_CURRENT_POSITION = 'CurrentPosition'
|
CHAR_CURRENT_POSITION = 'CurrentPosition'
|
||||||
CHAR_TARGET_POSITION = 'TargetPosition'
|
CHAR_CURRENT_TEMPERATURE = 'CurrentTemperature'
|
||||||
|
CHAR_LINK_QUALITY = 'LinkQuality'
|
||||||
|
CHAR_MANUFACTURER = 'Manufacturer'
|
||||||
|
CHAR_MODEL = 'Model'
|
||||||
CHAR_POSITION_STATE = 'PositionState'
|
CHAR_POSITION_STATE = 'PositionState'
|
||||||
|
CHAR_REACHABLE = 'Reachable'
|
||||||
|
CHAR_SERIAL_NUMBER = 'SerialNumber'
|
||||||
|
CHAR_TARGET_POSITION = 'TargetPosition'
|
||||||
|
|
||||||
# Properties
|
# Properties
|
||||||
PROP_CELSIUS = {'minValue': -273, 'maxValue': 999}
|
PROP_CELSIUS = {'minValue': -273, 'maxValue': 999}
|
||||||
|
|
|
@ -38,6 +38,9 @@ class Window(HomeAccessory):
|
||||||
get_characteristic(CHAR_TARGET_POSITION)
|
get_characteristic(CHAR_TARGET_POSITION)
|
||||||
self.char_position_state = self.serv_cover. \
|
self.char_position_state = self.serv_cover. \
|
||||||
get_characteristic(CHAR_POSITION_STATE)
|
get_characteristic(CHAR_POSITION_STATE)
|
||||||
|
self.char_current_position.value = 0
|
||||||
|
self.char_target_position.value = 0
|
||||||
|
self.char_position_state.value = 0
|
||||||
|
|
||||||
self.char_target_position.setter_callback = self.move_cover
|
self.char_target_position.setter_callback = self.move_cover
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ class TemperatureSensor(HomeAccessory):
|
||||||
self.char_temp = self.serv_temp. \
|
self.char_temp = self.serv_temp. \
|
||||||
get_characteristic(CHAR_CURRENT_TEMPERATURE)
|
get_characteristic(CHAR_CURRENT_TEMPERATURE)
|
||||||
override_properties(self.char_temp, PROP_CELSIUS)
|
override_properties(self.char_temp, PROP_CELSIUS)
|
||||||
|
self.char_temp.value = 0
|
||||||
self.unit = None
|
self.unit = None
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
165
tests/components/homekit/test_accessories.py
Normal file
165
tests/components/homekit/test_accessories.py
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
"""Test all functions related to the basic accessory implementation.
|
||||||
|
|
||||||
|
This includes tests for all mock object types.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
# pylint: disable=unused-import
|
||||||
|
from pyhap.loader import get_serv_loader, get_char_loader # noqa F401
|
||||||
|
|
||||||
|
from homeassistant.components.homekit.accessories import (
|
||||||
|
set_accessory_info, add_preload_service, override_properties,
|
||||||
|
HomeAccessory, HomeBridge)
|
||||||
|
from homeassistant.components.homekit.const import (
|
||||||
|
SERV_ACCESSORY_INFO, SERV_BRIDGING_STATE,
|
||||||
|
CHAR_MODEL, CHAR_MANUFACTURER, CHAR_SERIAL_NUMBER)
|
||||||
|
|
||||||
|
from tests.mock.homekit import (
|
||||||
|
get_patch_paths, mock_preload_service,
|
||||||
|
MockTypeLoader, MockAccessory, MockService, MockChar)
|
||||||
|
|
||||||
|
PATH_SERV = 'pyhap.loader.get_serv_loader'
|
||||||
|
PATH_CHAR = 'pyhap.loader.get_char_loader'
|
||||||
|
PATH_ACC, _ = get_patch_paths()
|
||||||
|
|
||||||
|
|
||||||
|
@patch(PATH_CHAR, return_value=MockTypeLoader('char'))
|
||||||
|
@patch(PATH_SERV, return_value=MockTypeLoader('service'))
|
||||||
|
def test_add_preload_service(mock_serv, mock_char):
|
||||||
|
"""Test method add_preload_service.
|
||||||
|
|
||||||
|
The methods 'get_serv_loader' and 'get_char_loader' are mocked.
|
||||||
|
"""
|
||||||
|
acc = MockAccessory('Accessory')
|
||||||
|
serv = add_preload_service(acc, 'TestService',
|
||||||
|
['TestChar', 'TestChar2'],
|
||||||
|
['TestOptChar', 'TestOptChar2'])
|
||||||
|
|
||||||
|
assert serv.display_name == 'TestService'
|
||||||
|
assert len(serv.characteristics) == 2
|
||||||
|
assert len(serv.opt_characteristics) == 2
|
||||||
|
|
||||||
|
acc.services = []
|
||||||
|
serv = add_preload_service(acc, 'TestService')
|
||||||
|
|
||||||
|
assert not serv.characteristics
|
||||||
|
assert not serv.opt_characteristics
|
||||||
|
|
||||||
|
acc.services = []
|
||||||
|
serv = add_preload_service(acc, 'TestService',
|
||||||
|
'TestChar', 'TestOptChar')
|
||||||
|
|
||||||
|
assert len(serv.characteristics) == 1
|
||||||
|
assert len(serv.opt_characteristics) == 1
|
||||||
|
|
||||||
|
assert serv.characteristics[0].display_name == 'TestChar'
|
||||||
|
assert serv.opt_characteristics[0].display_name == 'TestOptChar'
|
||||||
|
|
||||||
|
|
||||||
|
def test_override_properties():
|
||||||
|
"""Test override of characteristic properties with MockChar."""
|
||||||
|
char = MockChar('TestChar')
|
||||||
|
new_prop = {1: 'Test', 2: 'Demo'}
|
||||||
|
override_properties(char, new_prop)
|
||||||
|
|
||||||
|
assert char.properties == new_prop
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_accessory_info():
|
||||||
|
"""Test setting of basic accessory information with MockAccessory."""
|
||||||
|
acc = MockAccessory('Accessory')
|
||||||
|
set_accessory_info(acc, 'model', 'manufacturer', '0000')
|
||||||
|
|
||||||
|
assert len(acc.services) == 1
|
||||||
|
serv = acc.services[0]
|
||||||
|
|
||||||
|
assert serv.display_name == SERV_ACCESSORY_INFO
|
||||||
|
assert len(serv.characteristics) == 3
|
||||||
|
chars = serv.characteristics
|
||||||
|
|
||||||
|
assert chars[0].display_name == CHAR_MODEL
|
||||||
|
assert chars[0].value == 'model'
|
||||||
|
assert chars[1].display_name == CHAR_MANUFACTURER
|
||||||
|
assert chars[1].value == 'manufacturer'
|
||||||
|
assert chars[2].display_name == CHAR_SERIAL_NUMBER
|
||||||
|
assert chars[2].value == '0000'
|
||||||
|
|
||||||
|
|
||||||
|
@patch(PATH_ACC, side_effect=mock_preload_service)
|
||||||
|
def test_home_accessory(mock_pre_serv):
|
||||||
|
"""Test initializing a HomeAccessory object."""
|
||||||
|
acc = HomeAccessory('TestAccessory', 'test.accessory', 'WINDOW')
|
||||||
|
|
||||||
|
assert acc.display_name == 'TestAccessory'
|
||||||
|
assert acc.category == 13 # Category.WINDOW
|
||||||
|
assert len(acc.services) == 1
|
||||||
|
|
||||||
|
serv = acc.services[0]
|
||||||
|
assert serv.display_name == SERV_ACCESSORY_INFO
|
||||||
|
char_model = serv.get_characteristic(CHAR_MODEL)
|
||||||
|
assert char_model.get_value() == 'test.accessory'
|
||||||
|
|
||||||
|
|
||||||
|
@patch(PATH_ACC, side_effect=mock_preload_service)
|
||||||
|
def test_home_bridge(mock_pre_serv):
|
||||||
|
"""Test initializing a HomeBridge object."""
|
||||||
|
bridge = HomeBridge('TestBridge', 'test.bridge', b'123-45-678')
|
||||||
|
|
||||||
|
assert bridge.display_name == 'TestBridge'
|
||||||
|
assert bridge.pincode == b'123-45-678'
|
||||||
|
assert len(bridge.services) == 2
|
||||||
|
|
||||||
|
assert bridge.services[0].display_name == SERV_ACCESSORY_INFO
|
||||||
|
assert bridge.services[1].display_name == SERV_BRIDGING_STATE
|
||||||
|
|
||||||
|
char_model = bridge.services[0].get_characteristic(CHAR_MODEL)
|
||||||
|
assert char_model.get_value() == 'test.bridge'
|
||||||
|
|
||||||
|
|
||||||
|
def test_mock_accessory():
|
||||||
|
"""Test attributes and functions of a MockAccessory."""
|
||||||
|
acc = MockAccessory('TestAcc')
|
||||||
|
serv = MockService('TestServ')
|
||||||
|
acc.add_service(serv)
|
||||||
|
|
||||||
|
assert acc.display_name == 'TestAcc'
|
||||||
|
assert len(acc.services) == 1
|
||||||
|
|
||||||
|
assert acc.get_service('TestServ') == serv
|
||||||
|
assert acc.get_service('NewServ').display_name == 'NewServ'
|
||||||
|
assert len(acc.services) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_mock_service():
|
||||||
|
"""Test attributes and functions of a MockService."""
|
||||||
|
serv = MockService('TestServ')
|
||||||
|
char = MockChar('TestChar')
|
||||||
|
opt_char = MockChar('TestOptChar')
|
||||||
|
serv.add_characteristic(char)
|
||||||
|
serv.add_opt_characteristic(opt_char)
|
||||||
|
|
||||||
|
assert serv.display_name == 'TestServ'
|
||||||
|
assert len(serv.characteristics) == 1
|
||||||
|
assert len(serv.opt_characteristics) == 1
|
||||||
|
|
||||||
|
assert serv.get_characteristic('TestChar') == char
|
||||||
|
assert serv.get_characteristic('TestOptChar') == opt_char
|
||||||
|
assert serv.get_characteristic('NewChar').display_name == 'NewChar'
|
||||||
|
assert len(serv.characteristics) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_mock_char():
|
||||||
|
"""Test attributes and functions of a MockChar."""
|
||||||
|
def callback_method(value):
|
||||||
|
"""Provide a callback options for 'set_value' method."""
|
||||||
|
assert value == 'With callback'
|
||||||
|
|
||||||
|
char = MockChar('TestChar')
|
||||||
|
char.set_value('Value')
|
||||||
|
|
||||||
|
assert char.display_name == 'TestChar'
|
||||||
|
assert char.get_value() == 'Value'
|
||||||
|
|
||||||
|
char.setter_callback = callback_method
|
||||||
|
char.set_value('With callback')
|
|
@ -1,5 +1,6 @@
|
||||||
"""Test different accessory types: Covers."""
|
"""Test different accessory types: Covers."""
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
|
@ -10,6 +11,9 @@ from homeassistant.const import (
|
||||||
ATTR_SERVICE, ATTR_SERVICE_DATA, EVENT_CALL_SERVICE)
|
ATTR_SERVICE, ATTR_SERVICE_DATA, EVENT_CALL_SERVICE)
|
||||||
|
|
||||||
from tests.common import get_test_home_assistant
|
from tests.common import get_test_home_assistant
|
||||||
|
from tests.mock.homekit import get_patch_paths, mock_preload_service
|
||||||
|
|
||||||
|
PATH_ACC, PATH_FILE = get_patch_paths('covers')
|
||||||
|
|
||||||
|
|
||||||
class TestHomekitSensors(unittest.TestCase):
|
class TestHomekitSensors(unittest.TestCase):
|
||||||
|
@ -35,13 +39,14 @@ class TestHomekitSensors(unittest.TestCase):
|
||||||
"""Test if accessory and HA are updated accordingly."""
|
"""Test if accessory and HA are updated accordingly."""
|
||||||
window_cover = 'cover.window'
|
window_cover = 'cover.window'
|
||||||
|
|
||||||
acc = Window(self.hass, window_cover, 'Cover')
|
with patch(PATH_ACC, side_effect=mock_preload_service):
|
||||||
acc.run()
|
with patch(PATH_FILE, side_effect=mock_preload_service):
|
||||||
|
acc = Window(self.hass, window_cover, 'Cover')
|
||||||
|
acc.run()
|
||||||
|
|
||||||
self.assertEqual(acc.char_current_position.value, 0)
|
self.assertEqual(acc.char_current_position.value, 0)
|
||||||
self.assertEqual(acc.char_target_position.value, 0)
|
self.assertEqual(acc.char_target_position.value, 0)
|
||||||
# Temporarily disabled due to bug in HAP-python==1.15 with py3.5
|
self.assertEqual(acc.char_position_state.value, 0)
|
||||||
# self.assertEqual(acc.char_position_state.value, 0)
|
|
||||||
|
|
||||||
self.hass.states.set(window_cover, STATE_UNKNOWN,
|
self.hass.states.set(window_cover, STATE_UNKNOWN,
|
||||||
{ATTR_CURRENT_POSITION: None})
|
{ATTR_CURRENT_POSITION: None})
|
||||||
|
@ -49,8 +54,7 @@ class TestHomekitSensors(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(acc.char_current_position.value, 0)
|
self.assertEqual(acc.char_current_position.value, 0)
|
||||||
self.assertEqual(acc.char_target_position.value, 0)
|
self.assertEqual(acc.char_target_position.value, 0)
|
||||||
# Temporarily disabled due to bug in HAP-python==1.15 with py3.5
|
self.assertEqual(acc.char_position_state.value, 0)
|
||||||
# self.assertEqual(acc.char_position_state.value, 0)
|
|
||||||
|
|
||||||
self.hass.states.set(window_cover, STATE_OPEN,
|
self.hass.states.set(window_cover, STATE_OPEN,
|
||||||
{ATTR_CURRENT_POSITION: 50})
|
{ATTR_CURRENT_POSITION: 50})
|
||||||
|
|
|
@ -6,7 +6,7 @@ from homeassistant.components.homekit import (
|
||||||
TYPES, get_accessory, import_types)
|
TYPES, get_accessory, import_types)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_UNIT_OF_MEASUREMENT, ATTR_SUPPORTED_FEATURES,
|
ATTR_UNIT_OF_MEASUREMENT, ATTR_SUPPORTED_FEATURES,
|
||||||
TEMP_CELSIUS, STATE_UNKNOWN)
|
TEMP_CELSIUS, TEMP_FAHRENHEIT, STATE_UNKNOWN)
|
||||||
|
|
||||||
|
|
||||||
def test_import_types():
|
def test_import_types():
|
||||||
|
@ -26,21 +26,32 @@ def test_component_not_supported():
|
||||||
assert True if get_accessory(None, state) is None else False
|
assert True if get_accessory(None, state) is None else False
|
||||||
|
|
||||||
|
|
||||||
def test_sensor_temperatur_celsius():
|
def test_sensor_temperature_celsius():
|
||||||
"""Test temperature sensor with celsius as unit."""
|
"""Test temperature sensor with Celsius as unit."""
|
||||||
mock_type = MagicMock()
|
mock_type = MagicMock()
|
||||||
with patch.dict(TYPES, {'TemperatureSensor': mock_type}):
|
with patch.dict(TYPES, {'TemperatureSensor': mock_type}):
|
||||||
state = State('sensor.temperatur', '23',
|
state = State('sensor.temperature', '23',
|
||||||
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS})
|
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS})
|
||||||
get_accessory(None, state)
|
get_accessory(None, state)
|
||||||
assert len(mock_type.mock_calls) == 1
|
assert len(mock_type.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
def test_sensor_temperature_fahrenheit():
|
||||||
|
"""Test temperature sensor with Fahrenheit as unit."""
|
||||||
|
mock_type = MagicMock()
|
||||||
|
with patch.dict(TYPES, {'TemperatureSensor': mock_type}):
|
||||||
|
state = State('sensor.temperature', '74',
|
||||||
|
{ATTR_UNIT_OF_MEASUREMENT: TEMP_FAHRENHEIT})
|
||||||
|
get_accessory(None, state)
|
||||||
|
assert len(mock_type.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
def test_cover_set_position():
|
def test_cover_set_position():
|
||||||
"""Test cover with support for set_cover_position."""
|
"""Test cover with support for set_cover_position."""
|
||||||
mock_type = MagicMock()
|
mock_type = MagicMock()
|
||||||
with patch.dict(TYPES, {'Window': mock_type}):
|
with patch.dict(TYPES, {'Window': mock_type}):
|
||||||
state = State('cover.setposition', 'open',
|
state = State('cover.set_position', 'open',
|
||||||
{ATTR_SUPPORTED_FEATURES: 4})
|
{ATTR_SUPPORTED_FEATURES: 4})
|
||||||
get_accessory(None, state)
|
get_accessory(None, state)
|
||||||
assert len(mock_type.mock_calls) == 1
|
assert len(mock_type.mock_calls) == 1
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
"""Tests for the HomeKit component."""
|
"""Tests for the HomeKit component."""
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import call, patch
|
from unittest.mock import call, patch, ANY
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
# pylint: disable=unused-import
|
||||||
|
from pyhap.accessory_driver import AccessoryDriver # noqa F401
|
||||||
|
|
||||||
from homeassistant import setup
|
from homeassistant import setup
|
||||||
from homeassistant.core import Event
|
from homeassistant.core import Event
|
||||||
from homeassistant.components.homekit import (
|
from homeassistant.components.homekit import (
|
||||||
CONF_PIN_CODE, BRIDGE_NAME, HOMEKIT_FILE, HomeKit, valid_pin)
|
CONF_PIN_CODE, HOMEKIT_FILE, HomeKit, valid_pin)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_PORT, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
CONF_PORT, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
||||||
|
|
||||||
from tests.common import get_test_home_assistant
|
from tests.common import get_test_home_assistant
|
||||||
|
from tests.mock.homekit import get_patch_paths, PATH_HOMEKIT
|
||||||
|
|
||||||
HOMEKIT_PATH = 'homeassistant.components.homekit'
|
PATH_ACC, _ = get_patch_paths()
|
||||||
|
IP_ADDRESS = '127.0.0.1'
|
||||||
|
|
||||||
CONFIG_MIN = {'homekit': {}}
|
CONFIG_MIN = {'homekit': {}}
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
@ -36,39 +41,6 @@ class TestHomeKit(unittest.TestCase):
|
||||||
"""Stop down everything that was started."""
|
"""Stop down everything that was started."""
|
||||||
self.hass.stop()
|
self.hass.stop()
|
||||||
|
|
||||||
@patch(HOMEKIT_PATH + '.HomeKit.start_driver')
|
|
||||||
@patch(HOMEKIT_PATH + '.HomeKit.setup_bridge')
|
|
||||||
@patch(HOMEKIT_PATH + '.HomeKit.__init__')
|
|
||||||
def test_setup_min(self, mock_homekit, mock_setup_bridge,
|
|
||||||
mock_start_driver):
|
|
||||||
"""Test async_setup with minimal config option."""
|
|
||||||
mock_homekit.return_value = None
|
|
||||||
|
|
||||||
self.assertTrue(setup.setup_component(
|
|
||||||
self.hass, 'homekit', CONFIG_MIN))
|
|
||||||
|
|
||||||
mock_homekit.assert_called_once_with(self.hass, 51826)
|
|
||||||
mock_setup_bridge.assert_called_with(b'123-45-678')
|
|
||||||
mock_start_driver.assert_not_called()
|
|
||||||
|
|
||||||
self.hass.start()
|
|
||||||
self.hass.block_till_done()
|
|
||||||
self.assertEqual(mock_start_driver.call_count, 1)
|
|
||||||
|
|
||||||
@patch(HOMEKIT_PATH + '.HomeKit.start_driver')
|
|
||||||
@patch(HOMEKIT_PATH + '.HomeKit.setup_bridge')
|
|
||||||
@patch(HOMEKIT_PATH + '.HomeKit.__init__')
|
|
||||||
def test_setup_parameters(self, mock_homekit, mock_setup_bridge,
|
|
||||||
mock_start_driver):
|
|
||||||
"""Test async_setup with full config option."""
|
|
||||||
mock_homekit.return_value = None
|
|
||||||
|
|
||||||
self.assertTrue(setup.setup_component(
|
|
||||||
self.hass, 'homekit', CONFIG))
|
|
||||||
|
|
||||||
mock_homekit.assert_called_once_with(self.hass, 11111)
|
|
||||||
mock_setup_bridge.assert_called_with(b'987-65-432')
|
|
||||||
|
|
||||||
def test_validate_pincode(self):
|
def test_validate_pincode(self):
|
||||||
"""Test async_setup with invalid config option."""
|
"""Test async_setup with invalid config option."""
|
||||||
schema = vol.Schema(valid_pin)
|
schema = vol.Schema(valid_pin)
|
||||||
|
@ -80,45 +52,64 @@ class TestHomeKit(unittest.TestCase):
|
||||||
for value in ('123-45-678', '234-56-789'):
|
for value in ('123-45-678', '234-56-789'):
|
||||||
self.assertTrue(schema(value))
|
self.assertTrue(schema(value))
|
||||||
|
|
||||||
|
@patch(PATH_HOMEKIT + '.HomeKit')
|
||||||
|
def test_setup_min(self, mock_homekit):
|
||||||
|
"""Test async_setup with minimal config option."""
|
||||||
|
self.assertTrue(setup.setup_component(
|
||||||
|
self.hass, 'homekit', CONFIG_MIN))
|
||||||
|
|
||||||
|
self.assertEqual(mock_homekit.mock_calls,
|
||||||
|
[call(self.hass, 51826),
|
||||||
|
call().setup_bridge(b'123-45-678')])
|
||||||
|
mock_homekit.reset_mock()
|
||||||
|
|
||||||
|
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
|
||||||
|
self.assertEqual(mock_homekit.mock_calls,
|
||||||
|
[call().start_driver(ANY)])
|
||||||
|
|
||||||
|
@patch(PATH_HOMEKIT + '.HomeKit')
|
||||||
|
def test_setup_parameters(self, mock_homekit):
|
||||||
|
"""Test async_setup with full config option."""
|
||||||
|
self.assertTrue(setup.setup_component(
|
||||||
|
self.hass, 'homekit', CONFIG))
|
||||||
|
|
||||||
|
self.assertEqual(mock_homekit.mock_calls,
|
||||||
|
[call(self.hass, 11111),
|
||||||
|
call().setup_bridge(b'987-65-432')])
|
||||||
|
|
||||||
@patch('pyhap.accessory_driver.AccessoryDriver')
|
@patch('pyhap.accessory_driver.AccessoryDriver')
|
||||||
@patch('pyhap.accessory.Bridge.add_accessory')
|
def test_homekit_class(self, mock_acc_driver):
|
||||||
@patch(HOMEKIT_PATH + '.import_types')
|
|
||||||
@patch(HOMEKIT_PATH + '.get_accessory')
|
|
||||||
def test_homekit_pyhap_interaction(
|
|
||||||
self, mock_get_accessory, mock_import_types,
|
|
||||||
mock_add_accessory, mock_acc_driver):
|
|
||||||
"""Test interaction between the HomeKit class and pyhap."""
|
"""Test interaction between the HomeKit class and pyhap."""
|
||||||
mock_get_accessory.side_effect = ['TemperatureSensor', 'Window']
|
with patch(PATH_HOMEKIT + '.accessories.HomeBridge') as mock_bridge:
|
||||||
|
homekit = HomeKit(self.hass, 51826)
|
||||||
homekit = HomeKit(self.hass, 51826)
|
homekit.setup_bridge(b'123-45-678')
|
||||||
homekit.setup_bridge(b'123-45-678')
|
|
||||||
|
|
||||||
self.assertEqual(homekit.bridge.display_name, BRIDGE_NAME)
|
|
||||||
|
|
||||||
|
mock_bridge.reset_mock()
|
||||||
self.hass.states.set('demo.demo1', 'on')
|
self.hass.states.set('demo.demo1', 'on')
|
||||||
self.hass.states.set('demo.demo2', 'off')
|
self.hass.states.set('demo.demo2', 'off')
|
||||||
|
|
||||||
self.hass.start()
|
with patch(PATH_HOMEKIT + '.get_accessory') as mock_get_acc, \
|
||||||
self.hass.block_till_done()
|
patch(PATH_HOMEKIT + '.import_types') as mock_import_types, \
|
||||||
|
patch('homeassistant.util.get_local_ip') as mock_ip:
|
||||||
with patch('homeassistant.util.get_local_ip',
|
mock_get_acc.side_effect = ['TempSensor', 'Window']
|
||||||
return_value='127.0.0.1'):
|
mock_ip.return_value = IP_ADDRESS
|
||||||
homekit.start_driver(Event(EVENT_HOMEASSISTANT_START))
|
homekit.start_driver(Event(EVENT_HOMEASSISTANT_START))
|
||||||
|
|
||||||
ip_address = '127.0.0.1'
|
|
||||||
path = self.hass.config.path(HOMEKIT_FILE)
|
path = self.hass.config.path(HOMEKIT_FILE)
|
||||||
|
|
||||||
self.assertEqual(mock_get_accessory.call_count, 2)
|
|
||||||
self.assertEqual(mock_import_types.call_count, 1)
|
self.assertEqual(mock_import_types.call_count, 1)
|
||||||
|
self.assertEqual(mock_get_acc.call_count, 2)
|
||||||
|
self.assertEqual(mock_bridge.mock_calls,
|
||||||
|
[call().add_accessory('TempSensor'),
|
||||||
|
call().add_accessory('Window')])
|
||||||
self.assertEqual(mock_acc_driver.mock_calls,
|
self.assertEqual(mock_acc_driver.mock_calls,
|
||||||
[call(homekit.bridge, 51826, ip_address, path),
|
[call(homekit.bridge, 51826, IP_ADDRESS, path),
|
||||||
call().start()])
|
call().start()])
|
||||||
|
mock_acc_driver.reset_mock()
|
||||||
self.assertEqual(mock_add_accessory.mock_calls,
|
|
||||||
[call('TemperatureSensor'), call('Window')])
|
|
||||||
|
|
||||||
self.hass.bus.fire(EVENT_HOMEASSISTANT_STOP)
|
self.hass.bus.fire(EVENT_HOMEASSISTANT_STOP)
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
|
|
||||||
self.assertEqual(mock_acc_driver.mock_calls[2], call().stop())
|
self.assertEqual(mock_acc_driver.mock_calls, [call().stop()])
|
||||||
self.assertEqual(len(mock_acc_driver.mock_calls), 3)
|
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
"""Test different accessory types: Sensors."""
|
"""Test different accessory types: Sensors."""
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from homeassistant.components.homekit.const import PROP_CELSIUS
|
||||||
from homeassistant.components.homekit.sensors import (
|
from homeassistant.components.homekit.sensors import (
|
||||||
TemperatureSensor, calc_temperature)
|
TemperatureSensor, calc_temperature)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS, TEMP_FAHRENHEIT, STATE_UNKNOWN)
|
ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS, TEMP_FAHRENHEIT, STATE_UNKNOWN)
|
||||||
|
|
||||||
from tests.common import get_test_home_assistant
|
from tests.common import get_test_home_assistant
|
||||||
|
from tests.mock.homekit import get_patch_paths, mock_preload_service
|
||||||
|
|
||||||
|
PATH_ACC, PATH_FILE = get_patch_paths('sensors')
|
||||||
|
|
||||||
|
|
||||||
def test_calc_temperature():
|
def test_calc_temperature():
|
||||||
|
@ -27,19 +32,24 @@ class TestHomekitSensors(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""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()
|
||||||
|
get_patch_paths('sensors')
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Stop down everything that was started."""
|
"""Stop down everything that was started."""
|
||||||
self.hass.stop()
|
self.hass.stop()
|
||||||
|
|
||||||
def test_temperature_celsius(self):
|
def test_temperature(self):
|
||||||
"""Test if accessory is updated after state change."""
|
"""Test if accessory is updated after state change."""
|
||||||
temperature_sensor = 'sensor.temperature'
|
temperature_sensor = 'sensor.temperature'
|
||||||
|
|
||||||
acc = TemperatureSensor(self.hass, temperature_sensor, 'Temperature')
|
with patch(PATH_ACC, side_effect=mock_preload_service):
|
||||||
acc.run()
|
with patch(PATH_FILE, side_effect=mock_preload_service):
|
||||||
|
acc = TemperatureSensor(self.hass, temperature_sensor,
|
||||||
|
'Temperature')
|
||||||
|
acc.run()
|
||||||
|
|
||||||
self.assertEqual(acc.char_temp.value, 0.0)
|
self.assertEqual(acc.char_temp.value, 0.0)
|
||||||
|
self.assertEqual(acc.char_temp.properties, PROP_CELSIUS)
|
||||||
|
|
||||||
self.hass.states.set(temperature_sensor, STATE_UNKNOWN,
|
self.hass.states.set(temperature_sensor, STATE_UNKNOWN,
|
||||||
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS})
|
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS})
|
||||||
|
|
133
tests/mock/homekit.py
Normal file
133
tests/mock/homekit.py
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
"""Basic mock functions and objects related to the HomeKit component."""
|
||||||
|
PATH_HOMEKIT = 'homeassistant.components.homekit'
|
||||||
|
|
||||||
|
|
||||||
|
def get_patch_paths(name=None):
|
||||||
|
"""Return paths to mock 'add_preload_service'."""
|
||||||
|
path_acc = PATH_HOMEKIT + '.accessories.add_preload_service'
|
||||||
|
path_file = PATH_HOMEKIT + '.' + str(name) + '.add_preload_service'
|
||||||
|
return (path_acc, path_file)
|
||||||
|
|
||||||
|
|
||||||
|
def mock_preload_service(acc, service, chars=None, opt_chars=None):
|
||||||
|
"""Mock alternative for function 'add_preload_service'."""
|
||||||
|
service = MockService(service)
|
||||||
|
if chars:
|
||||||
|
chars = chars if isinstance(chars, list) else [chars]
|
||||||
|
for char_name in chars:
|
||||||
|
service.add_characteristic(char_name)
|
||||||
|
if opt_chars:
|
||||||
|
opt_chars = opt_chars if isinstance(opt_chars, list) else [opt_chars]
|
||||||
|
for opt_char_name in opt_chars:
|
||||||
|
service.add_characteristic(opt_char_name)
|
||||||
|
acc.add_service(service)
|
||||||
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
class MockAccessory():
|
||||||
|
"""Define all attributes and methods for a MockAccessory."""
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
"""Initialize a MockAccessory object."""
|
||||||
|
self.display_name = name
|
||||||
|
self.services = []
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Return a representation of a MockAccessory. Use for debugging."""
|
||||||
|
serv_list = [serv.display_name for serv in self.services]
|
||||||
|
return "<accessory \"{}\", services={}>".format(
|
||||||
|
self.display_name, serv_list)
|
||||||
|
|
||||||
|
def add_service(self, service):
|
||||||
|
"""Add service to list of services."""
|
||||||
|
self.services.append(service)
|
||||||
|
|
||||||
|
def get_service(self, name):
|
||||||
|
"""Retrieve service from service list or return new MockService."""
|
||||||
|
for serv in self.services:
|
||||||
|
if serv.display_name == name:
|
||||||
|
return serv
|
||||||
|
serv = MockService(name)
|
||||||
|
self.add_service(serv)
|
||||||
|
return serv
|
||||||
|
|
||||||
|
|
||||||
|
class MockService():
|
||||||
|
"""Define all attributes and methods for a MockService."""
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
"""Initialize a MockService object."""
|
||||||
|
self.characteristics = []
|
||||||
|
self.opt_characteristics = []
|
||||||
|
self.display_name = name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Return a representation of a MockService. Use for debugging."""
|
||||||
|
char_list = [char.display_name for char in self.characteristics]
|
||||||
|
opt_char_list = [
|
||||||
|
char.display_name for char in self.opt_characteristics]
|
||||||
|
return "<service \"{}\", chars={}, opt_chars={}>".format(
|
||||||
|
self.display_name, char_list, opt_char_list)
|
||||||
|
|
||||||
|
def add_characteristic(self, char):
|
||||||
|
"""Add characteristic to char list."""
|
||||||
|
self.characteristics.append(char)
|
||||||
|
|
||||||
|
def add_opt_characteristic(self, char):
|
||||||
|
"""Add characteristic to opt_char list."""
|
||||||
|
self.opt_characteristics.append(char)
|
||||||
|
|
||||||
|
def get_characteristic(self, name):
|
||||||
|
"""Get char for char lists or return new MockChar."""
|
||||||
|
for char in self.characteristics:
|
||||||
|
if char.display_name == name:
|
||||||
|
return char
|
||||||
|
for char in self.opt_characteristics:
|
||||||
|
if char.display_name == name:
|
||||||
|
return char
|
||||||
|
char = MockChar(name)
|
||||||
|
self.add_characteristic(char)
|
||||||
|
return char
|
||||||
|
|
||||||
|
|
||||||
|
class MockChar():
|
||||||
|
"""Define all attributes and methods for a MockChar."""
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
"""Initialize a MockChar object."""
|
||||||
|
self.display_name = name
|
||||||
|
self.properties = {}
|
||||||
|
self.value = None
|
||||||
|
self.type_id = None
|
||||||
|
self.setter_callback = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Return a representation of a MockChar. Use for debugging."""
|
||||||
|
return "<char \"{}\", value={}>".format(
|
||||||
|
self.display_name, self.value)
|
||||||
|
|
||||||
|
def set_value(self, value, should_notify=True, should_callback=True):
|
||||||
|
"""Set value of char."""
|
||||||
|
self.value = value
|
||||||
|
if self.setter_callback is not None and should_callback:
|
||||||
|
# pylint: disable=not-callable
|
||||||
|
self.setter_callback(value)
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
"""Get char value."""
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
|
class MockTypeLoader():
|
||||||
|
"""Define all attributes and methods for a MockTypeLoader."""
|
||||||
|
|
||||||
|
def __init__(self, class_type):
|
||||||
|
"""Initialize a MockTypeLoader object."""
|
||||||
|
self.class_type = class_type
|
||||||
|
|
||||||
|
def get(self, name):
|
||||||
|
"""Return a MockService or MockChar object."""
|
||||||
|
if self.class_type == 'service':
|
||||||
|
return MockService(name)
|
||||||
|
elif self.class_type == 'char':
|
||||||
|
return MockChar(name)
|
Loading…
Add table
Reference in a new issue