async def test_parallel_updates_sync_platform_with_constant(hass):
    """Test sync platform can set parallel_updates limit."""
    platform = MockPlatform()
    platform.PARALLEL_UPDATES = 2

    loader.set_component(hass, 'test_domain.platform', platform)

    component = EntityComponent(_LOGGER, DOMAIN, hass)
    component._platforms = {}

    await component.async_setup({
        DOMAIN: {
            'platform': 'platform',
        }
    })

    handle = list(component._platforms.values())[-1]
    assert handle.parallel_updates == 2

    class SyncEntity(MockEntity):
        """Mock entity that has update."""

        async def update(self):
            pass

    entity = SyncEntity()
    await handle.async_add_entities([entity])
    assert entity.parallel_updates is not None
    assert entity.parallel_updates._value == 2
    def test_setup(self):
        # Bogus config
        self.assertFalse(switch.setup(self.hass, {}))

        self.assertFalse(switch.setup(self.hass, {switch.DOMAIN: {}}))

        # Test with non-existing component
        self.assertFalse(switch.setup(
            self.hass, {switch.DOMAIN: {CONF_PLATFORM: 'nonexisting'}}
        ))

        # Test if switch component returns 0 switches
        test_platform = loader.get_component('switch.test')
        test_platform.init(True)

        self.assertEqual(
            [], test_platform.get_switches(None, None))

        self.assertFalse(switch.setup(
            self.hass, {switch.DOMAIN: {CONF_PLATFORM: 'test'}}
        ))

        # Test if we can load 2 platforms
        loader.set_component('switch.test2', test_platform)
        test_platform.init(False)

        self.assertTrue(switch.setup(
            self.hass, {
                switch.DOMAIN: {CONF_PLATFORM: 'test'},
                '{} 2'.format(switch.DOMAIN): {CONF_PLATFORM: 'test2'},
            }
        ))
async def test_parallel_updates_sync_platform(hass):
    """Test sync platform parallel_updates default set to 1."""
    platform = MockPlatform()

    loader.set_component(hass, 'test_domain.platform', platform)

    component = EntityComponent(_LOGGER, DOMAIN, hass)
    component._platforms = {}

    await component.async_setup({
        DOMAIN: {
            'platform': 'platform',
        }
    })

    handle = list(component._platforms.values())[-1]
    assert handle.parallel_updates is None

    class SyncEntity(MockEntity):
        """Mock entity that has update."""

        async def update(self):
            pass

    entity = SyncEntity()
    await handle.async_add_entities([entity])
    assert entity.parallel_updates is not None
    assert entity.parallel_updates._value == 1
def test_create_account(hass, client):
    """Test a flow that creates an account."""
    set_component(
        hass, 'test',
        MockModule('test', async_setup_entry=mock_coro_func(True)))

    class TestFlow(core_ce.ConfigFlow):
        VERSION = 1

        @asyncio.coroutine
        def async_step_user(self, user_input=None):
            return self.async_create_entry(
                title='Test Entry',
                data={'secret': 'account_token'}
            )

    with patch.dict(HANDLERS, {'test': TestFlow}):
        resp = yield from client.post('/api/config/config_entries/flow',
                                      json={'handler': 'test'})

    assert resp.status == 200
    data = yield from resp.json()
    data.pop('flow_id')
    assert data == {
        'handler': 'test',
        'title': 'Test Entry',
        'type': 'create_entry',
        'version': 1,
        'description': None,
        'description_placeholders': None,
    }
def test_remove_entry_raises(manager):
    """Test if a component raises while removing entry."""
    @asyncio.coroutine
    def mock_unload_entry(hass, entry):
        """Mock unload entry function."""
        raise Exception("BROKEN")

    loader.set_component(
        'test',
        MockModule('comp', async_unload_entry=mock_unload_entry))

    MockConfigEntry(domain='test', entry_id='test1').add_to_manager(manager)
    MockConfigEntry(domain='test', entry_id='test2').add_to_manager(manager)
    MockConfigEntry(domain='test', entry_id='test3').add_to_manager(manager)

    assert [item.entry_id for item in manager.async_entries()] == \
        ['test1', 'test2', 'test3']

    result = yield from manager.async_remove('test2')

    assert result == {
        'require_restart': True
    }
    assert [item.entry_id for item in manager.async_entries()] == \
        ['test1', 'test3']
    def test_component_platform_not_found(self):
        """Test errors if component or platform not found."""
        # Make sure they don't exist
        set_component('beer', None)
        set_component('light.beer', None)
        files = {
            'badcomponent.yaml': BASE_CONFIG + 'beer:',
            'badplatform.yaml': BASE_CONFIG + 'light:\n  platform: beer',
        }
        with patch_yaml_files(files):
            res = check_config.check(get_test_config_dir('badcomponent.yaml'))
            change_yaml_files(res)
            self.assertDictEqual({}, res['components'])
            self.assertDictEqual({
                    check_config.ERROR_STR: [
                        'Component not found: beer',
                        'Setup failed for beer: Component not found.']
                }, res['except'])
            self.assertDictEqual({}, res['secret_cache'])
            self.assertDictEqual({}, res['secrets'])
            self.assertListEqual(['.../badcomponent.yaml'], res['yaml_files'])

            res = check_config.check(get_test_config_dir('badplatform.yaml'))
            change_yaml_files(res)
            assert res['components'] == {'light': [], 'group': None}
            assert res['except'] == {
                check_config.ERROR_STR: [
                    'Platform not found: light.beer',
                ]}
            self.assertDictEqual({}, res['secret_cache'])
            self.assertDictEqual({}, res['secrets'])
            self.assertListEqual(['.../badplatform.yaml'], res['yaml_files'])
Example #7
0
    def test_validate_platform_config_4(self):
        """Test entity_namespace in PLATFORM_SCHEMA."""
        component_schema = PLATFORM_SCHEMA_BASE
        platform_schema = PLATFORM_SCHEMA
        loader.set_component(
            self.hass,
            'platform_conf',
            MockModule('platform_conf',
                       platform_schema_base=component_schema))

        loader.set_component(
            self.hass,
            'platform_conf.whatever',
            MockPlatform('whatever',
                         platform_schema=platform_schema))

        with assert_setup_component(1):
            assert setup.setup_component(self.hass, 'platform_conf', {
                'platform_conf': {
                    # pass: entity_namespace accepted by PLATFORM_SCHEMA
                    'platform': 'whatever',
                    'entity_namespace': 'yummy',
                }
            })

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')
Example #8
0
    def test_platform_specific_config_validation(self):
        """Test platform that specifies config."""

        platform_schema = PLATFORM_SCHEMA.extend({
            'valid': True,
        }, extra=vol.PREVENT_EXTRA)

        loader.set_component(
            'switch.platform_a',
            MockPlatform('comp_b', platform_schema=platform_schema))

        assert not bootstrap.setup_component(self.hass, 'switch', {
            'switch': {
                'platform': 'platform_a',
                'invalid': True
            }
        })

        assert not bootstrap.setup_component(self.hass, 'switch', {
            'switch': {
                'platform': 'platform_a',
                'valid': True,
                'invalid_extra': True,
            }
        })

        assert bootstrap.setup_component(self.hass, 'switch', {
            'switch': {
                'platform': 'platform_a',
                'valid': True
            }
        })
Example #9
0
    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
async def test_discovery_notification(hass):
    """Test that we create/dismiss a notification when source is discovery."""
    loader.set_component(hass, 'test', MockModule('test'))
    await async_setup_component(hass, 'persistent_notification', {})

    class TestFlow(config_entries.ConfigFlow):
        VERSION = 5

        async def async_step_discovery(self, user_input=None):
            if user_input is not None:
                return self.async_create_entry(
                    title='Test Title',
                    data={
                        'token': 'abcd'
                    }
                )
            return self.async_show_form(
                step_id='discovery',
            )

    with patch.dict(config_entries.HANDLERS, {'test': TestFlow}):
        result = await hass.config_entries.flow.async_init(
            'test', context={'source': config_entries.SOURCE_DISCOVERY})

    await hass.async_block_till_done()
    state = hass.states.get('persistent_notification.config_entry_discovery')
    assert state is not None

    result = await hass.config_entries.flow.async_configure(
        result['flow_id'], {})
    assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY

    await hass.async_block_till_done()
    state = hass.states.get('persistent_notification.config_entry_discovery')
    assert state is None
async def test_entry_reload_error(hass, manager, state):
    """Test that we can reload an entry."""
    entry = MockConfigEntry(
        domain='comp',
        state=state
    )
    entry.add_to_hass(hass)

    async_setup = MagicMock(return_value=mock_coro(True))
    async_setup_entry = MagicMock(return_value=mock_coro(True))
    async_unload_entry = MagicMock(return_value=mock_coro(True))

    loader.set_component(hass, 'comp', MockModule(
        'comp',
        async_setup=async_setup,
        async_setup_entry=async_setup_entry,
        async_unload_entry=async_unload_entry
    ))

    with pytest.raises(config_entries.OperationNotAllowed):
        assert await manager.async_reload(entry.entry_id)

    assert len(async_unload_entry.mock_calls) == 0
    assert len(async_setup.mock_calls) == 0
    assert len(async_setup_entry.mock_calls) == 0

    assert entry.state == state
async def test_remove_entry_handles_callback_error(hass, manager):
    """Test that exceptions in the remove callback are handled."""
    mock_setup_entry = MagicMock(return_value=mock_coro(True))
    mock_unload_entry = MagicMock(return_value=mock_coro(True))
    mock_remove_entry = MagicMock(
        side_effect=lambda *args, **kwargs: mock_coro())
    loader.set_component(hass, 'test', MockModule(
        'test',
        async_setup_entry=mock_setup_entry,
        async_unload_entry=mock_unload_entry,
        async_remove_entry=mock_remove_entry
    ))
    entry = MockConfigEntry(
        domain='test',
        entry_id='test1',
    )
    entry.add_to_manager(manager)
    # Check all config entries exist
    assert [item.entry_id for item in manager.async_entries()] == \
        ['test1']
    # Setup entry
    await entry.async_setup(hass)
    await hass.async_block_till_done()

    # Remove entry
    result = await manager.async_remove('test1')
    await hass.async_block_till_done()
    # Check that unload went well and so no need to restart
    assert result == {
        'require_restart': False
    }
    # Check the remove callback was invoked.
    assert mock_remove_entry.call_count == 1
    # Check that config entry was removed.
    assert [item.entry_id for item in manager.async_entries()] == []
Example #13
0
    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
async def test_parallel_updates_async_platform(hass):
    """Test async platform does not have parallel_updates limit by default."""
    platform = MockPlatform()

    loader.set_component(hass, 'test_domain.platform', platform)

    component = EntityComponent(_LOGGER, DOMAIN, hass)
    component._platforms = {}

    await component.async_setup({
        DOMAIN: {
            'platform': 'platform',
        }
    })

    handle = list(component._platforms.values())[-1]
    assert handle.parallel_updates is None

    class AsyncEntity(MockEntity):
        """Mock entity that has async_update."""

        async def async_update(self):
            pass

    entity = AsyncEntity()
    await handle.async_add_entities([entity])
    assert entity.parallel_updates is None
    def test_component_platform_not_found(self, isfile_patch):
        """Test errors if component or platform not found."""
        # Make sure they don't exist
        set_component('beer', None)
        files = {
            YAML_CONFIG_FILE: BASE_CONFIG + 'beer:',
        }
        with patch_yaml_files(files):
            res = check_config.check(get_test_config_dir())
            assert res['components'].keys() == {'homeassistant'}
            assert res['except'] == {
                check_config.ERROR_STR: ['Component not found: beer']}
            assert res['secret_cache'] == {}
            assert res['secrets'] == {}
            assert len(res['yaml_files']) == 1

        set_component('light.beer', None)
        files = {
            YAML_CONFIG_FILE: BASE_CONFIG + 'light:\n  platform: beer',
        }
        with patch_yaml_files(files):
            res = check_config.check(get_test_config_dir())
            assert res['components'].keys() == {'homeassistant', 'light'}
            assert res['components']['light'] == []
            assert res['except'] == {
                check_config.ERROR_STR: [
                    'Platform not found: light.beer',
                ]}
            assert res['secret_cache'] == {}
            assert res['secrets'] == {}
            assert len(res['yaml_files']) == 1
    def test_setup_recovers_when_setup_raises(self):
        """Test the setup if exceptions are happening."""
        platform1_setup = Mock(side_effect=Exception("Broken"))
        platform2_setup = Mock(return_value=None)

        loader.set_component("test_domain.mod1", MockPlatform(platform1_setup))
        loader.set_component("test_domain.mod2", MockPlatform(platform2_setup))

        component = EntityComponent(_LOGGER, DOMAIN, self.hass)

        assert not platform1_setup.called
        assert not platform2_setup.called

        component.setup(
            OrderedDict(
                [
                    (DOMAIN, {"platform": "mod1"}),
                    ("{} 2".format(DOMAIN), {"platform": "non_exist"}),
                    ("{} 3".format(DOMAIN), {"platform": "mod2"}),
                ]
            )
        )

        assert platform1_setup.called
        assert platform2_setup.called
    def test_set_entity_namespace_via_config(self):
        """Test setting an entity namespace."""
        def platform_setup(hass, config, add_devices, discovery_info=None):
            """Test the platform setup."""
            add_devices([
                MockEntity(name='beer'),
                MockEntity(name=None),
            ])

        platform = MockPlatform(platform_setup)

        loader.set_component(self.hass, 'test_domain.platform', platform)

        component = EntityComponent(_LOGGER, DOMAIN, self.hass)

        component.setup({
            DOMAIN: {
                'platform': 'platform',
                'entity_namespace': 'yummy'
            }
        })

        self.hass.block_till_done()

        assert sorted(self.hass.states.entity_ids()) == \
            ['test_domain.yummy_beer', 'test_domain.yummy_unnamed_device']
Example #18
0
def test_component_dependencies(hass):
    """Test if we can get the proper load order of components."""
    loader.set_component(hass, 'mod1', MockModule('mod1'))
    loader.set_component(hass, 'mod2', MockModule('mod2', ['mod1']))
    loader.set_component(hass, 'mod3', MockModule('mod3', ['mod2']))

    assert {'mod1', 'mod2', 'mod3'} == \
        loader.component_dependencies(hass, 'mod3')

    # Create circular dependency
    loader.set_component(hass, 'mod1', MockModule('mod1', ['mod3']))

    with pytest.raises(loader.CircularDependency):
        print(loader.component_dependencies(hass, 'mod3'))

    # Depend on non-existing component
    loader.set_component(hass, 'mod1',
                         MockModule('mod1', ['nonexisting']))

    with pytest.raises(loader.ComponentNotFound):
        print(loader.component_dependencies(hass, 'mod1'))

    # Try to get dependencies for non-existing component
    with pytest.raises(loader.ComponentNotFound):
        print(loader.component_dependencies(hass, 'nonexisting'))
def test_pararell_updates_async_platform_with_constant(hass):
    """Warn we log when platform setup takes a long time."""
    platform = MockPlatform()

    @asyncio.coroutine
    def mock_update(*args, **kwargs):
        pass

    platform.async_setup_platform = mock_update
    platform.PARALLEL_UPDATES = 1

    loader.set_component('test_domain.platform', platform)

    component = EntityComponent(_LOGGER, DOMAIN, hass)
    component._platforms = {}

    yield from component.async_setup({
        DOMAIN: {
            'platform': 'platform',
        }
    })

    handle = list(component._platforms.values())[-1]

    assert handle.parallel_updates is not None
def test_add_entry_calls_setup_entry(hass, manager):
    """Test we call setup_config_entry."""
    mock_setup_entry = MagicMock(return_value=mock_coro(True))

    loader.set_component(
        'comp',
        MockModule('comp', async_setup_entry=mock_setup_entry))

    class TestFlow(data_entry_flow.FlowHandler):

        VERSION = 1

        @asyncio.coroutine
        def async_step_init(self, user_input=None):
            return self.async_create_entry(
                title='title',
                data={
                    'token': 'supersecret'
                })

    with patch.dict(config_entries.HANDLERS, {'comp': TestFlow, 'beer': 5}):
        yield from manager.flow.async_init('comp')
        yield from hass.async_block_till_done()

    assert len(mock_setup_entry.mock_calls) == 1
    p_hass, p_entry = mock_setup_entry.mock_calls[0][1]

    assert p_hass is hass
    assert p_entry.data == {
        'token': 'supersecret'
    }
Example #21
0
    def test_validate_component_config(self):
        """Test validating component configuration."""
        config_schema = vol.Schema({
            'comp_conf': {
                'hello': str
            }
        }, required=True)
        loader.set_component(
            'comp_conf', MockModule('comp_conf', config_schema=config_schema))

        assert not bootstrap._setup_component(self.hass, 'comp_conf', {})

        assert not bootstrap._setup_component(self.hass, 'comp_conf', {
            'comp_conf': None
        })

        assert not bootstrap._setup_component(self.hass, 'comp_conf', {
            'comp_conf': {}
        })

        assert not bootstrap._setup_component(self.hass, 'comp_conf', {
            'comp_conf': {
                'hello': 'world',
                'invalid': 'extra',
            }
        })

        assert bootstrap._setup_component(self.hass, 'comp_conf', {
            'comp_conf': {
                'hello': 'world',
            }
        })
async def test_saving_and_loading(hass):
    """Test that we're saving and loading correctly."""
    loader.set_component(
        hass, 'test',
        MockModule('test', async_setup_entry=lambda *args: mock_coro(True)))

    class TestFlow(config_entries.ConfigFlow):
        VERSION = 5
        CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL

        @asyncio.coroutine
        def async_step_user(self, user_input=None):
            return self.async_create_entry(
                title='Test Title',
                data={
                    'token': 'abcd'
                }
            )

    with patch.dict(config_entries.HANDLERS, {'test': TestFlow}):
        await hass.config_entries.flow.async_init(
            'test', context={'source': config_entries.SOURCE_USER})

    class Test2Flow(config_entries.ConfigFlow):
        VERSION = 3
        CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_PUSH

        @asyncio.coroutine
        def async_step_user(self, user_input=None):
            return self.async_create_entry(
                title='Test 2 Title',
                data={
                    'username': '******'
                }
            )

    with patch('homeassistant.config_entries.HANDLERS.get',
               return_value=Test2Flow):
        await hass.config_entries.flow.async_init(
            'test', context={'source': config_entries.SOURCE_USER})

    # To trigger the call_later
    async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=1))
    # To execute the save
    await hass.async_block_till_done()

    # Now load written data in new config manager
    manager = config_entries.ConfigEntries(hass, {})
    await manager.async_initialize()

    # Ensure same order
    for orig, loaded in zip(hass.config_entries.async_entries(),
                            manager.async_entries()):
        assert orig.version == loaded.version
        assert orig.domain == loaded.domain
        assert orig.title == loaded.title
        assert orig.data == loaded.data
        assert orig.source == loaded.source
        assert orig.connection_class == loaded.connection_class
def test_two_step_flow(hass, client):
    """Test we can finish a two step flow."""
    set_component(
        hass, 'test',
        MockModule('test', async_setup_entry=mock_coro_func(True)))

    class TestFlow(core_ce.ConfigFlow):
        VERSION = 1

        @asyncio.coroutine
        def async_step_user(self, user_input=None):
            return self.async_show_form(
                step_id='account',
                data_schema=vol.Schema({
                    'user_title': str
                }))

        @asyncio.coroutine
        def async_step_account(self, user_input=None):
            return self.async_create_entry(
                title=user_input['user_title'],
                data={'secret': 'account_token'}
            )

    with patch.dict(HANDLERS, {'test': TestFlow}):
        resp = yield from client.post('/api/config/config_entries/flow',
                                      json={'handler': 'test'})
        assert resp.status == 200
        data = yield from resp.json()
        flow_id = data.pop('flow_id')
        assert data == {
            'type': 'form',
            'handler': 'test',
            'step_id': 'account',
            'data_schema': [
                {
                    'name': 'user_title',
                    'type': 'string'
                }
            ],
            'description_placeholders': None,
            'errors': None
        }

    with patch.dict(HANDLERS, {'test': TestFlow}):
        resp = yield from client.post(
            '/api/config/config_entries/flow/{}'.format(flow_id),
            json={'user_title': 'user-title'})
        assert resp.status == 200
        data = yield from resp.json()
        data.pop('flow_id')
        assert data == {
            'handler': 'test',
            'type': 'create_entry',
            'title': 'user-title',
            'version': 1,
            'description': None,
            'description_placeholders': None,
        }
Example #24
0
    def test_validate_platform_config(self):
        """Test validating platform configuration."""
        platform_schema = PLATFORM_SCHEMA.extend({
            'hello': str,
        })
        loader.set_component(
            'platform_conf',
            MockModule('platform_conf', platform_schema=platform_schema))

        loader.set_component(
            'platform_conf.whatever', MockPlatform('whatever'))

        assert not bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': None
        })

        assert not bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': {}
        })

        assert not bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': {
                'hello': 'world',
                'invalid': 'extra',
            }
        })

        assert not bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': {
                'platform': 'whatever',
                'hello': 'world',
            },

            'platform_conf 2': {
                'invalid': True
            }
        })

        assert not bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': {
                'platform': 'not_existing',
                'hello': 'world',
            }
        })

        assert bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': {
                'platform': 'whatever',
                'hello': 'world',
            }
        })

        assert bootstrap._setup_component(self.hass, 'platform_conf', {
            'platform_conf': [{
                'platform': 'whatever',
                'hello': 'world',
            }]
        })
def test_component_cannot_depend_config(hass):
    """Test config is not allowed to be a dependency."""
    loader.set_component(
        'test_component1',
        MockModule('test_component1', dependencies=['config']))

    with pytest.raises(HomeAssistantError):
        yield from bootstrap.async_from_config_dict(
            {'test_component1': None}, hass)
def test_platform_cannot_depend_config():
    """Test config is not allowed to be a dependency."""
    loader.set_component(
        'test_component1.test',
        MockPlatform('whatever', dependencies=['config']))

    with pytest.raises(HomeAssistantError):
        yield from bootstrap.async_prepare_setup_platform(
            mock.MagicMock(), {}, 'test_component1', 'test')
Example #27
0
    def test_circular_import(self):
        """Test we don't break doing circular import.

        This test will have test_component discover the switch.test_circular
        component while setting up.

        The supplied config will load test_component and will load
        switch.test_circular.

        That means that after startup, we will have test_component and switch
        setup. The test_circular platform has been loaded twice.
        """
        component_calls = []
        platform_calls = []

        def component_setup(hass, config):
            """Setup mock component."""
            discovery.load_platform(hass, 'switch', 'test_circular', 'disc',
                                    config)
            component_calls.append(1)
            return True

        def setup_platform(hass, config, add_devices_callback,
                           discovery_info=None):
            """Setup mock platform."""
            platform_calls.append('disc' if discovery_info else 'component')

        loader.set_component(
            'test_component',
            MockModule('test_component', setup=component_setup))

        loader.set_component(
            'switch.test_circular',
            MockPlatform(setup_platform,
                         dependencies=['test_component']))

        bootstrap.setup_component(self.hass, 'test_component', {
            'test_component': None,
            'switch': [{
                'platform': 'test_circular',
            }],
        })

        # We wait for the setup_lock to finish
        run_coroutine_threadsafe(
            self.hass.data['setup_lock'].acquire(), self.hass.loop).result()

        self.hass.block_till_done()

        # test_component will only be setup once
        assert len(component_calls) == 1
        # The platform will be setup once via the config in `setup_component`
        # and once via the discovery inside test_component.
        assert len(platform_calls) == 2
        assert 'test_component' in self.hass.config.components
        assert 'switch' in self.hass.config.components
Example #28
0
    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
Example #29
0
def test_platform_no_warn_slow(hass):
    """Do not warn for long entity setup time."""
    loader.set_component(
        'test_component1',
        MockModule('test_component1', platform_schema=PLATFORM_SCHEMA))
    with mock.patch.object(hass.loop, 'call_later', mock.MagicMock()) \
            as mock_call:
        result = yield from setup.async_setup_component(
            hass, 'test_component1', {})
        assert result
        assert not mock_call.called
Example #30
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)
Example #31
0
    def test_all_work_done_before_start(self):
        """Test all init work done till start."""
        call_order = []

        def component1_setup(hass, config):
            """Setup mock component."""
            discovery.discover(hass,
                               'test_component2',
                               component='test_component2')
            discovery.discover(hass,
                               'test_component3',
                               component='test_component3')
            return True

        def component_track_setup(hass, config):
            """Setup mock component."""
            call_order.append(1)
            return True

        loader.set_component(
            'test_component1',
            MockModule('test_component1', setup=component1_setup))

        loader.set_component(
            'test_component2',
            MockModule('test_component2', setup=component_track_setup))

        loader.set_component(
            'test_component3',
            MockModule('test_component3', setup=component_track_setup))

        @callback
        def track_start(event):
            """Track start event."""
            call_order.append(2)

        self.hass.bus.listen_once(EVENT_HOMEASSISTANT_START, track_start)

        self.hass.add_job(
            setup.async_setup_component(self.hass, 'test_component1', {}))
        self.hass.block_till_done()
        self.hass.start()
        assert call_order == [1, 1, 2]
Example #32
0
def test_setup_dependencies_platform(hass):
    """Test we setup the dependencies of a platform.

    We're explictely testing that we process dependencies even if a component
    with the same name has already been loaded.
    """
    loader.set_component('test_component', MockModule('test_component'))
    loader.set_component('test_component2', MockModule('test_component2'))
    loader.set_component(
        'test_domain.test_component',
        MockPlatform(dependencies=['test_component', 'test_component2']))

    component = EntityComponent(_LOGGER, DOMAIN, hass)

    yield from async_setup_component(hass, 'test_component', {})

    yield from component.async_setup(
        {DOMAIN: {
            'platform': 'test_component',
        }})

    assert 'test_component' in hass.config.components
    assert 'test_component2' in hass.config.components
    assert 'test_domain.test_component' in hass.config.components
Example #33
0
def _async_setup_component(hass: core.HomeAssistant, domain: str,
                           config) -> bool:
    """Set up a component for Home Assistant.

    This method is a coroutine.
    """
    def log_error(msg, link=True):
        """Log helper."""
        _LOGGER.error("Setup failed for %s: %s", domain, msg)
        async_notify_setup_error(hass, domain, link)

    component = loader.get_component(domain)

    if not component:
        log_error("Component not found.", False)
        return False

    # Validate no circular dependencies
    components = loader.load_order_component(domain)

    # OrderedSet is empty if component or dependencies could not be resolved
    if not components:
        log_error("Unable to resolve component or dependencies.")
        return False

    processed_config = \
        conf_util.async_process_component_config(hass, config, domain)

    if processed_config is None:
        log_error("Invalid config.")
        return False

    if not hass.config.skip_pip and hasattr(component, 'REQUIREMENTS'):
        req_success = yield from _async_process_requirements(
            hass, domain, component.REQUIREMENTS)
        if not req_success:
            log_error("Could not install all requirements.")
            return False

    if hasattr(component, 'DEPENDENCIES'):
        dep_success = yield from _async_process_dependencies(
            hass, config, domain, component.DEPENDENCIES)

        if not dep_success:
            log_error("Could not setup all dependencies.")
            return False

    async_comp = hasattr(component, 'async_setup')

    _LOGGER.info("Setting up %s", domain)
    warn_task = hass.loop.call_later(SLOW_SETUP_WARNING, _LOGGER.warning,
                                     "Setup of %s is taking over %s seconds.",
                                     domain, SLOW_SETUP_WARNING)

    try:
        if async_comp:
            result = yield from component.async_setup(hass, processed_config)
        else:
            result = yield from hass.loop.run_in_executor(
                None, component.setup, hass, processed_config)
    except Exception:  # pylint: disable=broad-except
        _LOGGER.exception("Error during setup of component %s", domain)
        async_notify_setup_error(hass, domain, True)
        return False
    finally:
        warn_task.cancel()

    if result is False:
        log_error("Component failed to initialize.")
        return False
    elif result is not True:
        log_error("Component did not return boolean if setup was successful. "
                  "Disabling component.")
        loader.set_component(domain, None)
        return False

    hass.config.components.add(component.DOMAIN)

    # Cleanup
    if domain in hass.data[DATA_SETUP]:
        hass.data[DATA_SETUP].pop(domain)

    hass.bus.async_fire(EVENT_COMPONENT_LOADED,
                        {ATTR_COMPONENT: component.DOMAIN})

    return True
Example #34
0
def _setup_component(hass: core.HomeAssistant, domain: str, config) -> bool:
    """Setup a component for Home Assistant."""
    # pylint: disable=too-many-return-statements,too-many-branches
    # pylint: disable=too-many-statements
    if domain in hass.config.components:
        return True

    with _SETUP_LOCK:
        # It might have been loaded while waiting for lock
        if domain in hass.config.components:
            return True

        if domain in _CURRENT_SETUP:
            _LOGGER.error('Attempt made to setup %s during setup of %s',
                          domain, domain)
            return False

        component = loader.get_component(domain)
        missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', [])
                        if dep not in hass.config.components]

        if missing_deps:
            _LOGGER.error(
                'Not initializing %s because not all dependencies loaded: %s',
                domain, ", ".join(missing_deps))
            return False

        if hasattr(component, 'CONFIG_SCHEMA'):
            try:
                config = component.CONFIG_SCHEMA(config)
            except vol.MultipleInvalid as ex:
                _log_exception(ex, domain, config)
                return False

        elif hasattr(component, 'PLATFORM_SCHEMA'):
            platforms = []
            for p_name, p_config in config_per_platform(config, domain):
                # Validate component specific platform schema
                try:
                    p_validated = component.PLATFORM_SCHEMA(p_config)
                except vol.MultipleInvalid as ex:
                    _log_exception(ex, domain, p_config)
                    return False

                # Not all platform components follow same pattern for platforms
                # So if p_name is None we are not going to validate platform
                # (the automation component is one of them)
                if p_name is None:
                    platforms.append(p_validated)
                    continue

                platform = prepare_setup_platform(hass, config, domain,
                                                  p_name)

                if platform is None:
                    return False

                # Validate platform specific schema
                if hasattr(platform, 'PLATFORM_SCHEMA'):
                    try:
                        p_validated = platform.PLATFORM_SCHEMA(p_validated)
                    except vol.MultipleInvalid as ex:
                        _log_exception(ex, '{}.{}'.format(domain, p_name),
                                       p_validated)
                        return False

                platforms.append(p_validated)

            # Create a copy of the configuration with all config for current
            # component removed and add validated config back in.
            filter_keys = extract_domain_configs(config, domain)
            config = {key: value for key, value in config.items()
                      if key not in filter_keys}
            config[domain] = platforms

        if not _handle_requirements(hass, component, domain):
            return False

        _CURRENT_SETUP.append(domain)

        try:
            result = component.setup(hass, config)
            if result is False:
                _LOGGER.error('component %s failed to initialize', domain)
                return False
            elif result is not True:
                _LOGGER.error('component %s did not return boolean if setup '
                              'was successful. Disabling component.', domain)
                loader.set_component(domain, None)
                return False
        except Exception:  # pylint: disable=broad-except
            _LOGGER.exception('Error during setup of component %s', domain)
            return False
        finally:
            _CURRENT_SETUP.remove(domain)

        hass.config.components.append(component.DOMAIN)

        # Assumption: if a component does not depend on groups
        # it communicates with devices
        if group.DOMAIN not in getattr(component, 'DEPENDENCIES', []):
            hass.pool.add_worker()

        hass.bus.fire(
            EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN})

        return True
Example #35
0
def test_set_component(hass):
    """Test if set_component works."""
    comp = object()
    loader.set_component(hass, 'switch.test_set', comp)

    assert loader.get_component(hass, 'switch.test_set') is comp
Example #36
0
async def test_remove_entry(hass, manager):
    """Test that we can remove an entry."""
    async def mock_setup_entry(hass, entry):
        """Mock setting up entry."""
        hass.loop.create_task(
            hass.config_entries.async_forward_entry_setup(entry, 'light'))
        return True

    async def mock_unload_entry(hass, entry):
        """Mock unloading an entry."""
        result = await hass.config_entries.async_forward_entry_unload(
            entry, 'light')
        assert result
        return result

    entity = MockEntity(
        unique_id='1234',
        name='Test Entity',
    )

    async def mock_setup_entry_platform(hass, entry, async_add_entities):
        """Mock setting up platform."""
        async_add_entities([entity])

    loader.set_component(
        hass, 'test',
        MockModule('test',
                   async_setup_entry=mock_setup_entry,
                   async_unload_entry=mock_unload_entry))
    loader.set_component(
        hass, 'light.test',
        MockPlatform(async_setup_entry=mock_setup_entry_platform))

    MockConfigEntry(domain='test', entry_id='test1').add_to_manager(manager)
    entry = MockConfigEntry(
        domain='test',
        entry_id='test2',
    )
    entry.add_to_manager(manager)
    MockConfigEntry(domain='test', entry_id='test3').add_to_manager(manager)

    # Check all config entries exist
    assert [item.entry_id for item in manager.async_entries()] == \
        ['test1', 'test2', 'test3']

    # Setup entry
    await entry.async_setup(hass)
    await hass.async_block_till_done()

    # Check entity state got added
    assert hass.states.get('light.test_entity') is not None
    # Group all_lights, light.test_entity
    assert len(hass.states.async_all()) == 2

    # Check entity got added to entity registry
    ent_reg = await hass.helpers.entity_registry.async_get_registry()
    assert len(ent_reg.entities) == 1
    entity_entry = list(ent_reg.entities.values())[0]
    assert entity_entry.config_entry_id == entry.entry_id

    # Remove entry
    result = await manager.async_remove('test2')
    await hass.async_block_till_done()

    # Check that unload went well and so no need to restart
    assert result == {'require_restart': False}

    # Check that config entry was removed.
    assert [item.entry_id for item in manager.async_entries()] == \
        ['test1', 'test3']

    # Check that entity state has been removed
    assert hass.states.get('light.test_entity') is None
    # Just Group all_lights
    assert len(hass.states.async_all()) == 1

    # Check that entity registry entry no longer references config_entry_id
    entity_entry = list(ent_reg.entities.values())[0]
    assert entity_entry.config_entry_id is None
Example #37
0
    def test_set_component(self):
        """ Test if set_component works. """
        loader.set_component('switch.test', mock_toggledevice_platform)

        self.assertEqual(
            mock_toggledevice_platform, loader.get_component('switch.test'))
    def test_set_component(self):
        """Test if set_component works."""
        loader.set_component('switch.test_set', http)

        self.assertEqual(http, loader.get_component('switch.test_set'))
 def setUp(self):  # pylint: disable=invalid-name
     self.hass = get_test_home_assistant()
     loader.prepare(self.hass)
     loader.set_component('light.test', mock_toggledevice_platform)
Example #40
0
def _async_setup_component(hass: core.HomeAssistant, domain: str,
                           config) -> bool:
    """Setup a component for Home Assistant.

    This method is a coroutine.
    """
    # pylint: disable=too-many-return-statements
    if domain in hass.config.components:
        return True

    setup_lock = hass.data.get('setup_lock')
    if setup_lock is None:
        setup_lock = hass.data['setup_lock'] = asyncio.Lock(loop=hass.loop)

    setup_progress = hass.data.get('setup_progress')
    if setup_progress is None:
        setup_progress = hass.data['setup_progress'] = []

    if domain in setup_progress:
        _LOGGER.error('Attempt made to setup %s during setup of %s', domain,
                      domain)
        _async_persistent_notification(hass, domain, True)
        return False

    try:
        # Used to indicate to discovery that a setup is ongoing and allow it
        # to wait till it is done.
        did_lock = False
        if not setup_lock.locked():
            yield from setup_lock.acquire()
            did_lock = True

        setup_progress.append(domain)
        config = yield from async_prepare_setup_component(hass, config, domain)

        if config is None:
            return False

        component = loader.get_component(domain)
        if component is None:
            _async_persistent_notification(hass, domain)
            return False

        async_comp = hasattr(component, 'async_setup')

        try:
            _LOGGER.info("Setting up %s", domain)
            if async_comp:
                result = yield from component.async_setup(hass, config)
            else:
                result = yield from hass.loop.run_in_executor(
                    None, component.setup, hass, config)
        except Exception:  # pylint: disable=broad-except
            _LOGGER.exception('Error during setup of component %s', domain)
            _async_persistent_notification(hass, domain, True)
            return False

        if result is False:
            _LOGGER.error('component %s failed to initialize', domain)
            _async_persistent_notification(hass, domain, True)
            return False
        elif result is not True:
            _LOGGER.error(
                'component %s did not return boolean if setup '
                'was successful. Disabling component.', domain)
            _async_persistent_notification(hass, domain, True)
            loader.set_component(domain, None)
            return False

        hass.config.components.append(component.DOMAIN)

        hass.bus.async_fire(EVENT_COMPONENT_LOADED,
                            {ATTR_COMPONENT: component.DOMAIN})

        return True
    finally:
        setup_progress.remove(domain)
        if did_lock:
            setup_lock.release()
Example #41
0
async def test_two_step_options_flow(hass, client):
    """Test we can finish a two step options flow."""
    set_component(hass, 'test',
                  MockModule('test', async_setup_entry=mock_coro_func(True)))

    class TestFlow(core_ce.ConfigFlow):
        @staticmethod
        @callback
        def async_get_options_flow(config, options):
            class OptionsFlowHandler(data_entry_flow.FlowHandler):
                def __init__(self, config, options):
                    self.config = config
                    self.options = options

                async def async_step_init(self, user_input=None):
                    return self.async_show_form(step_id='finish',
                                                data_schema=vol.Schema(
                                                    {'enabled': bool}))

                async def async_step_finish(self, user_input=None):
                    return self.async_create_entry(title='Enable disable',
                                                   data=user_input)

            return OptionsFlowHandler(config, options)

    MockConfigEntry(
        domain='test',
        entry_id='test1',
        source='bla',
        connection_class=core_ce.CONN_CLASS_LOCAL_POLL,
    ).add_to_hass(hass)
    entry = hass.config_entries._entries[0]

    with patch.dict(HANDLERS, {'test': TestFlow}):
        url = '/api/config/config_entries/entry/option/flow'
        resp = await client.post(url, json={'handler': entry.entry_id})

        assert resp.status == 200
        data = await resp.json()
        flow_id = data.pop('flow_id')
        assert data == {
            'type': 'form',
            'handler': 'test1',
            'step_id': 'finish',
            'data_schema': [{
                'name': 'enabled',
                'type': 'boolean'
            }],
            'description_placeholders': None,
            'errors': None
        }

    with patch.dict(HANDLERS, {'test': TestFlow}):
        resp = await client.post(
            '/api/config/config_entries/options/flow/{}'.format(flow_id),
            json={'enabled': True})
        assert resp.status == 200
        data = await resp.json()
        data.pop('flow_id')
        assert data == {
            'handler': 'test1',
            'type': 'create_entry',
            'title': 'Enable disable',
            'version': 1,
            'description': None,
            'description_placeholders': None,
        }
Example #42
0
    def test_load_order_components(self):
        loader.set_component('mod1', MockModule('mod1', ['group']))
        loader.set_component('mod2', MockModule('mod2', ['mod1', 'sun']))
        loader.set_component('mod3', MockModule('mod3', ['mod2']))
        loader.set_component('mod4', MockModule('mod4', ['group']))

        self.assertEqual(['group', 'mod4', 'mod1', 'sun', 'mod2', 'mod3'],
                         loader.load_order_components(['mod4', 'mod3',
                                                       'mod2']))

        loader.set_component('mod1', MockModule('mod1'))
        loader.set_component('mod2', MockModule('mod2', ['group']))

        self.assertEqual(['mod1', 'group', 'mod2'],
                         loader.load_order_components(['mod2', 'mod1']))

        # Add a non existing one
        self.assertEqual(['mod1', 'group', 'mod2'],
                         loader.load_order_components(
                             ['mod2', 'nonexisting', 'mod1']))

        # Depend on a non existing one
        loader.set_component('mod1', MockModule('mod1', ['nonexisting']))

        self.assertEqual(['group', 'mod2'],
                         loader.load_order_components(['mod2', 'mod1']))
Example #43
0
def mock_test_component(hass):
    """Ensure a component called 'test' exists."""
    set_component(hass, 'test', MockModule('test'))
def test_saving_and_loading(hass):
    """Test that we're saving and loading correctly."""
    loader.set_component(hass, 'test', MockModule('test'))

    class TestFlow(data_entry_flow.FlowHandler):
        VERSION = 5

        @asyncio.coroutine
        def async_step_init(self, user_input=None):
            return self.async_create_entry(
                title='Test Title',
                data={
                    'token': 'abcd'
                }
            )

    with patch.dict(config_entries.HANDLERS, {'test': TestFlow}):
        yield from hass.config_entries.flow.async_init('test')

    class Test2Flow(data_entry_flow.FlowHandler):
        VERSION = 3

        @asyncio.coroutine
        def async_step_init(self, user_input=None):
            return self.async_create_entry(
                title='Test 2 Title',
                data={
                    'username': '******'
                }
            )

    json_path = 'homeassistant.util.json.open'

    with patch('homeassistant.config_entries.HANDLERS.get',
               return_value=Test2Flow), \
            patch.object(config_entries, 'SAVE_DELAY', 0):
        yield from hass.config_entries.flow.async_init('test')

    with patch(json_path, mock_open(), create=True) as mock_write:
        # To trigger the call_later
        yield from asyncio.sleep(0, loop=hass.loop)
        # To execute the save
        yield from hass.async_block_till_done()

    # Mock open calls are: open file, context enter, write, context leave
    written = mock_write.mock_calls[2][1][0]

    # Now load written data in new config manager
    manager = config_entries.ConfigEntries(hass, {})

    with patch('os.path.isfile', return_value=True), \
            patch(json_path, mock_open(read_data=written), create=True):
        yield from manager.async_load()

    # Ensure same order
    for orig, loaded in zip(hass.config_entries.async_entries(),
                            manager.async_entries()):
        assert orig.version == loaded.version
        assert orig.domain == loaded.domain
        assert orig.title == loaded.title
        assert orig.data == loaded.data
        assert orig.source == loaded.source
Example #45
0
    def test_validate_platform_config(self):
        """Test validating platform configuration."""
        platform_schema = PLATFORM_SCHEMA.extend({
            'hello': str,
        })
        loader.set_component(
            'platform_conf',
            MockModule('platform_conf', platform_schema=platform_schema))

        loader.set_component('platform_conf.whatever',
                             MockPlatform('whatever'))

        with assert_setup_component(0):
            assert bootstrap._setup_component(
                self.hass, 'platform_conf',
                {'platform_conf': {
                    'hello': 'world',
                    'invalid': 'extra',
                }})

        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(1):
            assert bootstrap._setup_component(
                self.hass, 'platform_conf', {
                    'platform_conf': {
                        'platform': 'whatever',
                        'hello': 'world',
                    },
                    'platform_conf 2': {
                        'invalid': True
                    }
                })

        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(0):
            assert bootstrap._setup_component(self.hass, 'platform_conf', {
                'platform_conf': {
                    'platform': 'not_existing',
                    'hello': 'world',
                }
            })

        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(1):
            assert bootstrap._setup_component(self.hass, 'platform_conf', {
                'platform_conf': {
                    'platform': 'whatever',
                    'hello': 'world',
                }
            })

        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(1):
            assert bootstrap._setup_component(self.hass, 'platform_conf', {
                'platform_conf': [{
                    'platform': 'whatever',
                    'hello': 'world',
                }]
            })

        self.hass.config.components.remove('platform_conf')

        # Any falsey platform config will be ignored (None, {}, etc)
        with assert_setup_component(0) as config:
            assert bootstrap._setup_component(self.hass, 'platform_conf',
                                              {'platform_conf': None})
            assert 'platform_conf' in self.hass.config.components
            assert not config['platform_conf']  # empty

            assert bootstrap._setup_component(self.hass, 'platform_conf',
                                              {'platform_conf': {}})
            assert 'platform_conf' in self.hass.config.components
            assert not config['platform_conf']  # empty
Example #46
0
async def _async_setup_component(hass: core.HomeAssistant, domain: str,
                                 config) -> bool:
    """Set up a component for Home Assistant.

    This method is a coroutine.
    """
    def log_error(msg, link=True):
        """Log helper."""
        _LOGGER.error("Setup failed for %s: %s", domain, msg)
        async_notify_setup_error(hass, domain, link)

    component = loader.get_component(hass, domain)

    if not component:
        log_error("Component not found.", False)
        return False

    # Validate no circular dependencies
    components = loader.load_order_component(hass, domain)

    # OrderedSet is empty if component or dependencies could not be resolved
    if not components:
        log_error("Unable to resolve component or dependencies.")
        return False

    processed_config = \
        conf_util.async_process_component_config(hass, config, domain)

    if processed_config is None:
        log_error("Invalid config.")
        return False

    try:
        await async_process_deps_reqs(hass, config, domain, component)
    except HomeAssistantError as err:
        log_error(str(err))
        return False

    start = timer()
    _LOGGER.info("Setting up %s", domain)

    if hasattr(component, 'PLATFORM_SCHEMA'):
        # Entity components have their own warning
        warn_task = None
    else:
        warn_task = hass.loop.call_later(
            SLOW_SETUP_WARNING, _LOGGER.warning,
            "Setup of %s is taking over %s seconds.", domain,
            SLOW_SETUP_WARNING)

    try:
        if hasattr(component, 'async_setup'):
            result = await component.async_setup(  # type: ignore
                hass, processed_config)
        else:
            result = await hass.async_add_job(component.setup, hass,
                                              processed_config)  # type: ignore
    except Exception:  # pylint: disable=broad-except
        _LOGGER.exception("Error during setup of component %s", domain)
        async_notify_setup_error(hass, domain, True)
        return False
    finally:
        end = timer()
        if warn_task:
            warn_task.cancel()
    _LOGGER.info("Setup of domain %s took %.1f seconds.", domain, end - start)

    if result is False:
        log_error("Component failed to initialize.")
        return False
    elif result is not True:
        log_error("Component did not return boolean if setup was successful. "
                  "Disabling component.")
        loader.set_component(hass, domain, None)
        return False

    for entry in hass.config_entries.async_entries(domain):
        await entry.async_setup(hass, component=component)

    hass.config.components.add(component.DOMAIN)  # type: ignore

    # Cleanup
    if domain in hass.data[DATA_SETUP]:
        hass.data[DATA_SETUP].pop(domain)

    hass.bus.async_fire(
        EVENT_COMPONENT_LOADED,
        {ATTR_COMPONENT: component.DOMAIN}  # type: ignore
    )

    return True
Example #47
0
    def test_validate_platform_config(self, caplog):
        """Test validating platform configuration."""
        platform_schema = PLATFORM_SCHEMA.extend({
            'hello': str,
        })
        platform_schema_base = PLATFORM_SCHEMA_BASE.extend({})
        loader.set_component(
            self.hass, 'platform_conf',
            MockModule('platform_conf',
                       platform_schema_base=platform_schema_base))

        loader.set_component(
            self.hass, 'platform_conf.whatever',
            MockPlatform('whatever', platform_schema=platform_schema))

        with assert_setup_component(1):
            assert setup.setup_component(
                self.hass, 'platform_conf', {
                    'platform_conf': {
                        'platform': 'whatever',
                        'hello': 'world',
                        'invalid': 'extra',
                    }
                })
            assert caplog.text.count('Your configuration contains '
                                     'extra keys') == 1

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(2):
            assert setup.setup_component(
                self.hass, 'platform_conf', {
                    'platform_conf': {
                        'platform': 'whatever',
                        'hello': 'world',
                    },
                    'platform_conf 2': {
                        'platform': 'whatever',
                        'invalid': True
                    }
                })
            assert caplog.text.count('Your configuration contains '
                                     'extra keys') == 2

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(0):
            assert setup.setup_component(self.hass, 'platform_conf', {
                'platform_conf': {
                    'platform': 'not_existing',
                    'hello': 'world',
                }
            })

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(1):
            assert setup.setup_component(self.hass, 'platform_conf', {
                'platform_conf': {
                    'platform': 'whatever',
                    'hello': 'world',
                }
            })

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')

        with assert_setup_component(1):
            assert setup.setup_component(self.hass, 'platform_conf', {
                'platform_conf': [{
                    'platform': 'whatever',
                    'hello': 'world',
                }]
            })

        self.hass.data.pop(setup.DATA_SETUP)
        self.hass.config.components.remove('platform_conf')

        # Any falsey platform config will be ignored (None, {}, etc)
        with assert_setup_component(0) as config:
            assert setup.setup_component(self.hass, 'platform_conf',
                                         {'platform_conf': None})
            assert 'platform_conf' in self.hass.config.components
            assert not config['platform_conf']  # empty

            assert setup.setup_component(self.hass, 'platform_conf',
                                         {'platform_conf': {}})
            assert 'platform_conf' in self.hass.config.components
            assert not config['platform_conf']  # empty