#!/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()))