Add initial test
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user