Add initial test

This commit is contained in:
Michael Hansen
2024-01-09 12:02:05 -06:00
parent 172a85846c
commit e7dcbf4383
8 changed files with 203 additions and 128 deletions
-11
View File
@@ -1,17 +1,6 @@
"""Shared code for Wyoming satellite tests."""
from wyoming.info import Info, Attribution, Satellite
from wyoming.audio import AudioChunk
WYOMING_INFO = Info(
satellite=Satellite(
name="test satellite",
area="test area",
description="test description",
attribution=Attribution(name="", url=""),
installed=True,
)
)
AUDIO_CHUNK = AudioChunk(
rate=16000, width=2, channels=1, audio=bytes([255] * 960) # 30ms
)
-102
View File
@@ -1,102 +0,0 @@
import asyncio
import logging
import os
from pathlib import Path
import pytest
from wyoming.audio import AudioChunk
from wyoming.wake import Detection
from wyoming.client import AsyncClient
from wyoming.event import Event, async_write_event
from wyoming.server import AsyncServer, AsyncEventHandler
from wyoming_satellite import (
AlwaysStreamingSatellite,
EventSettings,
SatelliteSettings,
MicSettings,
WakeSettings,
)
from .shared import WYOMING_INFO, AUDIO_CHUNK
_LOGGER = logging.getLogger()
class MicEventHandler(AsyncEventHandler):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._is_connected = True
self._task = asyncio.create_task(self.stream_audio())
async def handle_event(self, event: Event) -> bool:
"""Output only."""
return True
async def stream_audio(self) -> None:
while self._is_connected:
await self.write_event(AUDIO_CHUNK.event())
await asyncio.sleep(AUDIO_CHUNK.seconds)
async def disconnect(self) -> None:
self._is_connected = False
await self._task
class WakeEventHandler(AsyncEventHandler):
async def handle_event(self, event: Event) -> bool:
_LOGGER.error(event)
if AudioChunk.is_type(event.type):
await self.write_event(Detection().event())
return True
@pytest.mark.asyncio
async def test_multiple_wakeups(tmp_path: Path) -> None:
mic_socket = tmp_path / "mic.socket"
os.mkfifo(mic_socket)
mic_uri = f"unix://{mic_socket}"
mic_server = AsyncServer.from_uri(mic_uri)
mic_task = asyncio.create_task(mic_server.run(MicEventHandler))
wake_socket = tmp_path / "wake.socket"
os.mkfifo(wake_socket)
wake_uri = f"unix://{wake_socket}"
wake_server = AsyncServer.from_uri(wake_uri)
wake_task = asyncio.create_task(wake_server.run(WakeEventHandler))
wake_event = asyncio.Event()
class EventEventHandler(AsyncEventHandler):
async def handle_event(self, event: Event) -> bool:
if Detection.is_type(event.type):
wake_event.set()
return True
event_socket = tmp_path / "event.socket"
os.mkfifo(event_socket)
event_uri = f"unix://{event_socket}"
event_server = AsyncServer.from_uri(event_uri)
event_task = asyncio.create_task(event_server.run(EventEventHandler))
satellite = AlwaysStreamingSatellite(
SatelliteSettings(
mic=MicSettings(uri=mic_uri),
wake=WakeSettings(uri=wake_uri),
event=EventSettings(uri=event_uri),
)
)
satellite_task = asyncio.create_task(satellite.run())
await asyncio.sleep(0.5)
# async with asyncio.timeout(1):
# await wake_event.wait()
# await satellite.stop()
tasks = [mic_task, wake_task, event_task]
for task in tasks:
task.cancel()
await asyncio.wait(tasks)
+108
View File
@@ -0,0 +1,108 @@
import asyncio
import logging
from pathlib import Path
from typing import Optional
from unittest.mock import patch
import pytest
from wyoming.asr import Transcript
from wyoming.audio import AudioChunk
from wyoming.client import AsyncClient
from wyoming.event import Event
from wyoming.satellite import RunSatellite
from wyoming.wake import Detection
from wyoming_satellite import (
EventSettings,
MicSettings,
SatelliteSettings,
WakeSettings,
WakeStreamingSatellite,
)
from .shared import AUDIO_CHUNK
_LOGGER = logging.getLogger()
class MicClient(AsyncClient):
async def read_event(self) -> Optional[Event]:
# Send 30ms of audio every 30ms
await asyncio.sleep(AUDIO_CHUNK.seconds)
return AUDIO_CHUNK.event()
async def write_event(self, event: Event) -> None:
# Output only
pass
class WakeClient(AsyncClient):
def __init__(self) -> None:
super().__init__()
self._detection_event = asyncio.Event()
async def read_event(self) -> Optional[Event]:
await self._detection_event.wait()
return Detection().event()
async def write_event(self, event: Event) -> None:
if AudioChunk.is_type(event.type):
self._detection_event.set()
class EventClient(AsyncClient):
def __init__(self) -> None:
super().__init__()
self.wake_event = asyncio.Event()
async def read_event(self) -> Optional[Event]:
# Input only
return None
async def write_event(self, event: Event) -> None:
_LOGGER.error(event.type)
if Detection.is_type(event.type):
self.wake_event.set()
@pytest.mark.asyncio
async def test_multiple_wakeups(tmp_path: Path) -> None:
mic_client = MicClient()
wake_client = WakeClient()
event_client = EventClient()
with patch(
"wyoming_satellite.satellite.SatelliteBase._make_mic_client",
return_value=mic_client,
), patch(
"wyoming_satellite.satellite.SatelliteBase._make_wake_client",
return_value=wake_client,
), patch(
"wyoming_satellite.satellite.SatelliteBase._make_event_client",
return_value=event_client,
):
satellite = WakeStreamingSatellite(
SatelliteSettings(
mic=MicSettings(uri="test"),
wake=WakeSettings(uri="test"),
event=EventSettings(uri="test"),
)
)
satellite_task = asyncio.create_task(satellite.run(), name="satellite")
await satellite.event_from_server(RunSatellite().event())
async with asyncio.timeout(1):
await event_client.wake_event.wait()
event_client.wake_event.clear()
# Stop streaming
await satellite.event_from_server(Transcript("test").event())
# Should not trigger again within refractory period (default: 5 sec)
with pytest.raises(asyncio.TimeoutError):
async with asyncio.timeout(0.15):
await event_client.wake_event.wait()
await satellite.stop()
await satellite_task