Skip to content

Commit 962d06f

Browse files
committed
Add medium real-source rerun regression
1 parent e605df5 commit 962d06f

1 file changed

Lines changed: 103 additions & 0 deletions

File tree

tests/test_medium_e2e_real_capture.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
from __future__ import annotations
22

3+
import json
34
import math
45
import queue
56
import shutil
67
import struct
78
import subprocess
9+
import sys
10+
import textwrap
811
import threading
912
import time
1013
import wave
@@ -30,6 +33,7 @@
3033
]
3134

3235
_CLOSE_TIMEOUT_S = 5.0
36+
_REAL_SOURCE_RERUN_TIMEOUT_S = 12.0
3337
_PLAYBACK_DURATION_S = 3.0
3438
_EXPECTED_TONE_HZ = 440.0
3539

@@ -312,3 +316,102 @@ def test_medium_system_audio_engine_close_remains_stable_across_repeated_runs(
312316

313317
assert len(elapsed_values) == 3
314318
assert max(elapsed_values) < _CLOSE_TIMEOUT_S
319+
320+
321+
def test_medium_real_source_asr_rerun_engine_close_remains_stable(tmp_path: Path) -> None:
322+
probe = textwrap.dedent(
323+
"""
324+
import asyncio
325+
import json
326+
import time
327+
328+
import macloop
329+
330+
async def run() -> None:
331+
stats = []
332+
for cycle in range(2):
333+
engine = macloop.AudioEngine()
334+
try:
335+
remote = engine.create_stream(macloop.SystemAudioSource)
336+
mic = engine.create_stream(macloop.MicrophoneSource, vpio_enabled=True)
337+
routes = [
338+
engine.route(id=f"remote_{cycle}", stream=remote),
339+
engine.route(id=f"mic_{cycle}", stream=mic),
340+
]
341+
342+
for sink_cycle in range(2):
343+
sink = macloop.AsrSink(
344+
routes=routes,
345+
chunk_frames=1280,
346+
sample_rate=16000,
347+
channels=1,
348+
sample_format="i16",
349+
)
350+
351+
async def reader() -> None:
352+
async for _ in sink.chunks_async():
353+
pass
354+
355+
task = asyncio.create_task(reader())
356+
await asyncio.sleep(1.5)
357+
sink.close()
358+
await task
359+
await asyncio.sleep(0.25)
360+
361+
started = time.monotonic()
362+
engine.close()
363+
stats.append(
364+
{
365+
"cycle": cycle,
366+
"engine_close_elapsed_s": round(time.monotonic() - started, 6),
367+
}
368+
)
369+
finally:
370+
try:
371+
engine.close()
372+
except Exception:
373+
pass
374+
375+
print(json.dumps(stats), flush=True)
376+
377+
asyncio.run(run())
378+
"""
379+
)
380+
381+
elapsed_values: list[float] = []
382+
for run_index in range(2):
383+
try:
384+
completed = subprocess.run(
385+
[sys.executable, "-c", probe],
386+
capture_output=True,
387+
text=True,
388+
timeout=_REAL_SOURCE_RERUN_TIMEOUT_S,
389+
cwd=tmp_path,
390+
)
391+
except subprocess.TimeoutExpired as exc:
392+
pytest.fail(
393+
f"real-source rerun probe timed out on run {run_index + 1} after "
394+
f"{_REAL_SOURCE_RERUN_TIMEOUT_S:.1f}s\nSTDOUT:\n{exc.stdout or ''}\nSTDERR:\n{exc.stderr or ''}"
395+
)
396+
397+
assert completed.returncode == 0, (
398+
f"real-source rerun probe failed on run {run_index + 1} with return code "
399+
f"{completed.returncode}\nSTDOUT:\n{completed.stdout}\nSTDERR:\n{completed.stderr}"
400+
)
401+
402+
payload = completed.stdout.strip().splitlines()
403+
assert payload, (
404+
f"real-source rerun probe produced no JSON payload on run {run_index + 1}\n"
405+
f"STDERR:\n{completed.stderr}"
406+
)
407+
stats = json.loads(payload[-1])
408+
assert len(stats) == 2
409+
close_times = [float(item["engine_close_elapsed_s"]) for item in stats]
410+
elapsed_values.extend(close_times)
411+
assert max(close_times) < _CLOSE_TIMEOUT_S, (
412+
f"real-source rerun probe observed slow engine.close() on run {run_index + 1}: "
413+
f"{close_times}\nSTDOUT:\n{completed.stdout}\nSTDERR:\n{completed.stderr}"
414+
)
415+
416+
assert len(elapsed_values) == 4
417+
assert max(elapsed_values) < _CLOSE_TIMEOUT_S

0 commit comments

Comments
 (0)