mirror of
https://github.com/home-assistant/core.git
synced 2025-08-30 18:01:31 +02:00
Add tests
This commit is contained in:
@@ -33,4 +33,6 @@ async def get_dynamic_camera_stream_settings(
|
|||||||
hass: HomeAssistant, entity_id: str
|
hass: HomeAssistant, entity_id: str
|
||||||
) -> DynamicStreamSettings:
|
) -> DynamicStreamSettings:
|
||||||
"""Get dynamic stream settings for a camera entity."""
|
"""Get dynamic stream settings for a camera entity."""
|
||||||
|
if DATA_CAMERA_PREFS not in hass.data:
|
||||||
|
raise HomeAssistantError("Camera integration not set up")
|
||||||
return await hass.data[DATA_CAMERA_PREFS].get_dynamic_stream_settings(entity_id)
|
return await hass.data[DATA_CAMERA_PREFS].get_dynamic_stream_settings(entity_id)
|
||||||
|
76
tests/components/camera/test_helper.py
Normal file
76
tests/components/camera/test_helper.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
"""Test camera helper functions."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.camera.const import DATA_CAMERA_PREFS
|
||||||
|
from homeassistant.components.camera.helper import get_dynamic_camera_stream_settings
|
||||||
|
from homeassistant.components.camera.prefs import (
|
||||||
|
CameraPreferences,
|
||||||
|
DynamicStreamSettings,
|
||||||
|
)
|
||||||
|
from homeassistant.components.stream import Orientation
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_dynamic_camera_stream_settings_missing_prefs(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test get_dynamic_camera_stream_settings when camera prefs are not set up."""
|
||||||
|
with pytest.raises(HomeAssistantError, match="Camera integration not set up"):
|
||||||
|
await get_dynamic_camera_stream_settings(hass, "camera.test")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_dynamic_camera_stream_settings_success(hass: HomeAssistant) -> None:
|
||||||
|
"""Test successful retrieval of dynamic camera stream settings."""
|
||||||
|
# Set up camera preferences
|
||||||
|
prefs = CameraPreferences(hass)
|
||||||
|
await prefs.async_load()
|
||||||
|
hass.data[DATA_CAMERA_PREFS] = prefs
|
||||||
|
|
||||||
|
# Test with default settings
|
||||||
|
settings = await get_dynamic_camera_stream_settings(hass, "camera.test")
|
||||||
|
assert settings.orientation == Orientation.NO_TRANSFORM
|
||||||
|
assert settings.preload_stream is False
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_dynamic_camera_stream_settings_with_custom_orientation(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test get_dynamic_camera_stream_settings with custom orientation set."""
|
||||||
|
# Set up camera preferences
|
||||||
|
prefs = CameraPreferences(hass)
|
||||||
|
await prefs.async_load()
|
||||||
|
hass.data[DATA_CAMERA_PREFS] = prefs
|
||||||
|
|
||||||
|
# Set custom orientation - this requires entity registry
|
||||||
|
# For this test, we'll directly manipulate the internal state
|
||||||
|
# since entity registry setup is complex for a unit test
|
||||||
|
test_settings = DynamicStreamSettings(
|
||||||
|
orientation=Orientation.ROTATE_LEFT, preload_stream=False
|
||||||
|
)
|
||||||
|
prefs._dynamic_stream_settings_by_entity_id["camera.test"] = test_settings
|
||||||
|
|
||||||
|
settings = await get_dynamic_camera_stream_settings(hass, "camera.test")
|
||||||
|
assert settings.orientation == Orientation.ROTATE_LEFT
|
||||||
|
assert settings.preload_stream is False
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_dynamic_camera_stream_settings_with_preload_stream(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test get_dynamic_camera_stream_settings with preload stream enabled."""
|
||||||
|
# Set up camera preferences
|
||||||
|
prefs = CameraPreferences(hass)
|
||||||
|
await prefs.async_load()
|
||||||
|
hass.data[DATA_CAMERA_PREFS] = prefs
|
||||||
|
|
||||||
|
# Set preload stream by directly setting the dynamic stream settings
|
||||||
|
test_settings = DynamicStreamSettings(
|
||||||
|
orientation=Orientation.NO_TRANSFORM, preload_stream=True
|
||||||
|
)
|
||||||
|
prefs._dynamic_stream_settings_by_entity_id["camera.test"] = test_settings
|
||||||
|
|
||||||
|
settings = await get_dynamic_camera_stream_settings(hass, "camera.test")
|
||||||
|
assert settings.orientation == Orientation.NO_TRANSFORM
|
||||||
|
assert settings.preload_stream is True
|
@@ -1,6 +1,6 @@
|
|||||||
"""The tests for the go2rtc component."""
|
"""The tests for the go2rtc component."""
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Awaitable, Callable
|
||||||
import logging
|
import logging
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
@@ -29,6 +29,11 @@ from homeassistant.components.camera import (
|
|||||||
WebRTCSendMessage,
|
WebRTCSendMessage,
|
||||||
async_get_image,
|
async_get_image,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.camera.const import DATA_CAMERA_PREFS
|
||||||
|
from homeassistant.components.camera.prefs import (
|
||||||
|
CameraPreferences,
|
||||||
|
DynamicStreamSettings,
|
||||||
|
)
|
||||||
from homeassistant.components.default_config import DOMAIN as DEFAULT_CONFIG_DOMAIN
|
from homeassistant.components.default_config import DOMAIN as DEFAULT_CONFIG_DOMAIN
|
||||||
from homeassistant.components.go2rtc import HomeAssistant, WebRTCProvider
|
from homeassistant.components.go2rtc import HomeAssistant, WebRTCProvider
|
||||||
from homeassistant.components.go2rtc.const import (
|
from homeassistant.components.go2rtc.const import (
|
||||||
@@ -37,6 +42,7 @@ from homeassistant.components.go2rtc.const import (
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
RECOMMENDED_VERSION,
|
RECOMMENDED_VERSION,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.stream import Orientation
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.const import CONF_URL
|
from homeassistant.const import CONF_URL
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
@@ -696,3 +702,211 @@ async def test_generic_workaround(
|
|||||||
f"ffmpeg:{camera.entity_id}#audio=opus#query=log_level=debug",
|
f"ffmpeg:{camera.entity_id}#audio=opus#query=log_level=debug",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _test_camera_orientation(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
camera: MockCamera,
|
||||||
|
orientation: Orientation,
|
||||||
|
rest_client: AsyncMock,
|
||||||
|
expected_stream_source: str,
|
||||||
|
camera_fn: Callable[[HomeAssistant, MockCamera], Awaitable[None]],
|
||||||
|
) -> None:
|
||||||
|
"""Test camera orientation handling in go2rtc provider."""
|
||||||
|
# Ensure go2rtc provider is initialized
|
||||||
|
assert isinstance(camera._webrtc_provider, WebRTCProvider)
|
||||||
|
|
||||||
|
prefs = CameraPreferences(hass)
|
||||||
|
await prefs.async_load()
|
||||||
|
hass.data[DATA_CAMERA_PREFS] = prefs
|
||||||
|
|
||||||
|
# Set the specific orientation for this test by directly setting the dynamic stream settings
|
||||||
|
test_settings = DynamicStreamSettings(orientation=orientation, preload_stream=False)
|
||||||
|
prefs._dynamic_stream_settings_by_entity_id[camera.entity_id] = test_settings
|
||||||
|
|
||||||
|
# Call the camera function that should trigger stream update
|
||||||
|
await camera_fn(hass, camera)
|
||||||
|
|
||||||
|
# Verify the stream was configured correctly
|
||||||
|
rest_client.streams.add.assert_called_once_with(
|
||||||
|
camera.entity_id,
|
||||||
|
[
|
||||||
|
expected_stream_source,
|
||||||
|
f"ffmpeg:{camera.entity_id}#audio=opus#query=log_level=debug",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _test_camera_orientation_webrtc(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
camera: MockCamera,
|
||||||
|
orientation: Orientation,
|
||||||
|
rest_client: AsyncMock,
|
||||||
|
expected_stream_source: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test camera orientation handling in go2rtc provider on WebRTC stream."""
|
||||||
|
|
||||||
|
# Ensure go2rtc provider is initialized
|
||||||
|
async def camera_fn(hass: HomeAssistant, camera: MockCamera) -> None:
|
||||||
|
"""Mock function to simulate WebRTC offer handling."""
|
||||||
|
receive_message_callback = Mock()
|
||||||
|
await camera.async_handle_async_webrtc_offer(
|
||||||
|
OFFER_SDP, "test_session", receive_message_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
await _test_camera_orientation(
|
||||||
|
hass,
|
||||||
|
camera,
|
||||||
|
orientation,
|
||||||
|
rest_client,
|
||||||
|
expected_stream_source,
|
||||||
|
camera_fn,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _test_camera_orientation_get_image(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
camera: MockCamera,
|
||||||
|
orientation: Orientation,
|
||||||
|
rest_client: AsyncMock,
|
||||||
|
expected_stream_source: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test camera orientation handling in go2rtc provider on get_image."""
|
||||||
|
|
||||||
|
async def camera_fn(hass: HomeAssistant, camera: MockCamera) -> None:
|
||||||
|
"""Mock function to simulate get_image handling."""
|
||||||
|
rest_client.get_jpeg_snapshot.return_value = b"image_bytes"
|
||||||
|
# Get image which should trigger stream update with orientation
|
||||||
|
await async_get_image(hass, camera.entity_id)
|
||||||
|
|
||||||
|
await _test_camera_orientation(
|
||||||
|
hass,
|
||||||
|
camera,
|
||||||
|
orientation,
|
||||||
|
rest_client,
|
||||||
|
expected_stream_source,
|
||||||
|
camera_fn,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("init_integration", "ws_client")
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("orientation", "expected_suffix"),
|
||||||
|
[
|
||||||
|
(Orientation.MIRROR, "#video=h264#audio=copy#raw=-vf hflip"),
|
||||||
|
(Orientation.ROTATE_180, "#video=h264#audio=copy#rotate=180"),
|
||||||
|
(Orientation.FLIP, "#video=h264#audio=copy#raw=-vf vflip"),
|
||||||
|
(
|
||||||
|
Orientation.ROTATE_LEFT_AND_FLIP,
|
||||||
|
"#video=h264#audio=copy#raw=-vf transpose=2,vflip",
|
||||||
|
),
|
||||||
|
(Orientation.ROTATE_LEFT, "#video=h264#audio=copy#rotate=-90"),
|
||||||
|
(
|
||||||
|
Orientation.ROTATE_RIGHT_AND_FLIP,
|
||||||
|
"#video=h264#audio=copy#raw=-vf transpose=1,vflip",
|
||||||
|
),
|
||||||
|
(Orientation.ROTATE_RIGHT, "#video=h264#audio=copy#rotate=90"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"test_fn",
|
||||||
|
[
|
||||||
|
_test_camera_orientation_webrtc,
|
||||||
|
_test_camera_orientation_get_image,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_stream_orientation(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
rest_client: AsyncMock,
|
||||||
|
init_test_integration: MockCamera,
|
||||||
|
orientation: Orientation,
|
||||||
|
expected_suffix: str,
|
||||||
|
test_fn: Callable[
|
||||||
|
[HomeAssistant, MockCamera, Orientation, AsyncMock, str], Awaitable[None]
|
||||||
|
],
|
||||||
|
) -> None:
|
||||||
|
"""Test WebRTC provider applies correct orientation filters."""
|
||||||
|
camera = init_test_integration
|
||||||
|
expected_stream_source = f"ffmpeg:rtsp://stream{expected_suffix}"
|
||||||
|
|
||||||
|
await test_fn(
|
||||||
|
hass,
|
||||||
|
camera,
|
||||||
|
orientation,
|
||||||
|
rest_client,
|
||||||
|
expected_stream_source,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("init_integration", "ws_client")
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("orientation", "stream_source", "expected_go2rtc_stream_source"),
|
||||||
|
[
|
||||||
|
(Orientation.NO_TRANSFORM, "rtsp://stream", "rtsp://stream"),
|
||||||
|
(
|
||||||
|
Orientation.ROTATE_LEFT,
|
||||||
|
"ffmpeg:rtsp://test.stream",
|
||||||
|
"ffmpeg:rtsp://test.stream#video=h264#audio=copy#rotate=-90",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"test_fn",
|
||||||
|
[
|
||||||
|
_test_camera_orientation_webrtc,
|
||||||
|
_test_camera_orientation_get_image,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_stream_orientation_other_cases(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
rest_client: AsyncMock,
|
||||||
|
init_test_integration: MockCamera,
|
||||||
|
orientation: Orientation,
|
||||||
|
stream_source: str,
|
||||||
|
expected_go2rtc_stream_source: str,
|
||||||
|
test_fn: Callable[
|
||||||
|
[HomeAssistant, MockCamera, Orientation, AsyncMock, str], Awaitable[None]
|
||||||
|
],
|
||||||
|
) -> None:
|
||||||
|
"""Test WebRTC provider with no orientation transform."""
|
||||||
|
camera = init_test_integration
|
||||||
|
camera.set_stream_source(stream_source)
|
||||||
|
|
||||||
|
await test_fn(
|
||||||
|
hass,
|
||||||
|
camera,
|
||||||
|
orientation,
|
||||||
|
rest_client,
|
||||||
|
expected_go2rtc_stream_source,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("init_integration", "ws_client")
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"test_fn",
|
||||||
|
[
|
||||||
|
_test_camera_orientation_webrtc,
|
||||||
|
_test_camera_orientation_get_image,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_stream_orientation_with_generic_camera(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
rest_client: AsyncMock,
|
||||||
|
init_test_integration: MockCamera,
|
||||||
|
test_fn: Callable[
|
||||||
|
[HomeAssistant, MockCamera, Orientation, AsyncMock, str], Awaitable[None]
|
||||||
|
],
|
||||||
|
) -> None:
|
||||||
|
"""Test WebRTC provider with orientation and generic camera platform."""
|
||||||
|
camera = init_test_integration
|
||||||
|
camera.set_stream_source("https://test.stream/video.m3u8")
|
||||||
|
|
||||||
|
# Test WebRTC offer handling with generic platform
|
||||||
|
with patch.object(camera.platform.platform_data, "platform_name", "generic"):
|
||||||
|
await test_fn(
|
||||||
|
hass,
|
||||||
|
camera,
|
||||||
|
Orientation.FLIP,
|
||||||
|
rest_client,
|
||||||
|
"ffmpeg:https://test.stream/video.m3u8#video=h264#audio=copy#raw=-vf vflip",
|
||||||
|
)
|
||||||
|
Reference in New Issue
Block a user