2017-09-24 20:08:58 +00:00
|
|
|
"""Test the UPNP component."""
|
|
|
|
from collections import OrderedDict
|
|
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
|
|
|
from homeassistant.setup import async_setup_component
|
2018-04-12 22:22:52 +00:00
|
|
|
from homeassistant.components.upnp import IP_SERVICE, DATA_UPNP
|
|
|
|
|
|
|
|
|
|
|
|
class MockService(MagicMock):
|
|
|
|
"""Mock upnp IP service."""
|
|
|
|
|
|
|
|
async def add_port_mapping(self, *args, **kwargs):
|
|
|
|
"""Original function."""
|
|
|
|
self.mock_add_port_mapping(*args, **kwargs)
|
|
|
|
|
|
|
|
async def delete_port_mapping(self, *args, **kwargs):
|
|
|
|
"""Original function."""
|
|
|
|
self.mock_delete_port_mapping(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
class MockDevice(MagicMock):
|
|
|
|
"""Mock upnp device."""
|
|
|
|
|
|
|
|
def find_first_service(self, *args, **kwargs):
|
|
|
|
"""Original function."""
|
|
|
|
self._service = MockService()
|
|
|
|
return self._service
|
|
|
|
|
|
|
|
def peep_first_service(self):
|
|
|
|
"""Access Mock first service."""
|
|
|
|
return self._service
|
|
|
|
|
|
|
|
|
|
|
|
class MockResp(MagicMock):
|
|
|
|
"""Mock upnp msearch response."""
|
|
|
|
|
|
|
|
async def get_device(self, *args, **kwargs):
|
|
|
|
"""Original function."""
|
|
|
|
device = MockDevice()
|
|
|
|
service = {'serviceType': IP_SERVICE}
|
|
|
|
device.services = [service]
|
|
|
|
return device
|
2017-09-24 20:08:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2018-04-12 22:22:52 +00:00
|
|
|
def mock_msearch_first(*args, **kwargs):
|
|
|
|
"""Wrapper to async mock function."""
|
|
|
|
async def async_mock_msearch_first(*args, **kwargs):
|
|
|
|
"""Mock msearch_first."""
|
|
|
|
return MockResp(*args, **kwargs)
|
2017-09-24 20:08:58 +00:00
|
|
|
|
2018-04-12 22:22:52 +00:00
|
|
|
with patch('pyupnp_async.msearch_first', new=async_mock_msearch_first):
|
|
|
|
yield
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def mock_async_exception(*args, **kwargs):
|
|
|
|
"""Wrapper to async mock function with exception."""
|
|
|
|
async def async_mock_exception(*args, **kwargs):
|
|
|
|
return Exception
|
|
|
|
|
|
|
|
with patch('pyupnp_async.msearch_first', new=async_mock_exception):
|
|
|
|
yield
|
2017-09-24 20:08:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def mock_local_ip():
|
|
|
|
"""Mock get_local_ip."""
|
|
|
|
with patch('homeassistant.components.upnp.get_local_ip',
|
|
|
|
return_value='192.168.0.10'):
|
|
|
|
yield
|
|
|
|
|
|
|
|
|
2018-04-12 22:22:52 +00:00
|
|
|
async def test_setup_fail_if_no_ip(hass):
|
2017-09-24 20:08:58 +00:00
|
|
|
"""Test setup fails if we can't find a local IP."""
|
|
|
|
with patch('homeassistant.components.upnp.get_local_ip',
|
|
|
|
return_value='127.0.0.1'):
|
2018-04-12 22:22:52 +00:00
|
|
|
result = await async_setup_component(hass, 'upnp', {
|
2017-09-24 20:08:58 +00:00
|
|
|
'upnp': {}
|
|
|
|
})
|
|
|
|
|
|
|
|
assert not result
|
|
|
|
|
|
|
|
|
2018-04-12 22:22:52 +00:00
|
|
|
async def test_setup_fail_if_cannot_select_igd(hass,
|
|
|
|
mock_local_ip,
|
|
|
|
mock_async_exception):
|
2017-09-24 20:08:58 +00:00
|
|
|
"""Test setup fails if we can't find an UPnP IGD."""
|
2018-04-12 22:22:52 +00:00
|
|
|
result = await async_setup_component(hass, 'upnp', {
|
2017-09-24 20:08:58 +00:00
|
|
|
'upnp': {}
|
|
|
|
})
|
|
|
|
|
|
|
|
assert not result
|
|
|
|
|
|
|
|
|
2018-04-12 22:22:52 +00:00
|
|
|
async def test_setup_succeeds_if_specify_ip(hass, mock_msearch_first):
|
2017-09-24 20:08:58 +00:00
|
|
|
"""Test setup succeeds if we specify IP and can't find a local IP."""
|
|
|
|
with patch('homeassistant.components.upnp.get_local_ip',
|
|
|
|
return_value='127.0.0.1'):
|
2018-04-12 22:22:52 +00:00
|
|
|
result = await async_setup_component(hass, 'upnp', {
|
2017-09-24 20:08:58 +00:00
|
|
|
'upnp': {
|
2018-08-23 08:54:02 +00:00
|
|
|
'local_ip': '192.168.0.10',
|
|
|
|
'port_mapping': 'True'
|
2017-09-24 20:08:58 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
assert result
|
2018-04-12 22:22:52 +00:00
|
|
|
mock_service = hass.data[DATA_UPNP].peep_first_service()
|
|
|
|
assert len(mock_service.mock_add_port_mapping.mock_calls) == 1
|
|
|
|
mock_service.mock_add_port_mapping.assert_called_once_with(
|
|
|
|
8123, 8123, '192.168.0.10', 'TCP', desc='Home Assistant')
|
2017-09-24 20:08:58 +00:00
|
|
|
|
|
|
|
|
2018-04-12 22:22:52 +00:00
|
|
|
async def test_no_config_maps_hass_local_to_remote_port(hass,
|
|
|
|
mock_local_ip,
|
|
|
|
mock_msearch_first):
|
2017-09-24 20:08:58 +00:00
|
|
|
"""Test by default we map local to remote port."""
|
2018-04-12 22:22:52 +00:00
|
|
|
result = await async_setup_component(hass, 'upnp', {
|
2018-08-23 08:54:02 +00:00
|
|
|
'upnp': {
|
|
|
|
'port_mapping': 'True'
|
|
|
|
}
|
2017-09-24 20:08:58 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
assert result
|
2018-04-12 22:22:52 +00:00
|
|
|
mock_service = hass.data[DATA_UPNP].peep_first_service()
|
|
|
|
assert len(mock_service.mock_add_port_mapping.mock_calls) == 1
|
|
|
|
mock_service.mock_add_port_mapping.assert_called_once_with(
|
|
|
|
8123, 8123, '192.168.0.10', 'TCP', desc='Home Assistant')
|
2017-09-24 20:08:58 +00:00
|
|
|
|
|
|
|
|
2018-04-12 22:22:52 +00:00
|
|
|
async def test_map_hass_to_remote_port(hass,
|
|
|
|
mock_local_ip,
|
|
|
|
mock_msearch_first):
|
2017-09-24 20:08:58 +00:00
|
|
|
"""Test mapping hass to remote port."""
|
2018-04-12 22:22:52 +00:00
|
|
|
result = await async_setup_component(hass, 'upnp', {
|
2017-09-24 20:08:58 +00:00
|
|
|
'upnp': {
|
2018-08-23 08:54:02 +00:00
|
|
|
'port_mapping': 'True',
|
2017-09-24 20:08:58 +00:00
|
|
|
'ports': {
|
|
|
|
'hass': 1000
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
assert result
|
2018-04-12 22:22:52 +00:00
|
|
|
mock_service = hass.data[DATA_UPNP].peep_first_service()
|
|
|
|
assert len(mock_service.mock_add_port_mapping.mock_calls) == 1
|
|
|
|
mock_service.mock_add_port_mapping.assert_called_once_with(
|
|
|
|
8123, 1000, '192.168.0.10', 'TCP', desc='Home Assistant')
|
2017-09-24 20:08:58 +00:00
|
|
|
|
|
|
|
|
2018-04-12 22:22:52 +00:00
|
|
|
async def test_map_internal_to_remote_ports(hass,
|
|
|
|
mock_local_ip,
|
|
|
|
mock_msearch_first):
|
2017-09-24 20:08:58 +00:00
|
|
|
"""Test mapping local to remote ports."""
|
|
|
|
ports = OrderedDict()
|
|
|
|
ports['hass'] = 1000
|
|
|
|
ports[1883] = 3883
|
|
|
|
|
2018-04-12 22:22:52 +00:00
|
|
|
result = await async_setup_component(hass, 'upnp', {
|
2017-09-24 20:08:58 +00:00
|
|
|
'upnp': {
|
2018-08-23 08:54:02 +00:00
|
|
|
'port_mapping': 'True',
|
2017-09-24 20:08:58 +00:00
|
|
|
'ports': ports
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
assert result
|
2018-04-12 22:22:52 +00:00
|
|
|
mock_service = hass.data[DATA_UPNP].peep_first_service()
|
|
|
|
assert len(mock_service.mock_add_port_mapping.mock_calls) == 2
|
2017-09-24 20:08:58 +00:00
|
|
|
|
2018-04-12 22:22:52 +00:00
|
|
|
mock_service.mock_add_port_mapping.assert_any_call(
|
|
|
|
8123, 1000, '192.168.0.10', 'TCP', desc='Home Assistant')
|
|
|
|
mock_service.mock_add_port_mapping.assert_any_call(
|
|
|
|
1883, 3883, '192.168.0.10', 'TCP', desc='Home Assistant')
|
2017-09-24 20:08:58 +00:00
|
|
|
|
|
|
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
2018-04-12 22:22:52 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(mock_service.mock_delete_port_mapping.mock_calls) == 2
|
|
|
|
|
|
|
|
mock_service.mock_delete_port_mapping.assert_any_call(1000, 'TCP')
|
|
|
|
mock_service.mock_delete_port_mapping.assert_any_call(3883, 'TCP')
|