"""Utils for bluesound tests.""" import asyncio from typing import Protocol class Etag(Protocol): """Etag protocol.""" etag: str class LongPollingMock[T: Etag]: """Mock long polling methods(status, sync_status).""" def __init__(self, value: T) -> None: """Store value and allows to wait for changes.""" self._value = value self._error: Exception | None = None self._event = asyncio.Event() self._event.set() def trigger(self): """Trigger the event without changing the value.""" self._event.set() def set(self, value: T): """Set the value and notify all waiting.""" self._value = value self._event.set() def set_error(self, error: Exception | None): """Set the error and notify all waiting.""" self._error = error self._event.set() def get(self) -> T: """Get the value without waiting.""" return self._value async def wait(self) -> T: """Wait for the value or error to change.""" await self._event.wait() self._event.clear() return self._value def side_effect(self): """Return the side_effect for mocking.""" last_etag = None async def mock(*args, **kwargs) -> T: nonlocal last_etag if self._error is not None: raise self._error etag = kwargs.get("etag") if etag is None or etag != last_etag: last_etag = self.get().etag return self.get() value = await self.wait() last_etag = value.etag if self._error is not None: raise self._error return value return mock