From 8ceab5d4ba45335466de29e415c45c6266433e4e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 29 Mar 2016 22:50:38 -0700 Subject: [PATCH] Add extra tests --- tests/common.py | 3 +- tests/test_bootstrap.py | 184 +++++++++++++++++++++++++++++----------- 2 files changed, 138 insertions(+), 49 deletions(-) diff --git a/tests/common.py b/tests/common.py index cb7476be828..1774e6845b0 100644 --- a/tests/common.py +++ b/tests/common.py @@ -144,10 +144,11 @@ class MockModule(object): """Representation of a fake module.""" def __init__(self, domain=None, dependencies=[], setup=None, - config_schema=None, platform_schema=None): + requirements=[], config_schema=None, platform_schema=None): """Initialize the mock module.""" self.DOMAIN = domain self.DEPENDENCIES = dependencies + self.REQUIREMENTS = requirements if config_schema is not None: self.CONFIG_SCHEMA = config_schema diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index c2426dbe968..d6a72fdd86e 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -2,7 +2,8 @@ # pylint: disable=too-many-public-methods,protected-access import os import tempfile -import unittest +from unittest import mock +import threading import voluptuous as vol @@ -15,17 +16,29 @@ from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from tests.common import get_test_home_assistant, MockModule +ORIG_TIMEZONE = dt_util.DEFAULT_TIME_ZONE -class TestBootstrap(unittest.TestCase): + +class TestBootstrap: """Test the bootstrap utils.""" - def setUp(self): + def setup_method(self, method): """Setup the test.""" - self.orig_timezone = dt_util.DEFAULT_TIME_ZONE + if method == self.test_from_config_file: + return - def tearDown(self): + self.hass = get_test_home_assistant() + self.backup_cache = loader._COMPONENT_CACHE + + def teardown_method(self, method): """Clean up.""" - dt_util.DEFAULT_TIME_ZONE = self.orig_timezone + dt_util.DEFAULT_TIME_ZONE = ORIG_TIMEZONE + + if method == self.test_from_config_file: + return + + self.hass.stop() + loader._COMPONENT_CACHE = self.backup_cache def test_from_config_file(self): """Test with configuration file.""" @@ -39,8 +52,7 @@ class TestBootstrap(unittest.TestCase): components.append('group') - self.assertEqual(sorted(components), - sorted(hass.config.components)) + assert sorted(components) == sorted(hass.config.components) def test_remove_lib_on_upgrade(self): """Test removal of library on upgrade.""" @@ -57,13 +69,11 @@ class TestBootstrap(unittest.TestCase): with open(check_file, 'w'): pass - hass = get_test_home_assistant() - hass.config.config_dir = config_dir + self.hass.config.config_dir = config_dir - self.assertTrue(os.path.isfile(check_file)) - bootstrap.process_ha_config_upgrade(hass) - self.assertFalse(os.path.isfile(check_file)) - hass.stop() + assert os.path.isfile(check_file) + bootstrap.process_ha_config_upgrade(self.hass) + assert not os.path.isfile(check_file) def test_not_remove_lib_if_not_upgrade(self): """Test removal of library with no upgrade.""" @@ -80,13 +90,11 @@ class TestBootstrap(unittest.TestCase): with open(check_file, 'w'): pass - hass = get_test_home_assistant() - hass.config.config_dir = config_dir + self.hass.config.config_dir = config_dir - bootstrap.process_ha_config_upgrade(hass) + bootstrap.process_ha_config_upgrade(self.hass) - self.assertTrue(os.path.isfile(check_file)) - hass.stop() + assert os.path.isfile(check_file) def test_entity_customization(self): """Test entity customization through configuration.""" @@ -95,23 +103,19 @@ class TestBootstrap(unittest.TestCase): CONF_NAME: 'Test', CONF_CUSTOMIZE: {'test.test': {'hidden': True}}} - hass = get_test_home_assistant() - - bootstrap.process_ha_core_config(hass, config) + bootstrap.process_ha_core_config(self.hass, config) entity = Entity() entity.entity_id = 'test.test' - entity.hass = hass + entity.hass = self.hass entity.update_ha_state() - state = hass.states.get('test.test') + state = self.hass.states.get('test.test') - self.assertTrue(state.attributes['hidden']) - hass.stop() + assert state.attributes['hidden'] def test_handle_setup_circular_dependency(self): """Test the setup of circular dependencies.""" - hass = get_test_home_assistant() loader.set_component('comp_b', MockModule('comp_b', ['comp_a'])) def setup_a(hass, config): @@ -121,9 +125,8 @@ class TestBootstrap(unittest.TestCase): loader.set_component('comp_a', MockModule('comp_a', setup=setup_a)) - bootstrap.setup_component(hass, 'comp_a') - self.assertEqual(['comp_a'], hass.config.components) - hass.stop() + bootstrap.setup_component(self.hass, 'comp_a') + assert ['comp_a'] == self.hass.config.components def test_validate_component_config(self): """Test validating component configuration.""" @@ -135,33 +138,29 @@ class TestBootstrap(unittest.TestCase): loader.set_component( 'comp_conf', MockModule('comp_conf', config_schema=config_schema)) - hass = get_test_home_assistant() + assert not bootstrap._setup_component(self.hass, 'comp_conf', {}) - assert not bootstrap._setup_component(hass, 'comp_conf', {}) - - assert not bootstrap._setup_component(hass, 'comp_conf', { + assert not bootstrap._setup_component(self.hass, 'comp_conf', { 'comp_conf': None }) - assert not bootstrap._setup_component(hass, 'comp_conf', { + assert not bootstrap._setup_component(self.hass, 'comp_conf', { 'comp_conf': {} }) - assert not bootstrap._setup_component(hass, 'comp_conf', { + assert not bootstrap._setup_component(self.hass, 'comp_conf', { 'comp_conf': { 'hello': 'world', 'invalid': 'extra', } }) - assert bootstrap._setup_component(hass, 'comp_conf', { + assert bootstrap._setup_component(self.hass, 'comp_conf', { 'comp_conf': { 'hello': 'world', } }) - hass.stop() - def test_validate_platform_config(self): """Test validating platform configuration.""" platform_schema = PLATFORM_SCHEMA.extend({ @@ -171,24 +170,22 @@ class TestBootstrap(unittest.TestCase): 'platform_conf', MockModule('platform_conf', platform_schema=platform_schema)) - hass = get_test_home_assistant() - - assert not bootstrap._setup_component(hass, 'platform_conf', { + assert not bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': None }) - assert not bootstrap._setup_component(hass, 'platform_conf', { + assert not bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': {} }) - assert not bootstrap._setup_component(hass, 'platform_conf', { + assert not bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'hello': 'world', 'invalid': 'extra', } }) - assert not bootstrap._setup_component(hass, 'platform_conf', { + assert not bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'whatever', 'hello': 'world', @@ -199,18 +196,109 @@ class TestBootstrap(unittest.TestCase): } }) - assert bootstrap._setup_component(hass, 'platform_conf', { + assert bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'whatever', 'hello': 'world', } }) - assert bootstrap._setup_component(hass, 'platform_conf', { + assert bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': [{ 'platform': 'whatever', 'hello': 'world', }] }) - hass.stop() + def test_component_not_found(self): + """setup_component should not crash if component doesn't exist.""" + assert not bootstrap.setup_component(self.hass, 'non_existing') + + def test_component_not_double_initialized(self): + """Test we do not setup a component twice.""" + + mock_setup = mock.MagicMock() + + loader.set_component('comp', MockModule('comp', setup=mock_setup)) + + assert bootstrap.setup_component(self.hass, 'comp') + assert mock_setup.called + + mock_setup.reset_mock() + + assert bootstrap.setup_component(self.hass, 'comp') + assert not mock_setup.called + + @mock.patch('homeassistant.util.package.install_package', + return_value=False) + def test_component_not_installed_if_requirement_fails(self, mock_install): + """Component setup should fail if requirement can't install.""" + loader.set_component( + 'comp', MockModule('comp', requirements=['package==0.0.1'])) + + assert not bootstrap.setup_component(self.hass, 'comp') + assert 'comp' not in self.hass.config.components + + def test_component_not_setup_twice_if_loaded_during_other_setup(self): + """ + Test component that gets setup while waiting for lock is not setup + twice. + """ + loader.set_component('comp', MockModule('comp')) + + result = [] + + def setup_component(): + result.append(bootstrap.setup_component(self.hass, 'comp')) + + with bootstrap._SETUP_LOCK: + thread = threading.Thread(target=setup_component) + thread.start() + self.hass.config.components.append('comp') + + thread.join() + + assert len(result) == 1 + assert result[0] + + def test_component_not_setup_missing_dependencies(self): + """Test we do not setup a component if not all dependencies loaded.""" + deps = ['non_existing'] + loader.set_component('comp', MockModule('comp', dependencies=deps)) + + assert not bootstrap._setup_component(self.hass, 'comp', None) + assert 'comp' not in self.hass.config.components + + self.hass.config.components.append('non_existing') + + assert bootstrap._setup_component(self.hass, 'comp', None) + + def test_component_failing_setup(self): + """Test component that fails setup.""" + loader.set_component( + 'comp', MockModule('comp', setup=lambda hass, config: False)) + + assert not bootstrap._setup_component(self.hass, 'comp', None) + assert 'comp' not in self.hass.config.components + + def test_component_exception_setup(self): + """Test component that raises exception during setup.""" + def exception_setup(hass, config): + """Setup that raises exception.""" + raise Exception('fail!') + + loader.set_component('comp', MockModule('comp', setup=exception_setup)) + + assert not bootstrap._setup_component(self.hass, 'comp', None) + assert 'comp' not in self.hass.config.components + + @mock.patch('homeassistant.bootstrap.process_ha_core_config') + def test_home_assistant_core_config_validation(self, mock_process): + """Test if we pass in wrong information for HA conf.""" + # Extensive HA conf validation testing is done in test_config.py + assert None is bootstrap.from_config_dict({ + 'homeassistant': { + 'latitude': 'some string' + } + }) + assert not mock_process.called