memorybridge / tests /test_signal_buffer.py
kimandrew927's picture
Initial Space deployment
1004967
"""Tests for SignalBuffer."""
from __future__ import annotations
import asyncio
import time
import pytest
from memorybridge.core.schemas import (
AffectSignal,
AirSignChar,
GestureSignal,
SignalBufferState,
VisionSnapshot,
)
from memorybridge.vision_path.signal_buffer import SignalBuffer
def make_snapshot(
gesture_class="neutral",
gesture_confidence=1.0,
affect_class="neutral",
affect_confidence=0.9,
air_sign_char=None,
) -> VisionSnapshot:
ts = time.time()
return VisionSnapshot(
timestamp=ts,
gestures=[GestureSignal(gesture_class=gesture_class, confidence=gesture_confidence, timestamp=ts)],
affect=AffectSignal(affect_class=affect_class, confidence=affect_confidence, timestamp=ts),
air_sign_char=AirSignChar(character=air_sign_char, confidence=0.9, timestamp=ts) if air_sign_char else None,
)
class TestSignalBuffer:
@pytest.mark.asyncio
async def test_push_and_size(self):
buf = SignalBuffer(buffer_size=4)
await buf.push(make_snapshot())
assert len(buf._buffer) == 1
@pytest.mark.asyncio
async def test_max_size_rolling(self):
buf = SignalBuffer(buffer_size=3)
for _ in range(5):
await buf.push(make_snapshot())
assert len(buf._buffer) == 3 # maxlen enforced
@pytest.mark.asyncio
async def test_get_state_returns_signal_buffer_state(self):
"""get_state() returns a SignalBufferState with the pushed snapshot."""
buf = SignalBuffer(buffer_size=4)
await buf.push(make_snapshot())
state = await buf.get_state()
assert isinstance(state, SignalBufferState)
assert len(state.snapshots) == 1
def test_clear(self):
buf = SignalBuffer(buffer_size=4)
buf._buffer.append(make_snapshot())
buf.clear()
assert len(buf._buffer) == 0
def test_aggregate_gesture_neutral_returns_none(self):
"""Neutral gestures are skipped — all-neutral snapshots return None."""
buf = SignalBuffer()
result = buf._aggregate_gesture([make_snapshot(gesture_class="neutral")])
assert result is None
def test_aggregate_gesture_non_neutral_returned(self):
"""Non-neutral gesture with highest confidence is returned."""
buf = SignalBuffer()
snap = make_snapshot(gesture_class="thumbs_up", gesture_confidence=0.95)
result = buf._aggregate_gesture([snap])
assert result is not None
assert result.gesture_class == "thumbs_up"
def test_aggregate_affect_empty_snapshots_returns_none(self):
"""Empty snapshot list → None."""
buf = SignalBuffer()
result = buf._aggregate_affect([])
assert result is None
def test_aggregate_affect_non_neutral_dominant(self):
"""Happy affect appearing in ≥ half the snapshots returns AffectSignal."""
buf = SignalBuffer()
snaps = [make_snapshot(affect_class="happy", affect_confidence=0.9) for _ in range(3)]
result = buf._aggregate_affect(snaps)
assert result is not None
assert result.affect_class == "happy"
def test_confirm_air_sign_insufficient_windows_returns_empty(self):
"""Single snapshot with air_sign_char — insufficient windows → []."""
buf = SignalBuffer()
result = buf._confirm_air_sign_sequence([make_snapshot(air_sign_char="B")])
# confirmation_windows defaults to 2; one snapshot is not enough
assert isinstance(result, list)
assert len(result) == 0