Files
ai-companion/app/tests/test_transport.py
T

79 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""传输适配层的无硬件回归测试。"""
from __future__ import annotations
import asyncio
import tempfile
import time
import wave
from pathlib import Path
import sys
import numpy as np
from av import AudioFrame
ROOT = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(ROOT))
from client.test_client import FileAudioTrack, ReplyRecorder
from services.bot_server import SimpleVAD
def _write_test_wav(path: Path) -> None:
samples = np.ones(1600, dtype=np.int16) * 1000 # 100ms 语音
with wave.open(str(path), "wb") as output:
output.setnchannels(1)
output.setsampwidth(2)
output.setframerate(16000)
output.writeframes(samples.tobytes())
async def test_file_track_clock() -> None:
with tempfile.TemporaryDirectory(prefix="transport_test_") as directory:
path = Path(directory) / "input.wav"
_write_test_wav(path)
track = FileAudioTrack(path, tail_silence_s=0.8)
track._t0 = time.perf_counter() - 10 # 测试不需要真实等待
pts = []
while not track.finished:
pts.append((await track.recv()).pts)
assert len(pts) == 90, len(pts) # 100ms 音频 + 800ms 静音
assert all(after > before for before, after in zip(pts, pts[1:])), pts[-5:]
def test_vad_tail_silence() -> None:
vad = SimpleVAD()
events = [vad.feed(np.ones(320, dtype=np.int16) * 1000, 20) for _ in range(20)]
events += [vad.feed(np.zeros(320, dtype=np.int16), 20) for _ in range(40)]
assert "end" in events
def test_reply_requires_non_silent_audio() -> None:
recorder = ReplyRecorder(Path("/tmp/unused-transport-test.wav"))
silence = AudioFrame(format="s16", layout="mono", samples=320)
silence.planes[0].update(np.zeros(320, dtype=np.int16).tobytes())
silence.sample_rate = 48000
recorder.feed(silence)
assert not recorder.has_speech
assert recorder.sample_rate == 48000
speech = AudioFrame(format="s16", layout="mono", samples=320)
speech.planes[0].update((np.ones(320, dtype=np.int16) * 500).tobytes())
speech.sample_rate = 48000
recorder.feed(speech)
assert recorder.has_speech
async def main() -> int:
await test_file_track_clock()
test_vad_tail_silence()
test_reply_requires_non_silent_audio()
print("传输适配层回归测试:3/3 PASS")
return 0
if __name__ == "__main__":
raise SystemExit(asyncio.run(main()))