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(): def test_default_construction():
ap = AudioPipeline() 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.capturing is False
assert ap.deafened is False assert ap.deafened is False
assert ap.input_gain == 1.0
assert ap.output_gain == 1.0
def test_custom_construction(): def test_custom_construction():
ap = AudioPipeline(sample_rate=24000, frame_size=480, ap = AudioPipeline(sample_rate=24000, frame_size=480,
input_device=1, output_device=2) input_device=1, output_device=2)
assert ap._sample_rate == 24000 # Verify via public behavior: get_capture_frame returns None
assert ap._frame_size == 480 assert ap.get_capture_frame() is None
assert ap._input_device == 1
assert ap._output_device == 2
def test_capturing_toggle(): def test_capturing_toggle():
@@ -38,10 +34,13 @@ def test_get_capture_frame_empty():
assert ap.get_capture_frame() is None 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 = AudioPipeline()
ap._capture_queue.put(b"\x01\x02\x03") ap.capturing = True
assert ap.get_capture_frame() == b"\x01\x02\x03" 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 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(): def test_queue_playback_overflow_drops():
"""Full queue drops new data silently.""" """Full queue drops new data silently."""
ap = AudioPipeline(frame_size=FRAME_SIZE) ap = AudioPipeline(frame_size=FRAME_SIZE)
# Fill the queue # Use non-zero PCM so we can distinguish from silence
for i in range(ap._playback_queue.maxsize): frame = b"\x42\x42"
ap.queue_playback(b"\x00") for _ in range(50): # maxsize=50
# This should not raise ap.queue_playback(frame)
ap.queue_playback(b"\xff") # This should not raise (dropped because queue is full)
assert ap._playback_queue.qsize() == ap._playback_queue.maxsize 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(): def test_deafened_toggle():
@@ -106,14 +113,16 @@ def test_queue_playback_discards_when_deafened():
ap = AudioPipeline() ap = AudioPipeline()
ap.deafened = True ap.deafened = True
ap.queue_playback(b"\x42" * 100) 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(): def test_playback_callback_silence_when_deafened():
"""Playback callback writes silence when deafened, even with queued data.""" """Playback callback writes silence when deafened, even with queued data."""
ap = AudioPipeline() ap = AudioPipeline()
frame_bytes = FRAME_SIZE * 2 frame_bytes = FRAME_SIZE * 2
# Queue data before deafening
pcm = b"\x42" * frame_bytes pcm = b"\x42" * frame_bytes
ap.queue_playback(pcm) ap.queue_playback(pcm)
ap.deafened = True ap.deafened = True
@@ -129,6 +138,20 @@ def test_stop_without_start():
ap.stop() 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 ------------------------------------------------------- # -- _apply_gain tests -------------------------------------------------------