Add diagnostics for rtsp_to_webrtc (#65138)

pull/65145/head
Allen Porter 2022-01-28 09:07:41 -08:00 committed by GitHub
parent d0d55db936
commit 0c9be604c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 168 additions and 92 deletions

View File

@ -0,0 +1,17 @@
"""Diagnostics support for Nest."""
from __future__ import annotations
from typing import Any
from rtsp_to_webrtc import client
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
return dict(client.get_diagnostics())

View File

@ -0,0 +1,98 @@
"""Tests for RTSPtoWebRTC inititalization."""
from __future__ import annotations
from collections.abc import AsyncGenerator, Awaitable, Callable, Generator
from typing import Any, TypeVar
from unittest.mock import patch
import pytest
import rtsp_to_webrtc
from homeassistant.components import camera
from homeassistant.components.rtsp_to_webrtc import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
STREAM_SOURCE = "rtsp://example.com"
SERVER_URL = "http://127.0.0.1:8083"
CONFIG_ENTRY_DATA = {"server_url": SERVER_URL}
# Typing helpers
ComponentSetup = Callable[[], Awaitable[None]]
T = TypeVar("T")
YieldFixture = Generator[T, None, None]
@pytest.fixture(autouse=True)
async def webrtc_server() -> None:
"""Patch client library to force usage of RTSPtoWebRTC server."""
with patch(
"rtsp_to_webrtc.client.WebClient.heartbeat",
side_effect=rtsp_to_webrtc.exceptions.ResponseError(),
):
yield
@pytest.fixture
async def mock_camera(hass) -> AsyncGenerator[None, None]:
"""Initialize a demo camera platform."""
assert await async_setup_component(
hass, "camera", {camera.DOMAIN: {"platform": "demo"}}
)
await hass.async_block_till_done()
with patch(
"homeassistant.components.demo.camera.Path.read_bytes",
return_value=b"Test",
), patch(
"homeassistant.components.camera.Camera.stream_source",
return_value=STREAM_SOURCE,
), patch(
"homeassistant.components.camera.Camera.supported_features",
return_value=camera.SUPPORT_STREAM,
):
yield
@pytest.fixture
async def config_entry_data() -> dict[str, Any]:
"""Fixture for MockConfigEntry data."""
return CONFIG_ENTRY_DATA
@pytest.fixture
async def config_entry(config_entry_data: dict[str, Any]) -> MockConfigEntry:
"""Fixture for MockConfigEntry."""
return MockConfigEntry(domain=DOMAIN, data=config_entry_data)
@pytest.fixture
async def rtsp_to_webrtc_client() -> None:
"""Fixture for mock rtsp_to_webrtc client."""
with patch("rtsp_to_webrtc.client.Client.heartbeat"):
yield
@pytest.fixture
async def setup_integration(
hass: HomeAssistant, config_entry: MockConfigEntry
) -> YieldFixture[ComponentSetup]:
"""Fixture for setting up the component."""
config_entry.add_to_hass(hass)
async def func() -> None:
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
yield func
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
await hass.config_entries.async_unload(entries[0].entry_id)
await hass.async_block_till_done()
assert not hass.data.get(DOMAIN)
assert entries[0].state is ConfigEntryState.NOT_LOADED

View File

@ -0,0 +1,27 @@
"""Test nest diagnostics."""
from typing import Any
from .conftest import ComponentSetup
from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
THERMOSTAT_TYPE = "sdm.devices.types.THERMOSTAT"
async def test_entry_diagnostics(
hass,
hass_client,
config_entry: MockConfigEntry,
rtsp_to_webrtc_client: Any,
setup_integration: ComponentSetup,
):
"""Test config entry diagnostics."""
await setup_integration()
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"discovery": {"attempt": 1, "web.failure": 1, "webrtc.success": 1},
"web": {},
"webrtc": {},
}

View File

@ -3,7 +3,7 @@
from __future__ import annotations
import base64
from collections.abc import AsyncGenerator, Awaitable, Callable
from collections.abc import Awaitable, Callable
from typing import Any
from unittest.mock import patch
@ -11,147 +11,84 @@ import aiohttp
import pytest
import rtsp_to_webrtc
from homeassistant.components import camera
from homeassistant.components.rtsp_to_webrtc import DOMAIN
from homeassistant.components.websocket_api.const import TYPE_RESULT
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
from .conftest import SERVER_URL, STREAM_SOURCE, ComponentSetup
from tests.test_util.aiohttp import AiohttpClientMocker
STREAM_SOURCE = "rtsp://example.com"
# The webrtc component does not inspect the details of the offer and answer,
# and is only a pass through.
OFFER_SDP = "v=0\r\no=carol 28908764872 28908764872 IN IP4 100.3.6.6\r\n..."
ANSWER_SDP = "v=0\r\no=bob 2890844730 2890844730 IN IP4 host.example.com\r\n..."
SERVER_URL = "http://127.0.0.1:8083"
CONFIG_ENTRY_DATA = {"server_url": SERVER_URL}
@pytest.fixture(autouse=True)
async def webrtc_server() -> None:
"""Patch client library to force usage of RTSPtoWebRTC server."""
with patch(
"rtsp_to_webrtc.client.WebClient.heartbeat",
side_effect=rtsp_to_webrtc.exceptions.ResponseError(),
):
yield
@pytest.fixture
async def mock_camera(hass) -> AsyncGenerator[None, None]:
"""Initialize a demo camera platform."""
assert await async_setup_component(
hass, "camera", {camera.DOMAIN: {"platform": "demo"}}
)
await hass.async_block_till_done()
with patch(
"homeassistant.components.demo.camera.Path.read_bytes",
return_value=b"Test",
), patch(
"homeassistant.components.camera.Camera.stream_source",
return_value=STREAM_SOURCE,
), patch(
"homeassistant.components.camera.Camera.supported_features",
return_value=camera.SUPPORT_STREAM,
):
yield
async def async_setup_rtsp_to_webrtc(hass: HomeAssistant) -> None:
"""Set up the component."""
return await async_setup_component(hass, DOMAIN, {})
async def test_setup_success(hass: HomeAssistant) -> None:
async def test_setup_success(
hass: HomeAssistant, rtsp_to_webrtc_client: Any, setup_integration: ComponentSetup
) -> None:
"""Test successful setup and unload."""
config_entry = MockConfigEntry(domain=DOMAIN, data=CONFIG_ENTRY_DATA)
config_entry.add_to_hass(hass)
with patch("rtsp_to_webrtc.client.Client.heartbeat"):
assert await async_setup_rtsp_to_webrtc(hass)
await hass.async_block_till_done()
await setup_integration()
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
assert entries[0].state is ConfigEntryState.LOADED
await hass.config_entries.async_unload(config_entry.entry_id)
await hass.async_block_till_done()
assert not hass.data.get(DOMAIN)
assert config_entry.state is ConfigEntryState.NOT_LOADED
async def test_invalid_config_entry(hass: HomeAssistant) -> None:
@pytest.mark.parametrize("config_entry_data", [{}])
async def test_invalid_config_entry(
hass: HomeAssistant, rtsp_to_webrtc_client: Any, setup_integration: ComponentSetup
) -> None:
"""Test a config entry with missing required fields."""
config_entry = MockConfigEntry(domain=DOMAIN, data={})
config_entry.add_to_hass(hass)
assert await async_setup_rtsp_to_webrtc(hass)
await setup_integration()
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
assert entries[0].state is ConfigEntryState.SETUP_ERROR
async def test_setup_server_failure(hass: HomeAssistant) -> None:
async def test_setup_server_failure(
hass: HomeAssistant, setup_integration: ComponentSetup
) -> None:
"""Test server responds with a failure on startup."""
config_entry = MockConfigEntry(domain=DOMAIN, data=CONFIG_ENTRY_DATA)
config_entry.add_to_hass(hass)
with patch(
"rtsp_to_webrtc.client.Client.heartbeat",
side_effect=rtsp_to_webrtc.exceptions.ResponseError(),
):
assert await async_setup_rtsp_to_webrtc(hass)
await hass.async_block_till_done()
await setup_integration()
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
assert entries[0].state is ConfigEntryState.SETUP_RETRY
await hass.config_entries.async_unload(config_entry.entry_id)
await hass.async_block_till_done()
async def test_setup_communication_failure(hass: HomeAssistant) -> None:
async def test_setup_communication_failure(
hass: HomeAssistant, setup_integration: ComponentSetup
) -> None:
"""Test unable to talk to server on startup."""
config_entry = MockConfigEntry(domain=DOMAIN, data=CONFIG_ENTRY_DATA)
config_entry.add_to_hass(hass)
with patch(
"rtsp_to_webrtc.client.Client.heartbeat",
side_effect=rtsp_to_webrtc.exceptions.ClientError(),
):
assert await async_setup_rtsp_to_webrtc(hass)
await hass.async_block_till_done()
await setup_integration()
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
assert entries[0].state is ConfigEntryState.SETUP_RETRY
await hass.config_entries.async_unload(config_entry.entry_id)
await hass.async_block_till_done()
async def test_offer_for_stream_source(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hass_ws_client: Callable[[...], Awaitable[aiohttp.ClientWebSocketResponse]],
mock_camera: Any,
rtsp_to_webrtc_client: Any,
setup_integration: ComponentSetup,
) -> None:
"""Test successful response from RTSPtoWebRTC server."""
config_entry = MockConfigEntry(domain=DOMAIN, data=CONFIG_ENTRY_DATA)
config_entry.add_to_hass(hass)
with patch("rtsp_to_webrtc.client.Client.heartbeat"):
assert await async_setup_rtsp_to_webrtc(hass)
await hass.async_block_till_done()
await setup_integration()
aioclient_mock.post(
f"{SERVER_URL}/stream",
@ -188,14 +125,11 @@ async def test_offer_failure(
aioclient_mock: AiohttpClientMocker,
hass_ws_client: Callable[[...], Awaitable[aiohttp.ClientWebSocketResponse]],
mock_camera: Any,
rtsp_to_webrtc_client: Any,
setup_integration: ComponentSetup,
) -> None:
"""Test a transient failure talking to RTSPtoWebRTC server."""
config_entry = MockConfigEntry(domain=DOMAIN, data=CONFIG_ENTRY_DATA)
config_entry.add_to_hass(hass)
with patch("rtsp_to_webrtc.client.Client.heartbeat"):
assert await async_setup_rtsp_to_webrtc(hass)
await hass.async_block_till_done()
await setup_integration()
aioclient_mock.post(
f"{SERVER_URL}/stream",