test: refactor audio tests to use public interfaces

Replace direct queue/attribute access with capture_callback and
get_capture_frame. Add stop-drains-queues test.
This commit is contained in:
Username
2026-02-24 16:38:03 +01:00
parent 65de74193a
commit d117576449

View File

@@ -7,21 +7,17 @@ from tuimble.audio import FRAME_SIZE, SAMPLE_RATE, AudioPipeline, _apply_gain
def test_default_construction():
ap = AudioPipeline()
assert ap._sample_rate == SAMPLE_RATE
assert ap._frame_size == FRAME_SIZE
assert ap._input_device is None
assert ap._output_device is None
assert ap.capturing is False
assert ap.deafened is False
assert ap.input_gain == 1.0
assert ap.output_gain == 1.0
def test_custom_construction():
ap = AudioPipeline(sample_rate=24000, frame_size=480,
input_device=1, output_device=2)
assert ap._sample_rate == 24000
assert ap._frame_size == 480
assert ap._input_device == 1
assert ap._output_device == 2
# Verify via public behavior: get_capture_frame returns None
assert ap.get_capture_frame() is None
def test_capturing_toggle():
@@ -38,10 +34,13 @@ def test_get_capture_frame_empty():
assert ap.get_capture_frame() is None
def test_get_capture_frame_returns_queued():
def test_capture_and_retrieve():
"""Capture callback queues frames; get_capture_frame retrieves them."""
ap = AudioPipeline()
ap._capture_queue.put(b"\x01\x02\x03")
assert ap.get_capture_frame() == b"\x01\x02\x03"
ap.capturing = True
pcm = b"\x01\x02\x03\x04"
ap._capture_callback(pcm, 2, None, None)
assert ap.get_capture_frame() == pcm
assert ap.get_capture_frame() is None
@@ -84,12 +83,20 @@ def test_playback_callback_short_pcm_pads_silence():
def test_queue_playback_overflow_drops():
"""Full queue drops new data silently."""
ap = AudioPipeline(frame_size=FRAME_SIZE)
# Fill the queue
for i in range(ap._playback_queue.maxsize):
ap.queue_playback(b"\x00")
# This should not raise
ap.queue_playback(b"\xff")
assert ap._playback_queue.qsize() == ap._playback_queue.maxsize
# Use non-zero PCM so we can distinguish from silence
frame = b"\x42\x42"
for _ in range(50): # maxsize=50
ap.queue_playback(frame)
# This should not raise (dropped because queue is full)
ap.queue_playback(b"\xff\xff")
# Drain and count -- frames with our marker byte
count = 0
for _ in range(60): # more than queue size
outdata = bytearray(2)
ap._playback_callback(outdata, 1, None, None)
if outdata != bytearray(2):
count += 1
assert count == 50
def test_deafened_toggle():
@@ -106,14 +113,16 @@ def test_queue_playback_discards_when_deafened():
ap = AudioPipeline()
ap.deafened = True
ap.queue_playback(b"\x42" * 100)
assert ap._playback_queue.qsize() == 0
# Nothing to play back
outdata = bytearray(200)
ap._playback_callback(outdata, 100, None, None)
assert outdata == bytearray(200) # silence
def test_playback_callback_silence_when_deafened():
"""Playback callback writes silence when deafened, even with queued data."""
ap = AudioPipeline()
frame_bytes = FRAME_SIZE * 2
# Queue data before deafening
pcm = b"\x42" * frame_bytes
ap.queue_playback(pcm)
ap.deafened = True
@@ -129,6 +138,20 @@ def test_stop_without_start():
ap.stop()
def test_stop_drains_queues():
"""Queues are empty after stop()."""
ap = AudioPipeline()
ap.capturing = True
ap._capture_callback(b"\x00\x00", 1, None, None)
ap.queue_playback(b"\x00\x00")
ap.stop()
assert ap.get_capture_frame() is None
# Playback queue also drained -- callback produces silence
outdata = bytearray(2)
ap._playback_callback(outdata, 1, None, None)
assert outdata == bytearray(2)
# -- _apply_gain tests -------------------------------------------------------