improved unit tests / add a run_test function to test processors
This commit is contained in:
122
tests/skipped/test_aggregators.py
Normal file
122
tests/skipped/test_aggregators.py
Normal file
@@ -0,0 +1,122 @@
|
||||
import asyncio
|
||||
import doctest
|
||||
import functools
|
||||
import unittest
|
||||
|
||||
from pipecat.frames.frames import (
|
||||
AudioRawFrame,
|
||||
EndFrame,
|
||||
Frame,
|
||||
ImageRawFrame,
|
||||
LLMFullResponseEndFrame,
|
||||
LLMFullResponseStartFrame,
|
||||
TextFrame,
|
||||
)
|
||||
from pipecat.pipeline.parallel_pipeline import ParallelPipeline
|
||||
from pipecat.pipeline.pipeline import Pipeline
|
||||
from pipecat.processors.aggregators.gated import GatedAggregator
|
||||
from pipecat.processors.aggregators.sentence import SentenceAggregator
|
||||
from pipecat.processors.text_transformer import StatelessTextTransformer
|
||||
|
||||
|
||||
class TestDailyFrameAggregators(unittest.IsolatedAsyncioTestCase):
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
async def test_sentence_aggregator(self):
|
||||
sentence = "Hello, world. How are you? I am fine"
|
||||
expected_sentences = ["Hello, world.", " How are you?", " I am fine "]
|
||||
aggregator = SentenceAggregator()
|
||||
for word in sentence.split(" "):
|
||||
async for sentence in aggregator.process_frame(TextFrame(word + " ")):
|
||||
self.assertIsInstance(sentence, TextFrame)
|
||||
if isinstance(sentence, TextFrame):
|
||||
self.assertEqual(sentence.text, expected_sentences.pop(0))
|
||||
|
||||
async for sentence in aggregator.process_frame(EndFrame()):
|
||||
if len(expected_sentences):
|
||||
self.assertIsInstance(sentence, TextFrame)
|
||||
if isinstance(sentence, TextFrame):
|
||||
self.assertEqual(sentence.text, expected_sentences.pop(0))
|
||||
else:
|
||||
self.assertIsInstance(sentence, EndFrame)
|
||||
|
||||
self.assertEqual(expected_sentences, [])
|
||||
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
async def test_gated_accumulator(self):
|
||||
gated_aggregator = GatedAggregator(
|
||||
gate_open_fn=lambda frame: isinstance(frame, ImageRawFrame),
|
||||
gate_close_fn=lambda frame: isinstance(frame, LLMFullResponseStartFrame),
|
||||
start_open=False,
|
||||
)
|
||||
|
||||
frames = [
|
||||
LLMFullResponseStartFrame(),
|
||||
TextFrame("Hello, "),
|
||||
TextFrame("world."),
|
||||
AudioRawFrame(b"hello"),
|
||||
ImageRawFrame(b"image", (0, 0)),
|
||||
AudioRawFrame(b"world"),
|
||||
LLMFullResponseEndFrame(),
|
||||
]
|
||||
|
||||
expected_output_frames = [
|
||||
ImageRawFrame(b"image", (0, 0)),
|
||||
LLMFullResponseStartFrame(),
|
||||
TextFrame("Hello, "),
|
||||
TextFrame("world."),
|
||||
AudioRawFrame(b"hello"),
|
||||
AudioRawFrame(b"world"),
|
||||
LLMFullResponseEndFrame(),
|
||||
]
|
||||
for frame in frames:
|
||||
async for out_frame in gated_aggregator.process_frame(frame):
|
||||
self.assertEqual(out_frame, expected_output_frames.pop(0))
|
||||
self.assertEqual(expected_output_frames, [])
|
||||
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
async def test_parallel_pipeline(self):
|
||||
async def slow_add(sleep_time: float, name: str, x: str):
|
||||
await asyncio.sleep(sleep_time)
|
||||
return ":".join([x, name])
|
||||
|
||||
pipe1_annotation = StatelessTextTransformer(functools.partial(slow_add, 0.1, "pipe1"))
|
||||
pipe2_annotation = StatelessTextTransformer(functools.partial(slow_add, 0.2, "pipe2"))
|
||||
sentence_aggregator = SentenceAggregator()
|
||||
add_dots = StatelessTextTransformer(lambda x: x + ".")
|
||||
|
||||
source = asyncio.Queue()
|
||||
sink = asyncio.Queue()
|
||||
pipeline = Pipeline(
|
||||
[
|
||||
ParallelPipeline([[pipe1_annotation], [sentence_aggregator, pipe2_annotation]]),
|
||||
add_dots,
|
||||
],
|
||||
source,
|
||||
sink,
|
||||
)
|
||||
|
||||
frames = [TextFrame("Hello, "), TextFrame("world."), EndFrame()]
|
||||
|
||||
expected_output_frames: list[Frame] = [
|
||||
TextFrame(text="Hello, :pipe1."),
|
||||
TextFrame(text="world.:pipe1."),
|
||||
TextFrame(text="Hello, world.:pipe2."),
|
||||
EndFrame(),
|
||||
]
|
||||
|
||||
for frame in frames:
|
||||
await source.put(frame)
|
||||
|
||||
await pipeline.run_pipeline()
|
||||
|
||||
while not sink.empty():
|
||||
frame = await sink.get()
|
||||
self.assertEqual(frame, expected_output_frames.pop(0))
|
||||
|
||||
|
||||
def load_tests(loader, tests, ignore):
|
||||
"""Run doctests on the aggregators module."""
|
||||
from pipecat.processors import aggregators
|
||||
|
||||
tests.addTests(doctest.DocTestSuite(aggregators))
|
||||
return tests
|
||||
86
tests/skipped/test_daily_transport_service.py
Normal file
86
tests/skipped/test_daily_transport_service.py
Normal file
@@ -0,0 +1,86 @@
|
||||
import unittest
|
||||
|
||||
|
||||
class TestDailyTransport(unittest.IsolatedAsyncioTestCase):
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
async def test_event_handler(self):
|
||||
from pipecat.transports.daily_transport import DailyTransport
|
||||
|
||||
transport = DailyTransport("mock.daily.co/mock", "token", "bot")
|
||||
|
||||
was_called = False
|
||||
|
||||
@transport.event_handler("on_first_other_participant_joined")
|
||||
def test_event_handler(transport, participant):
|
||||
nonlocal was_called
|
||||
was_called = True
|
||||
|
||||
transport.on_first_other_participant_joined({"id": "user-id"})
|
||||
|
||||
self.assertTrue(was_called)
|
||||
|
||||
"""
|
||||
TODO: fix this test, it broke when I added the `.result` call in the patch.
|
||||
async def test_event_handler_async(self):
|
||||
from pipecat.services.daily_transport_service import DailyTransportService
|
||||
|
||||
transport = DailyTransportService("mock.daily.co/mock", "token", "bot")
|
||||
|
||||
event = asyncio.Event()
|
||||
|
||||
@transport.event_handler("on_first_other_participant_joined")
|
||||
async def test_event_handler(transport, participant):
|
||||
nonlocal event
|
||||
print("sleeping")
|
||||
await asyncio.sleep(0.1)
|
||||
print("setting")
|
||||
event.set()
|
||||
print("returning")
|
||||
|
||||
thread = threading.Thread(target=transport.on_first_other_participant_joined)
|
||||
thread.start()
|
||||
thread.join()
|
||||
|
||||
await asyncio.wait_for(event.wait(), timeout=1)
|
||||
self.assertTrue(event.is_set())
|
||||
"""
|
||||
|
||||
"""
|
||||
@patch("pipecat.services.daily_transport_service.CallClient")
|
||||
@patch("pipecat.services.daily_transport_service.Daily")
|
||||
async def test_run_with_camera_and_mic(self, daily_mock, callclient_mock):
|
||||
from pipecat.services.daily_transport_service import DailyTransportService
|
||||
transport = DailyTransportService(
|
||||
"https://mock.daily.co/mock",
|
||||
"token",
|
||||
"bot",
|
||||
mic_enabled=True,
|
||||
camera_enabled=True,
|
||||
duration_minutes=0.01,
|
||||
)
|
||||
|
||||
mic = MagicMock()
|
||||
camera = MagicMock()
|
||||
daily_mock.create_microphone_device.return_value = mic
|
||||
daily_mock.create_camera_device.return_value = camera
|
||||
|
||||
async def send_audio_frame():
|
||||
await transport.send_queue.put(AudioFrame(bytes([0] * 3300)))
|
||||
|
||||
async def send_video_frame():
|
||||
await transport.send_queue.put(ImageFrame(b"test", (0, 0)))
|
||||
|
||||
await asyncio.gather(transport.run(), send_audio_frame(), send_video_frame())
|
||||
|
||||
daily_mock.init.assert_called_once_with()
|
||||
daily_mock.create_microphone_device.assert_called_once()
|
||||
daily_mock.create_camera_device.assert_called_once()
|
||||
|
||||
callclient_mock.return_value.set_user_name.assert_called_once_with("bot")
|
||||
callclient_mock.return_value.join.assert_called_once_with(
|
||||
"https://mock.daily.co/mock", "token", completion=transport.call_joined
|
||||
)
|
||||
|
||||
camera.write_frame.assert_called_with(b"test")
|
||||
mic.write_frames.assert_called()
|
||||
"""
|
||||
37
tests/skipped/test_openai_tts.py
Normal file
37
tests/skipped/test_openai_tts.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import asyncio
|
||||
import unittest
|
||||
|
||||
import openai
|
||||
import pyaudio
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from pipecat.frames.frames import AudioRawFrame, ErrorFrame
|
||||
from pipecat.services.openai import OpenAITTSService
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class TestWhisperOpenAIService(unittest.IsolatedAsyncioTestCase):
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
async def test_whisper_tts(self):
|
||||
pa = pyaudio.PyAudio()
|
||||
stream = pa.open(format=pyaudio.paInt16, channels=1, rate=24_000, output=True)
|
||||
|
||||
tts = OpenAITTSService(voice="nova")
|
||||
|
||||
async for frame in tts.run_tts("Hello, there. Nice to meet you, seems to work well"):
|
||||
self.assertIsInstance(frame, AudioRawFrame)
|
||||
stream.write(frame.audio)
|
||||
|
||||
await asyncio.sleep(0.5)
|
||||
stream.stop_stream()
|
||||
pa.terminate()
|
||||
|
||||
tts = OpenAITTSService(voice="invalid_voice")
|
||||
with self.assertRaises(openai.BadRequestError):
|
||||
async for frame in tts.run_tts("wont work"):
|
||||
self.assertIsInstance(frame, ErrorFrame)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
116
tests/skipped/test_pipeline.py
Normal file
116
tests/skipped/test_pipeline.py
Normal file
@@ -0,0 +1,116 @@
|
||||
import asyncio
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
from pipecat.frames.frames import EndFrame, TextFrame
|
||||
from pipecat.pipeline.pipeline import Pipeline
|
||||
from pipecat.processors.aggregators.sentence import SentenceAggregator
|
||||
from pipecat.processors.frame_processor import FrameProcessor
|
||||
from pipecat.processors.text_transformer import StatelessTextTransformer
|
||||
|
||||
|
||||
class TestDailyPipeline(unittest.IsolatedAsyncioTestCase):
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
async def test_pipeline_simple(self):
|
||||
aggregator = SentenceAggregator()
|
||||
|
||||
outgoing_queue = asyncio.Queue()
|
||||
incoming_queue = asyncio.Queue()
|
||||
pipeline = Pipeline([aggregator], incoming_queue, outgoing_queue)
|
||||
|
||||
await incoming_queue.put(TextFrame("Hello, "))
|
||||
await incoming_queue.put(TextFrame("world."))
|
||||
await incoming_queue.put(EndFrame())
|
||||
|
||||
await pipeline.run_pipeline()
|
||||
|
||||
self.assertEqual(await outgoing_queue.get(), TextFrame("Hello, world."))
|
||||
self.assertIsInstance(await outgoing_queue.get(), EndFrame)
|
||||
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
async def test_pipeline_multiple_stages(self):
|
||||
sentence_aggregator = SentenceAggregator()
|
||||
to_upper = StatelessTextTransformer(lambda x: x.upper())
|
||||
add_space = StatelessTextTransformer(lambda x: x + " ")
|
||||
|
||||
outgoing_queue = asyncio.Queue()
|
||||
incoming_queue = asyncio.Queue()
|
||||
pipeline = Pipeline(
|
||||
[add_space, sentence_aggregator, to_upper], incoming_queue, outgoing_queue
|
||||
)
|
||||
|
||||
sentence = "Hello, world. It's me, a pipeline."
|
||||
for c in sentence:
|
||||
await incoming_queue.put(TextFrame(c))
|
||||
await incoming_queue.put(EndFrame())
|
||||
|
||||
await pipeline.run_pipeline()
|
||||
|
||||
self.assertEqual(await outgoing_queue.get(), TextFrame("H E L L O , W O R L D ."))
|
||||
self.assertEqual(
|
||||
await outgoing_queue.get(),
|
||||
TextFrame(" I T ' S M E , A P I P E L I N E ."),
|
||||
)
|
||||
# leftover little bit because of the spacing
|
||||
self.assertEqual(
|
||||
await outgoing_queue.get(),
|
||||
TextFrame(" "),
|
||||
)
|
||||
self.assertIsInstance(await outgoing_queue.get(), EndFrame)
|
||||
|
||||
|
||||
class TestLogFrame(unittest.TestCase):
|
||||
class MockProcessor(FrameProcessor):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def setUp(self):
|
||||
self.processor1 = self.MockProcessor("processor1")
|
||||
self.processor2 = self.MockProcessor("processor2")
|
||||
self.pipeline = Pipeline(processors=[self.processor1, self.processor2])
|
||||
self.pipeline._name = "MyClass"
|
||||
self.pipeline._logger = Mock()
|
||||
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
def test_log_frame_from_source(self):
|
||||
frame = Mock(__class__=Mock(__name__="MyFrame"))
|
||||
self.pipeline._log_frame(frame, depth=1)
|
||||
self.pipeline._logger.debug.assert_called_once_with(
|
||||
"MyClass source -> MyFrame -> processor1"
|
||||
)
|
||||
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
def test_log_frame_to_sink(self):
|
||||
frame = Mock(__class__=Mock(__name__="MyFrame"))
|
||||
self.pipeline._log_frame(frame, depth=3)
|
||||
self.pipeline._logger.debug.assert_called_once_with(
|
||||
"MyClass processor2 -> MyFrame -> sink"
|
||||
)
|
||||
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
def test_log_frame_repeated_log(self):
|
||||
frame = Mock(__class__=Mock(__name__="MyFrame"))
|
||||
self.pipeline._log_frame(frame, depth=2)
|
||||
self.pipeline._logger.debug.assert_called_once_with(
|
||||
"MyClass processor1 -> MyFrame -> processor2"
|
||||
)
|
||||
self.pipeline._log_frame(frame, depth=2)
|
||||
self.pipeline._logger.debug.assert_called_with("MyClass ... repeated")
|
||||
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
def test_log_frame_reset_repeated_log(self):
|
||||
frame1 = Mock(__class__=Mock(__name__="MyFrame1"))
|
||||
frame2 = Mock(__class__=Mock(__name__="MyFrame2"))
|
||||
self.pipeline._log_frame(frame1, depth=2)
|
||||
self.pipeline._logger.debug.assert_called_once_with(
|
||||
"MyClass processor1 -> MyFrame1 -> processor2"
|
||||
)
|
||||
self.pipeline._log_frame(frame1, depth=2)
|
||||
self.pipeline._logger.debug.assert_called_with("MyClass ... repeated")
|
||||
self.pipeline._log_frame(frame2, depth=2)
|
||||
self.pipeline._logger.debug.assert_called_with(
|
||||
"MyClass processor1 -> MyFrame2 -> processor2"
|
||||
)
|
||||
29
tests/skipped/test_protobuf_serializer.py
Normal file
29
tests/skipped/test_protobuf_serializer.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import unittest
|
||||
|
||||
from pipecat.frames.frames import AudioRawFrame, TextFrame, TranscriptionFrame
|
||||
from pipecat.serializers.protobuf import ProtobufFrameSerializer
|
||||
|
||||
|
||||
class TestProtobufFrameSerializer(unittest.IsolatedAsyncioTestCase):
|
||||
def setUp(self):
|
||||
self.serializer = ProtobufFrameSerializer()
|
||||
|
||||
@unittest.skip("FIXME: This test is failing")
|
||||
async def test_roundtrip(self):
|
||||
text_frame = TextFrame(text="hello world")
|
||||
frame = self.serializer.deserialize(self.serializer.serialize(text_frame))
|
||||
self.assertEqual(frame, TextFrame(text="hello world"))
|
||||
|
||||
transcription_frame = TranscriptionFrame(
|
||||
text="Hello there!", participantId="123", timestamp="2021-01-01"
|
||||
)
|
||||
frame = self.serializer.deserialize(self.serializer.serialize(transcription_frame))
|
||||
self.assertEqual(frame, transcription_frame)
|
||||
|
||||
audio_frame = AudioRawFrame(data=b"1234567890")
|
||||
frame = self.serializer.deserialize(self.serializer.serialize(audio_frame))
|
||||
self.assertEqual(frame, audio_frame)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
113
tests/skipped/test_websocket_transport.py
Normal file
113
tests/skipped/test_websocket_transport.py
Normal file
@@ -0,0 +1,113 @@
|
||||
# import asyncio
|
||||
# import unittest
|
||||
# from unittest.mock import AsyncMock, patch, Mock
|
||||
|
||||
# from pipecat.pipeline.frames import AudioFrame, EndFrame, TextFrame, TTSEndFrame, TTSStartFrame
|
||||
# from pipecat.pipeline.pipeline import Pipeline
|
||||
# from pipecat.transports.websocket_transport import WebSocketFrameProcessor, WebsocketTransport
|
||||
|
||||
|
||||
# class TestWebSocketTransportService(unittest.IsolatedAsyncioTestCase):
|
||||
# def setUp(self):
|
||||
# self.transport = WebsocketTransport(host="localhost", port=8765)
|
||||
# self.pipeline = Pipeline([])
|
||||
# self.sample_frame = TextFrame("Hello there!")
|
||||
# self.serialized_sample_frame = self.transport._serializer.serialize(
|
||||
# self.sample_frame)
|
||||
|
||||
# async def queue_frame(self):
|
||||
# await asyncio.sleep(0.1)
|
||||
# await self.pipeline.queue_frames([self.sample_frame, EndFrame()])
|
||||
|
||||
# async def test_websocket_handler(self):
|
||||
# mock_websocket = AsyncMock()
|
||||
|
||||
# with patch("websockets.serve", return_value=AsyncMock()) as mock_serve:
|
||||
# mock_serve.return_value.__anext__.return_value = (
|
||||
# mock_websocket, "/")
|
||||
|
||||
# await self.transport._websocket_handler(mock_websocket, "/")
|
||||
|
||||
# await asyncio.gather(self.transport.run(self.pipeline), self.queue_frame())
|
||||
# self.assertEqual(mock_websocket.send.call_count, 1)
|
||||
|
||||
# self.assertEqual(
|
||||
# mock_websocket.send.call_args[0][0], self.serialized_sample_frame)
|
||||
|
||||
# async def test_on_connection_decorator(self):
|
||||
# mock_websocket = AsyncMock()
|
||||
|
||||
# connection_handler_called = asyncio.Event()
|
||||
|
||||
# @self.transport.on_connection
|
||||
# async def connection_handler():
|
||||
# connection_handler_called.set()
|
||||
|
||||
# with patch("websockets.serve", return_value=AsyncMock()):
|
||||
# await self.transport._websocket_handler(mock_websocket, "/")
|
||||
|
||||
# self.assertTrue(connection_handler_called.is_set())
|
||||
|
||||
# async def test_frame_processor(self):
|
||||
# processor = WebSocketFrameProcessor(audio_frame_size=4)
|
||||
|
||||
# source_frames = [
|
||||
# TTSStartFrame(),
|
||||
# AudioFrame(b"1234"),
|
||||
# AudioFrame(b"5678"),
|
||||
# TTSEndFrame(),
|
||||
# TextFrame("hello world")
|
||||
# ]
|
||||
|
||||
# frames = []
|
||||
# for frame in source_frames:
|
||||
# async for output_frame in processor.process_frame(frame):
|
||||
# frames.append(output_frame)
|
||||
|
||||
# self.assertEqual(len(frames), 3)
|
||||
# self.assertIsInstance(frames[0], AudioFrame)
|
||||
# self.assertEqual(frames[0].data, b"1234")
|
||||
# self.assertIsInstance(frames[1], AudioFrame)
|
||||
# self.assertEqual(frames[1].data, b"5678")
|
||||
# self.assertIsInstance(frames[2], TextFrame)
|
||||
# self.assertEqual(frames[2].text, "hello world")
|
||||
|
||||
# async def test_serializer_parameter(self):
|
||||
# mock_websocket = AsyncMock()
|
||||
|
||||
# # Test with ProtobufFrameSerializer (default)
|
||||
# with patch("websockets.serve", return_value=AsyncMock()) as mock_serve:
|
||||
# mock_serve.return_value.__anext__.return_value = (
|
||||
# mock_websocket, "/")
|
||||
|
||||
# await self.transport._websocket_handler(mock_websocket, "/")
|
||||
|
||||
# await asyncio.gather(self.transport.run(self.pipeline), self.queue_frame())
|
||||
# self.assertEqual(mock_websocket.send.call_count, 1)
|
||||
# self.assertEqual(
|
||||
# mock_websocket.send.call_args[0][0],
|
||||
# self.serialized_sample_frame,
|
||||
# )
|
||||
|
||||
# # Test with a mock serializer
|
||||
# mock_serializer = Mock()
|
||||
# mock_serializer.serialize.return_value = b"mock_serialized_data"
|
||||
# self.transport = WebsocketTransport(
|
||||
# host="localhost", port=8765, serializer=mock_serializer
|
||||
# )
|
||||
# mock_websocket.reset_mock()
|
||||
# with patch("websockets.serve", return_value=AsyncMock()) as mock_serve:
|
||||
# mock_serve.return_value.__anext__.return_value = (
|
||||
# mock_websocket, "/")
|
||||
|
||||
# await self.transport._websocket_handler(mock_websocket, "/")
|
||||
# await asyncio.gather(self.transport.run(self.pipeline), self.queue_frame())
|
||||
# self.assertEqual(mock_websocket.send.call_count, 1)
|
||||
# self.assertEqual(
|
||||
# mock_websocket.send.call_args[0][0], b"mock_serialized_data")
|
||||
# mock_serializer.serialize.assert_called_once_with(
|
||||
# TextFrame("Hello there!"))
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# unittest.main()
|
||||
Reference in New Issue
Block a user