Skip to content

tests: speed up the unit suite and fold per-PR test files - #8510

Merged
longcw merged 1 commit into
mainfrom
longc/fast-unit-tests
Nov 11, 2026
Merged

longcw merged 1 commit into
mainfrom
longc/fast-unit-tests

Conversation

@longcw

@longcw longcw commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

Problem: The unit-tests CI job grew from about 1 min in July to 8 min, because most new async tests wait on the real clock through sleeps, timeouts, retry backoff and close delays. The suite also grew from about 90 to 357 files, mostly one file per PR, with copied fakes and tests that repeat what another test already covers.

Fix: Timing-driven tests now run under virtual_time, real servers give way to in-memory fakes where possible, and the tests that still need real time use short timeouts and events. Per-PR files are folded into one module per unit under test, near-duplicates become parametrized tests, and a test is deleted only when another test still covers its behavior.

Context for reviewing and coding agents

Where the numbers come from

Before: CI run 42965670118 on main spent 450s in pytest for 7262 tests. A local per-test timing pass showed 30 files took 389s of the 432s summed test time, and the other ~225 files took under 1s each. After: the local --unit --audio_eot gate runs in about 44s wall (6115 passed, 2 runs, no flakes), against about 320s on main on the same machine. Test files go from 357 to 236.

What made it fast, by size of effect

  • virtual_time on timing-driven modules: voice core 65s → 3s, realtime 75s → 4.5s, telemetry 59s → 1.7s.
  • tests/virtual_time.py now patches time.time_ns and the time_ns that opentelemetry.sdk.trace captures at import. Span tests needed real_time before this, because the SDK stamped spans on the real clock while the agent passed virtual start times.
  • shutdown_default_executor in the virtual loop waits on real time. Its 300s join timeout fired at once under virtual time, which printed "The executor did not finish joining its threads within 300 seconds".
  • In-memory fakes replace loopback servers in test_inference_stt.py and test_plugin_baseten_stt.py, so those run under virtual time.
  • The AWS STT fake now ends its output stream when the input closes, as AWS does; every test used to wait out the plugin's hard-coded 3s drain (18s → 0.5s).
  • test_ipc.py spawns about 11 processes instead of about 30.

What was deleted

  • test_llm.py was entirely commented out.
  • test_chat_ctx.py::test_summarize made a real inference.LLM call when credentials were set, and asserted nothing.
  • test_tokenizer_xml_markup.py downloaded a tiktoken encoding from openaipublic.blob.core.windows.net; it now splits word pieces locally. That was the only network call left in the unit gate.
  • Print-only tests, getter-only tests, and tests that read private fields when a wire-level test covers the outcome.
  • Tests whose behavior another remaining test covers. Each one was mapped to its covering test during the cleanup, for example test_trace_schema.py::test_full_fake_session_is_well_formed → test_trace_spans.py::test_plain_reply_is_one_generation.

Most of the drop from about 7250 to about 6130 collected tests is parametrization, not lost cases: test_transcription_filter.py checks all 8 chunk sizes inside one test per case (430 → 89 items).

What stays slow, and why

test_ipc.py (about 6s wall, real process spawns, concurrent group) and the Rime, Reson8 and Zoom plugin tests (about 8s summed, real loopback websocket servers). A shared in-memory websocket fake that aiohttp ws_connect code accepts would let those three run under virtual time.

Scope boundaries

Modules in the plugin, stt, tts, realtime, evals and docs categories are unchanged, and they still collect. test_plugin_liveavatar.py and test_plugin_synthesia.py carried both unit and plugin(...) markers; both are hermetic and keep only unit. No source outside tests/ changes, apart from dropping the unused OPENAI_API_KEY from the unit job, --durations=15 in make unit-tests, and the AGENTS.md rule on where a new test goes.

Product issues found, not fixed here

  • APIConnectOptions._interval_for_retry in livekit-agents/livekit/agents/types.py documents an immediate first retry but returns 0.1s.
  • ThreadJobExecutor.initialize() ignores InitializeResponse.error, so a failing initialize_process_fnc passes on THREAD and fails on PROCESS.
  • utils/aio/interval.py Interval.tick treats _last_sleep == 0.0 as "not yet ticked", so it spins when loop.time() is 0.0.
  • AgentSession.aclose() waits the full session_close_transcript_timeout (2s) even for an idle session.

The unit gate grew from about 1 min to 8 min on CI because most new
async tests waited on the real clock: sleeps, timeouts, retry backoff
and close delays. Timing-driven tests now run on virtual time, real
servers give way to in-memory fakes where possible, and the remaining
real-time tests use short timeouts and events instead of sleeps.

Per-PR test files are folded into one module per unit under test,
near-duplicates become parametrized tests, and tests already covered
by another test are deleted. The two tests that reached the network
(a tiktoken download, a real inference.LLM call) are gone or hermetic.

virtual_time also patches time.time_ns and the OpenTelemetry span
clock, and keeps the default-executor shutdown on real time. AGENTS.md
states where a new test goes and that unit tests stay off the network
and the real clock.
@longcw
longcw requested a review from a team as a code owner November 11, 2026 10:25
@longcw
longcw merged commit 59753ba into main Nov 11, 2026
25 of 26 checks passed
@longcw
longcw deleted the longc/fast-unit-tests branch November 11, 2026 10:32
neil-sarvam added a commit to neil-sarvam/agents that referenced this pull request Nov 11, 2026
Fold the end-of-input tests into tests/test_plugin_sarvam_stt.py, following livekit#8510,
and re-anchor the provider clock with restart_provider_clock() on reconnect, following livekit#7795.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
neil-sarvam added a commit to neil-sarvam/agents that referenced this pull request Nov 11, 2026
Fold the end-of-input tests into tests/test_plugin_sarvam_stt.py, following livekit#8510.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
neil-sarvam added a commit to neil-sarvam/agents that referenced this pull request Nov 11, 2026
Fold the reconnect tests into the reorganized tests/test_plugin_sarvam_stt.py, following livekit#8510,
and re-anchor the provider clock with restart_provider_clock() on reconnect, following livekit#7795.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
neil-sarvam added a commit to neil-sarvam/agents that referenced this pull request Nov 11, 2026
Move the timestamp tests into the reorganized tests/test_plugin_sarvam_stt.py, following livekit#8510.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kimnamu added a commit to kimnamu/agents that referenced this pull request Nov 11, 2026
Resolve the comment conflict in tests/test_plugin_aws_llm.py by keeping the
comment style from livekit#8510 and adding the Sonnet 5.5 / Sonnet 5 mention.

Co-Authored-By: Claude <noreply@anthropic.com>
Rehansanjay added a commit to Rehansanjay/agents that referenced this pull request Nov 12, 2026
…ime_turns.py

Upstream folded the per-PR test files (livekit#8510), deleting test_playout_launch_pause.py
and test_realtime_reply_chat_ctx.py. The six hold_reply_while_user_speaking tests now
sit at the end of test_playback.py, and the _waits_for_user_silence stub moves to the
realtime reply fake in test_realtime_turns.py.
chenghao-mou added a commit to sarankumar1325/agents that referenced this pull request Nov 12, 2026
main folded tests/test_amd_detector.py into tests/test_amd.py
(livekit#8510). The hangup tests and their fake room move there.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
chenghao-mou added a commit that referenced this pull request Nov 12, 2026
…6-mini-2s-eot-model

#8510 folded tests/test_turn_detection_fsm.py and
tests/test_speech_start_time_persistence.py into tests/test_turn_detector.py and
tests/test_audio_recognition.py. The tests this branch added to the deleted files
move there, and tests/test_audio_eot_local_transport.py folds into
tests/test_turn_detector.py to follow the same layout.

The folded request tests asserted on the transport right after predict(). The
request now waits in the audio queue, so their scripted transport drains it.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
peirangong-baseten added a commit to peirangong-baseten/agents that referenced this pull request Nov 12, 2026
Conflict in tests/test_plugin_baseten_stt.py: main (livekit#8510) moved the Whisper
tests onto an in-process FakeWebSocket under virtual time. The v2 tests are
moved the same way, since a real local server times out on the virtual
clock: the fake server now answers each event as the client sends it, and its
segment times count the socket's own audio.

main (livekit#7796) also re-anchors start_time_offset on the first frame each
connection reads. A reconnected v2 socket's clock starts at the replayed
audio, before that frame, so the stream now uses the live offset only until
its first reconnect after reading audio, and keeps that anchor from then on.
The offset test starts its audio 1 s after the offset is set and fails both
with the anchor read at connect time and with the live offset throughout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chenghao-mou added a commit that referenced this pull request Nov 13, 2026
main folded per-PR test files (#8510) and fixed callee hangups in the old
AMD classifier (#8242). This branch replaces that classifier, and its
detector already completes with reason participant_disconnected, so the
old classifier.py and main's tests/test_amd.py are dropped. The branch's
edits to the folded test files move to tests/test_audio_recognition.py
and tests/test_agent_interruptions.py.
chenghao-mou added a commit that referenced this pull request Nov 13, 2026
main now keeps one test module per source module (#8510). The nine
tests/test_amd_*.py files and tests/amd_test_utils.py become regions of
tests/test_amd.py. The realtime session helper is now running_realtime,
because the detector tests already define running.
chenghao-mou added a commit that referenced this pull request Nov 13, 2026
…nto chenghao/feat/AGT-3866-amd-client-feature

The base branch merged main, which folded the per-PR test files (#8510)
and dropped tests/test_inference_stt_start_of_speech.py. The one-line
edit this branch made to that file goes with it: the test it touched no
longer exists.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants