# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project import asyncio import base64 import json import wave from contextlib import asynccontextmanager from typing import TypedDict import numpy as np import pytest import websockets from tests.e2e.online_serving import personaplex_realtime_duplex as driver pytestmark = [pytest.mark.core_model, pytest.mark.cpu] class _ServerState(TypedDict): index: int closed: bool frames: int close_requested: bool def _args(tmp_path, *extra): wav_path = tmp_path / "input.wav" with wave.open(str(wav_path), "wb") as stream: stream.setnchannels(1) stream.setsampwidth(2) stream.setframerate(driver.SAMPLE_RATE_HZ) stream.writeframes(np.full(driver.FRAME_SAMPLES * 2, 2000, dtype="= 1 assert report["client_pacing_warning"] is False assert "secret" not in json.dumps(report) assert "-999" not in json.dumps(report) def test_empty_output_has_missing_not_zero_latency(): report = driver._load_metrics(driver.RawRealtimeProbe("ws://unused"), []) assert report["client_stream_rtf"] is None assert report["audio_continuity_ok"] is None assert report["audio_underrun_s"] is None intervals = report["client_audio_packet_interval_ms"] assert isinstance(intervals, dict) assert intervals["p99"] is None def test_failed_acceptance_retains_frame_counts_without_remote_data(): client = driver.RawRealtimeProbe("ws://unused") client.events.add(_audio(), received_at_s=1.0) args = driver.parse_args( ["--model", "unused", "--input-wav", "unused.wav", "--max-frame-deficit", "0", "--min-voiced-frames", "1"] ) with pytest.raises(AssertionError) as raised: driver._session_result(client, input_frames=2, args=args, minimum_chunks=1) assert getattr(raised.value, "check", None) == "frame_deficit" stats = getattr(raised.value, "stats", {}) assert stats["frame_deficit"] == 1 and type(stats["frame_deficit"]) is int assert stats["output_frames"] == stats["voiced_frames"] == 1 assert stats["frame_coverage_ratio"] == 0.5 @pytest.mark.parametrize("event_type", sorted(driver.AUDIO_DELTA_EVENT_TYPES)) def test_session_result_accepts_current_and_legacy_audio_event_names(tmp_path, event_type): client = driver.RawRealtimeProbe("ws://unused") client.events.add(_audio(samples=driver.FRAME_SAMPLES, event_type=event_type), received_at_s=1.0) raw, response_ids, stats = driver._session_result( client, input_frames=1, args=_args(tmp_path, "--sessions", "1"), minimum_chunks=1, ) assert len(raw) == driver.FRAME_SAMPLES * 2 assert response_ids == {"r"} assert stats["frame_deficit"] == 0 def test_legacy_audio_event_name_contributes_load_metrics(): client = driver.RawRealtimeProbe("ws://unused") client.events.add(_audio(samples=driver.FRAME_SAMPLES * 5, event_type="response.audio.delta"), received_at_s=10.4) report = driver._load_metrics(client, [(10.0, 10.0, 10.01)]) assert report["output_samples"] == driver.FRAME_SAMPLES * 5 assert report["response_ids"] == ["r"] assert report["invalid_audio_packets"] == [] @pytest.mark.asyncio @pytest.mark.parametrize("sessions", [1, 2, 4]) async def test_real_websocket_load_success(tmp_path, sessions): args = _args(tmp_path, "--sessions", str(sessions)) async with _server() as (url, states): args.url = url result = await driver.run(args) assert result["ok"] is True assert result["passed_sessions"] == sessions rows = result["sessions"] assert isinstance(rows, list) assert len(rows) == sessions assert all(row["frame_deficit"] == 0 for row in rows) for row in rows: assert type(row["frame_deficit"]) is int assert row["output_frames"] == row["voiced_frames"] == row["audio_chunks"] == 2 assert row["silent_frames"] == 0 assert row["frame_coverage_ratio"] == 1.0 assert row["audio_rms"] == pytest.approx(2000 / 32768) assert row["acceptance_check"] is None assert isinstance(row["audio_continuity_ok"], bool) assert all(s["closed"] and s["close_requested"] for s in states) assert "must-not-be-exported" not in json.dumps(result) saved = json.loads((tmp_path / "out/load-result.json").read_text()) assert saved == result origins = [row["input_timeline"][0]["planned_at_s"] for row in rows] assert len(set(origins)) == 1 @pytest.mark.asyncio async def test_real_websocket_load_accepts_legacy_audio_event_name(tmp_path): args = _args(tmp_path, "--sessions", "2") async with _server("legacy_audio_event") as (url, states): args.url = url result = await driver.run(args) assert result["ok"] is True assert result["passed_sessions"] == 2 assert result["shared_response_ids"] == [] assert all(s["closed"] and s["close_requested"] for s in states) @pytest.mark.asyncio @pytest.mark.parametrize( "mode", [ "reject_second", "silent", "disconnect", "no_close_ack", "shared_response", "late_on_close", "error_on_close", "bad_json", "missing_capabilities", ], ) async def test_real_websocket_failures_remain_in_results(tmp_path, mode): args = _args(tmp_path) async with _server(mode) as (url, states): args.url = url result = await asyncio.wait_for(driver.run(args), timeout=4) assert result["ok"] is False assert "must-not-be-exported" not in json.dumps(result) assert result["requested_sessions"] == 2 rows = result["sessions"] assert isinstance(rows, list) assert len(rows) == 2 assert all(s["closed"] for s in states) if mode == "reject_second": assert result["passed_sessions"] == 1 rejected = next(row for row in rows if not row["ok"]) assert rejected["input_frames"] == 0 assert rejected["client_stream_rtf"] is None assert rejected["server_error_codes"] == ["resource_exhausted"] if mode == "silent": # A failed measurement must still release its admitted session. assert all(s["close_requested"] for s in states) if mode == "late_on_close": assert all(row["output_samples"] == 0 for row in rows) if mode == "shared_response": assert result["passed_sessions"] == 0 @pytest.mark.asyncio async def test_existing_report_is_not_overwritten(tmp_path): args = _args(tmp_path) path = tmp_path / "out/load-result.json" path.parent.mkdir() path.write_text("previous measurement") with pytest.raises(FileExistsError): await driver.run(args) assert path.read_text() == "previous measurement" @pytest.mark.asyncio @pytest.mark.parametrize("mode", ["success", "no_close_ack"]) async def test_cancellation_closes_all_connections(tmp_path, mode): args = _args(tmp_path, "--drain-s", "10", "--timeout-s", "30") async with _server(mode) as (url, states): args.url = url task = asyncio.create_task(driver.run(args)) async def started(): while len(states) < 2 or not all(state["frames"] for state in states): await asyncio.sleep(0.01) await asyncio.wait_for(started(), timeout=3) task.cancel() with pytest.raises(asyncio.CancelledError): await asyncio.wait_for(task, timeout=3) assert all(s["closed"] for s in states) @pytest.mark.asyncio async def test_client_rtf_ceiling_marks_slow_sessions_failed(tmp_path): args = _args(tmp_path, "--max-client-rtf", "0.001") async with _server() as (url, _): args.url = url result = await driver.run(args) assert result["passed_sessions"] == 0 rows = result["sessions"] assert isinstance(rows, list) assert all("client_stream_rtf exceeds" in row["error"] for row in rows) @pytest.mark.asyncio async def test_stalled_send_does_not_cause_catchup_burst(): class Clock: now = 10.0 def monotonic(self): return self.now async def sleep(self, delay): self.now += delay class Client(driver.RawRealtimeProbe): async def send(self, event): if event["audio_end_ms"] == 80: clock.now += 0.25 clock = Clock() sends: list[tuple[float, float, float]] = [] await driver._paced_load_frames( Client("ws://unused"), np.zeros(driver.FRAME_SAMPLES * 3, dtype="= sends[0][2] + driver.FRAME_PERIOD_S assert sends[1][1] - sends[1][0] == pytest.approx(0.25) assert sends[2][1] >= sends[1][2] + driver.FRAME_PERIOD_S metrics = driver._load_metrics(Client("ws://unused"), sends) assert metrics["client_pacing_warning"] is True assert asyncio.sleep != clock.sleep @pytest.mark.asyncio async def test_failed_frame_acceptance_preserves_numeric_diagnostics(tmp_path): args = _args(tmp_path, "--sessions", "1") async with _server("short_audio") as (url, states): args.url = url result = await driver.run(args) rows = result["sessions"] assert isinstance(rows, list) row = rows[0] assert row["ok"] is False assert row["acceptance_check"] == "frame_deficit" assert row["frame_deficit"] == 1 and type(row["frame_deficit"]) is int assert row["output_frames"] == row["voiced_frames"] == row["audio_chunks"] == 1 assert row["frame_coverage_ratio"] == 0.5 assert row["audio_rms"] == pytest.approx(2000 / 32768) assert "input=2" in row["error"] and "maximum_deficit=0" in row["error"] assert all(state["closed"] and state["close_requested"] for state in states) assert json.loads((tmp_path / "out/load-result.json").read_text()) == result @pytest.mark.asyncio async def test_acceptance_failure_does_not_export_remote_metadata(tmp_path): args = _args(tmp_path, "--sessions", "1") async with _server("private_metadata") as (url, _): args.url = url result = await driver.run(args) rows = result["sessions"] assert isinstance(rows, list) row = rows[0] assert row["ok"] is False assert row["acceptance_check"] == "scheduler_data_plane" assert "remote-detail" not in json.dumps(result) assert "must-not-be-exported" not in json.dumps(result) @pytest.mark.parametrize("ok", [True, False]) def test_load_main_exit_status(tmp_path, monkeypatch, ok): args = _args(tmp_path) async def completed(_args): return {"mode": "paced_multi_session", "ok": ok} monkeypatch.setattr(driver, "parse_args", lambda: args) monkeypatch.setattr(driver, "run", completed) if ok: driver.main() else: with pytest.raises(SystemExit) as exc: driver.main() assert exc.value.code == 1 @pytest.mark.asyncio async def test_handshake_rejection_is_retained_without_server_details(tmp_path, monkeypatch): from websockets.exceptions import InvalidHandshake async def rejected(_self): raise InvalidHandshake("credentials-in-remote-handshake-detail") monkeypatch.setattr(driver.RawRealtimeProbe, "__aenter__", rejected) result = await asyncio.wait_for(driver.run(_args(tmp_path)), timeout=3) assert result["ok"] is False rows = result["sessions"] assert isinstance(rows, list) and len(rows) == 2 assert all(row["error"] == "admission: InvalidHandshake" for row in rows) assert "credentials-in-remote-handshake-detail" not in json.dumps(result) @pytest.mark.asyncio @pytest.mark.parametrize("sessions", [1, 2]) @pytest.mark.parametrize( "mode", [ "missing_packet_rate", "string_packet_rate", "invalid_base64", "orphan_audio", "odd_packet", ], ) async def test_load_rejects_invalid_individual_audio_packets(tmp_path, sessions, mode): # The concatenated PCM can still meet coverage/voicing thresholds, so the # validator must also check each packet before accepting the whole run. args = _args(tmp_path, "--sessions", str(sessions)) async with _server(mode) as (url, states): args.url = url result = await asyncio.wait_for(driver.run(args), timeout=4) assert result["ok"] is False assert result["passed_sessions"] == 0 rows = result["sessions"] assert isinstance(rows, list) and len(rows) == sessions assert all(row["error"] is not None for row in rows) expected_code = { "missing_packet_rate": "invalid_audio_sample_rate", "string_packet_rate": "invalid_audio_sample_rate", "invalid_base64": "invalid_audio_base64", "orphan_audio": "missing_audio_response_id", "odd_packet": "unaligned_pcm16_packet", }[mode] assert all({packet["code"] for packet in row["invalid_audio_packets"]} == {expected_code} for row in rows) assert all( sum(packet["samples"] for packet in row["audio_packet_timeline"]) == row["output_samples"] for row in rows ) assert all(state["closed"] and state["close_requested"] for state in states) assert json.loads((tmp_path / "out/load-result.json").read_text()) == result def test_load_allows_empty_and_multi_frame_audio_packets(): client = driver.RawRealtimeProbe("ws://unused") client.events.add(_audio(samples=0), received_at_s=1.0) client.events.add(_audio(samples=driver.FRAME_SAMPLES * 5), received_at_s=1.4) metrics = driver._load_metrics(client, [(1.0, 1.0, 1.001)]) assert metrics["invalid_audio_packets"] == [] assert metrics["output_samples"] == driver.FRAME_SAMPLES * 5 packets = metrics["audio_packet_timeline"] assert isinstance(packets, list) and len(packets) == 1 def test_load_packet_diagnostics_do_not_copy_remote_payload(): client = driver.RawRealtimeProbe("ws://unused") event = _audio() event["delta"] = "private-remote-payload!" client.events.add(event, received_at_s=1.4) metrics = driver._load_metrics(client, []) assert metrics["output_samples"] == 0 assert metrics["invalid_audio_packets"] == [ { "event_index": 0, "received_at_s": 1.4, "code": "invalid_audio_base64", } ] assert "private-remote-payload" not in json.dumps(metrics)