SSDP matching improvements (#28285)

* SSDP matching improvements

- support multiple match groups per domain
- require matches on all, not any item in a group
- support matching on all UPnP device description data

* Manifest structure fixes
This commit is contained in:
Ville Skyttä 2019-11-02 21:30:09 +02:00 committed by Paulus Schoutsen
parent a8dff2f2d0
commit 1679ec3245
10 changed files with 113 additions and 92 deletions

View file

@ -17,7 +17,7 @@ async def test_scan_match_st(hass):
with patch(
"netdisco.ssdp.scan", return_value=[Mock(st="mock-st", location=None)]
), patch.dict(gn_ssdp.SSDP["st"], {"mock-st": ["mock-domain"]}), patch.object(
), patch.dict(gn_ssdp.SSDP, {"mock-domain": [{"st": "mock-st"}]}), patch.object(
hass.config_entries.flow, "async_init", return_value=mock_coro()
) as mock_init:
await scanner.async_scan(None)
@ -27,14 +27,15 @@ async def test_scan_match_st(hass):
assert mock_init.mock_calls[0][2]["context"] == {"source": "ssdp"}
async def test_scan_match_manufacturer(hass, aioclient_mock):
"""Test matching based on ST."""
@pytest.mark.parametrize("key", ("manufacturer", "deviceType"))
async def test_scan_match_upnp_devicedesc(hass, aioclient_mock, key):
"""Test matching based on UPnP device description data."""
aioclient_mock.get(
"http://1.1.1.1",
text="""
text=f"""
<root>
<device>
<manufacturer>Paulus</manufacturer>
<{key}>Paulus</{key}>
</device>
</root>
""",
@ -44,9 +45,7 @@ async def test_scan_match_manufacturer(hass, aioclient_mock):
with patch(
"netdisco.ssdp.scan",
return_value=[Mock(st="mock-st", location="http://1.1.1.1")],
), patch.dict(
gn_ssdp.SSDP["manufacturer"], {"Paulus": ["mock-domain"]}
), patch.object(
), patch.dict(gn_ssdp.SSDP, {"mock-domain": [{key: "Paulus"}]}), patch.object(
hass.config_entries.flow, "async_init", return_value=mock_coro()
) as mock_init:
await scanner.async_scan(None)
@ -56,11 +55,11 @@ async def test_scan_match_manufacturer(hass, aioclient_mock):
assert mock_init.mock_calls[0][2]["context"] == {"source": "ssdp"}
async def test_scan_match_device_type(hass, aioclient_mock):
"""Test matching based on ST."""
async def test_scan_not_all_present(hass, aioclient_mock):
"""Test match fails if some specified attributes are not present."""
aioclient_mock.get(
"http://1.1.1.1",
text="""
text=f"""
<root>
<device>
<deviceType>Paulus</deviceType>
@ -74,15 +73,43 @@ async def test_scan_match_device_type(hass, aioclient_mock):
"netdisco.ssdp.scan",
return_value=[Mock(st="mock-st", location="http://1.1.1.1")],
), patch.dict(
gn_ssdp.SSDP["device_type"], {"Paulus": ["mock-domain"]}
gn_ssdp.SSDP,
{"mock-domain": [{"deviceType": "Paulus", "manufacturer": "Paulus"}]},
), patch.object(
hass.config_entries.flow, "async_init", return_value=mock_coro()
) as mock_init:
await scanner.async_scan(None)
assert len(mock_init.mock_calls) == 1
assert mock_init.mock_calls[0][1][0] == "mock-domain"
assert mock_init.mock_calls[0][2]["context"] == {"source": "ssdp"}
assert not mock_init.mock_calls
async def test_scan_not_all_match(hass, aioclient_mock):
"""Test match fails if some specified attribute values differ."""
aioclient_mock.get(
"http://1.1.1.1",
text=f"""
<root>
<device>
<deviceType>Paulus</deviceType>
<manufacturer>Paulus</manufacturer>
</device>
</root>
""",
)
scanner = ssdp.Scanner(hass)
with patch(
"netdisco.ssdp.scan",
return_value=[Mock(st="mock-st", location="http://1.1.1.1")],
), patch.dict(
gn_ssdp.SSDP,
{"mock-domain": [{"deviceType": "Paulus", "manufacturer": "Not-Paulus"}]},
), patch.object(
hass.config_entries.flow, "async_init", return_value=mock_coro()
) as mock_init:
await scanner.async_scan(None)
assert not mock_init.mock_calls
@pytest.mark.parametrize("exc", [asyncio.TimeoutError, aiohttp.ClientError])