项目文件夹

文件
2026-07-13 13:12:33 +08:00

120 行
4.2 KiB
Python

from __future__ import annotations
import asyncio
import importlib.util
import json
import sys
from pathlib import Path
from typing import Any
PROJECT_ROOT = Path(__file__).resolve().parents[4]
BENCH_SCRIPT = PROJECT_ROOT / "scripts" / "bench_tui_replay.py"
FIXTURE_MODULE = Path(__file__).with_name("replay_fixtures.py")
def _load_module(name: str, path: Path) -> Any:
spec = importlib.util.spec_from_file_location(name, path)
assert spec is not None
assert spec.loader is not None
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
spec.loader.exec_module(module)
return module
fixtures = _load_module("tui_replay_fixtures", FIXTURE_MODULE)
ReplayEvent = fixtures.ReplayEvent
build_dense_history_events = fixtures.build_dense_history_events
build_long_stream_events = fixtures.build_long_stream_events
def _events_by_kind(events: list[Any], kind: str) -> list[Any]:
return [event for event in events if event.kind == kind]
def _text_chars(events: list[ReplayEvent]) -> int:
return sum(
len(str(event.payload["text"]))
for event in events
if event.kind == "text_delta"
)
def test_long_stream_fixture_matches_baseline_shape() -> None:
events = build_long_stream_events()
assert all(isinstance(event, ReplayEvent) for event in events)
assert events[0].kind == "user_input"
assert events[1].kind == "router_decision"
assert events[-1].kind == "done"
assert len(_events_by_kind(events, "text_delta")) == 4_000
assert _text_chars(events) == 160_000
assert len(_events_by_kind(events, "tool_start")) == 4
assert len(_events_by_kind(events, "tool_finished")) == 4
assert len(_events_by_kind(events, "router_decision")) == 1
def test_dense_history_fixture_matches_baseline_shape() -> None:
events = build_dense_history_events()
messages = _events_by_kind(events, "history_message")
tool_cards = _events_by_kind(events, "tool_card")
text_chars = sum(
len(str(event.payload["content"]))
for event in messages
if "content" in event.payload
)
assert len(messages) == 500
assert sum(1 for event in messages if event.payload["role"] == "user") == 250
assert sum(1 for event in messages if event.payload["role"] == "assistant") == 250
assert len(tool_cards) == 120
assert sum(1 for event in tool_cards if event.payload["expanded_candidate"]) == 20
assert len(_events_by_kind(events, "router_decision")) == 4
assert text_chars >= 24 * 80 * 30
def test_opentui_replay_summary_can_be_written(tmp_path) -> None:
bench = _load_module("bench_tui_replay", BENCH_SCRIPT)
summary = asyncio.run(bench.run_replay("opentui", "long-stream", repeat=1))
summary_path = tmp_path / "summary.json"
bench.write_summary(summary, summary_path)
data = json.loads(summary_path.read_text())
assert data["renderer"] == "opentui"
assert data["fixture"] == "long-stream"
assert data["event_count"] == 4011
assert data["text_chars"] == 160_000
assert data["tool_count"] == 4
assert data["router_decision_count"] == 1
assert data["flush_count"] == 0
assert 0 < data["coalescing_ratio"] < 1
assert data["max_buffer_chars"] <= 2_048
assert data["transcript_items"] == 0
assert data["visible_items"] == 0
assert data["expanded_tools"] == 0
assert data["projection_wall_ms"] == 0
assert data["available"] is True
assert data["skip_reason"] is None
assert data["rendered_text_matches"] is True
assert data["plugin_error_count"] == 0
assert data["errors"] == []
def test_dense_history_replay_uses_bounded_transcript_projection() -> None:
bench = _load_module("bench_tui_replay", BENCH_SCRIPT)
summary = asyncio.run(bench.run_replay("opentui", "dense-history", repeat=1))
assert summary.event_count == 624
assert summary.text_chars >= 24 * 80 * 30
assert summary.tool_count == 120
assert summary.router_decision_count == 4
assert summary.flush_count == 0
assert summary.coalescing_ratio == 0
assert summary.transcript_items == 624
assert 0 < summary.visible_items <= 30
assert summary.expanded_tools == 20
assert summary.projection_wall_ms >= 0
assert summary.errors == []