mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-11-12 00:23:01 +03:00
91 lines
3.4 KiB
Python
91 lines
3.4 KiB
Python
"""Tests for audio maintainer spawning on runtime camera adds."""
|
|||
|
|
|
||
|
|
import threading
|
||
|
|
import unittest
|
||
|
|
from unittest.mock import MagicMock, patch
|
||
|
|
|
||
|
|
from frigate.config import FrigateConfig
|
||
|
|
from frigate.events.audio import AudioProcessor
|
||
|
|
|
||
|
|
|
||
|
|
def _build_config() -> FrigateConfig:
|
||
|
|
return FrigateConfig(
|
||
|
|
**{
|
||
|
|
"mqtt": {"host": "mqtt"},
|
||
|
|
"cameras": {
|
||
|
|
"front_door": {
|
||
|
|
"ffmpeg": {
|
||
|
|
"inputs": [
|
||
|
|
{
|
||
|
|
"path": "rtsp://10.0.0.1:554/video",
|
||
|
|
"roles": ["detect", "audio"],
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"audio": {"enabled": True},
|
||
|
|
}
|
||
|
|
},
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class TestAudioSpawnIfNeeded(unittest.TestCase):
|
||
|
|
def _processor(self, config: FrigateConfig, camera_metrics: dict) -> AudioProcessor:
|
||
|
|
processor = AudioProcessor.__new__(AudioProcessor)
|
||
|
|
processor.config = config
|
||
|
|
processor.camera_metrics = camera_metrics
|
||
|
|
processor.embeddings_metrics = MagicMock()
|
||
|
|
processor.audio_threads = {}
|
||
|
|
processor.transcription_model_runner = None
|
||
|
|
processor.genai_manager = None
|
||
|
|
processor.stop_event = threading.Event()
|
||
|
|
processor.logger = MagicMock()
|
||
|
|
return processor
|
||
|
|
|
||
|
|
def test_skips_camera_whose_metrics_have_not_been_created_yet(self):
|
||
|
|
"""The camera maintainer creates metrics on its own poll of the add
|
||
|
|
update, so the audio processor can see the camera first."""
|
||
|
|
config = _build_config()
|
||
|
|
processor = self._processor(config, {})
|
||
|
|
|
||
|
|
with patch("frigate.events.audio.AudioEventMaintainer") as maintainer:
|
||
|
|
processor.spawn_if_needed(config.cameras["front_door"])
|
||
|
|
|
||
|
|
maintainer.assert_not_called()
|
||
|
|
assert processor.audio_threads == {}
|
||
|
|
|
||
|
|
def test_spawns_once_metrics_exist_and_passes_the_metrics_object(self):
|
||
|
|
config = _build_config()
|
||
|
|
metrics = object()
|
||
|
|
processor = self._processor(config, {"front_door": metrics})
|
||
|
|
|
||
|
|
with patch("frigate.events.audio.AudioEventMaintainer") as maintainer:
|
||
|
|
processor.spawn_if_needed(config.cameras["front_door"])
|
||
|
|
|
||
|
|
assert maintainer.call_args.args[2] is metrics
|
||
|
|
assert "front_door" in processor.audio_threads
|
||
|
|
maintainer.return_value.start.assert_called_once()
|
||
|
|
|
||
|
|
def test_does_not_respawn_for_a_camera_already_running(self):
|
||
|
|
config = _build_config()
|
||
|
|
processor = self._processor(config, {"front_door": object()})
|
||
|
|
processor.audio_threads["front_door"] = MagicMock()
|
||
|
|
|
||
|
|
with patch("frigate.events.audio.AudioEventMaintainer") as maintainer:
|
||
|
|
processor.spawn_if_needed(config.cameras["front_door"])
|
||
|
|
|
||
|
|
maintainer.assert_not_called()
|
||
|
|
|
||
|
|
def test_skips_camera_whose_ffmpeg_update_has_not_arrived_yet(self):
|
||
|
|
"""The add update carries the camera before its ffmpeg inputs are
|
||
|
|
applied, so the audio role can be briefly missing."""
|
||
|
|
config = _build_config()
|
||
|
|
camera = config.cameras["front_door"]
|
||
|
|
camera.ffmpeg.inputs[0].roles = ["detect"]
|
||
|
|
processor = self._processor(config, {"front_door": object()})
|
||
|
|
|
||
|
|
with patch("frigate.events.audio.AudioEventMaintainer") as maintainer:
|
||
|
|
processor.spawn_if_needed(camera)
|
||
|
|
|
||
|
|
maintainer.assert_not_called()
|