chunker and ui
This commit is contained in:
115
tests/chunker/test_queue.py
Normal file
115
tests/chunker/test_queue.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Tests for ChunkQueue — backpressure, sentinel shutdown, timeout behavior.
|
||||
|
||||
Demonstrates: TDD (Interview Topic 8) — concurrency testing.
|
||||
"""
|
||||
|
||||
import queue
|
||||
import threading
|
||||
|
||||
import pytest
|
||||
|
||||
from core.chunker.queue import ChunkQueue
|
||||
|
||||
|
||||
class TestChunkQueue:
|
||||
def test_put_and_get(self, make_chunk):
|
||||
"""Basic put/get cycle."""
|
||||
q = ChunkQueue(maxsize=5)
|
||||
chunk = make_chunk(0)
|
||||
q.put(chunk)
|
||||
result = q.get(timeout=1.0)
|
||||
assert result.sequence == 0
|
||||
|
||||
def test_fifo_order(self, make_chunk):
|
||||
"""Items come out in FIFO order."""
|
||||
q = ChunkQueue(maxsize=5)
|
||||
for i in range(3):
|
||||
q.put(make_chunk(i))
|
||||
|
||||
for i in range(3):
|
||||
assert q.get(timeout=1.0).sequence == i
|
||||
|
||||
def test_close_returns_none(self, make_chunk):
|
||||
"""After close(), get() returns None (sentinel)."""
|
||||
q = ChunkQueue(maxsize=5)
|
||||
q.put(make_chunk(0))
|
||||
q.close()
|
||||
|
||||
result = q.get(timeout=1.0)
|
||||
assert result.sequence == 0
|
||||
|
||||
# Next get should hit sentinel
|
||||
result = q.get(timeout=1.0)
|
||||
assert result is None
|
||||
|
||||
def test_close_propagates_to_multiple_consumers(self, make_chunk):
|
||||
"""Sentinel propagates: multiple consumers all get None."""
|
||||
q = ChunkQueue(maxsize=5)
|
||||
q.close()
|
||||
|
||||
# Multiple consumers should all see None
|
||||
assert q.get(timeout=1.0) is None
|
||||
assert q.get(timeout=1.0) is None
|
||||
|
||||
def test_is_closed(self):
|
||||
"""is_closed reflects state."""
|
||||
q = ChunkQueue()
|
||||
assert not q.is_closed
|
||||
q.close()
|
||||
assert q.is_closed
|
||||
|
||||
def test_qsize(self, make_chunk):
|
||||
"""qsize tracks approximate queue depth."""
|
||||
q = ChunkQueue(maxsize=10)
|
||||
assert q.qsize() == 0
|
||||
|
||||
q.put(make_chunk(0))
|
||||
q.put(make_chunk(1))
|
||||
assert q.qsize() == 2
|
||||
|
||||
q.get(timeout=1.0)
|
||||
assert q.qsize() == 1
|
||||
|
||||
def test_backpressure_blocks(self, make_chunk):
|
||||
"""Put blocks when queue is full (backpressure)."""
|
||||
q = ChunkQueue(maxsize=2)
|
||||
q.put(make_chunk(0))
|
||||
q.put(make_chunk(1))
|
||||
|
||||
# Queue is full — put with short timeout should raise
|
||||
with pytest.raises(queue.Full):
|
||||
q.put(make_chunk(2), timeout=0.05)
|
||||
|
||||
def test_get_timeout(self):
|
||||
"""Get on empty queue with timeout raises Empty."""
|
||||
q = ChunkQueue(maxsize=5)
|
||||
|
||||
with pytest.raises(queue.Empty):
|
||||
q.get(timeout=0.05)
|
||||
|
||||
def test_concurrent_put_get(self, make_chunk):
|
||||
"""Producer/consumer threads work correctly."""
|
||||
q = ChunkQueue(maxsize=3)
|
||||
results = []
|
||||
|
||||
def producer():
|
||||
for i in range(10):
|
||||
q.put(make_chunk(i))
|
||||
q.close()
|
||||
|
||||
def consumer():
|
||||
while True:
|
||||
item = q.get(timeout=2.0)
|
||||
if item is None:
|
||||
break
|
||||
results.append(item.sequence)
|
||||
|
||||
t1 = threading.Thread(target=producer)
|
||||
t2 = threading.Thread(target=consumer)
|
||||
t1.start()
|
||||
t2.start()
|
||||
t1.join(timeout=5.0)
|
||||
t2.join(timeout=5.0)
|
||||
|
||||
assert sorted(results) == list(range(10))
|
||||
Reference in New Issue
Block a user