|
|
@@ -36,8 +36,7 @@ class VoiceTests(unittest.IsolatedAsyncioTestCase):
|
|
|
self.sink.queue.task_done()
|
|
|
self.assertEqual(user.id, 1)
|
|
|
self.assertEqual(pcm, self.pcm)
|
|
|
- self.assertFalse(self.sink.buffers)
|
|
|
- self.assertTrue(self.sink.busy)
|
|
|
+ self.assertIn(2, self.sink.buffers)
|
|
|
|
|
|
async def test_audio_is_bounded_and_short_noise_ignored(self):
|
|
|
self.sink.worker_task.cancel()
|
|
|
@@ -76,55 +75,48 @@ class VoiceTests(unittest.IsolatedAsyncioTestCase):
|
|
|
await asyncio.wait_for(self.sink.queue.join(), 2)
|
|
|
self.sink.queue.put_nowait((self.user, self.pcm))
|
|
|
await asyncio.wait_for(self.sink.queue.join(), 2)
|
|
|
- self.reply.assert_awaited_once_with(self.user, 'Bonjour')
|
|
|
+ self.reply.assert_awaited_once_with([(self.user, 'Bonjour')])
|
|
|
payload = post.call_args.kwargs['files']['file'][1]
|
|
|
with wave.open(BytesIO(payload)) as wav:
|
|
|
self.assertEqual((wav.getnchannels(), wav.getsampwidth(), wav.getframerate()), (2, 2, 48000))
|
|
|
self.assertEqual(wav.readframes(wav.getnframes()), self.pcm)
|
|
|
|
|
|
- async def test_no_backlog_during_stt_and_reply(self):
|
|
|
+ async def test_transcriptions_continue_and_next_reply_groups_speakers(self):
|
|
|
await self.sink.close()
|
|
|
- stt_started, release_stt = asyncio.Event(), asyncio.Event()
|
|
|
- reply_started, release_reply = asyncio.Event(), asyncio.Event()
|
|
|
-
|
|
|
- async def post(*args, **kwargs):
|
|
|
- stt_started.set()
|
|
|
- await release_stt.wait()
|
|
|
- return httpx.Response(200, json={'text': 'Bonjour'},
|
|
|
- request=httpx.Request('POST', 'http://stt'))
|
|
|
-
|
|
|
- async def reply(*args):
|
|
|
- reply_started.set()
|
|
|
- await release_reply.wait()
|
|
|
+ started, release, second = asyncio.Event(), asyncio.Event(), asyncio.Event()
|
|
|
+ displayed = AsyncMock()
|
|
|
+ batches = []
|
|
|
+
|
|
|
+ async def reply(batch):
|
|
|
+ batches.append(batch)
|
|
|
+ if len(batches) == 1:
|
|
|
+ started.set()
|
|
|
+ await release.wait()
|
|
|
+ else:
|
|
|
+ second.set()
|
|
|
|
|
|
http = AsyncMock()
|
|
|
- http.__aenter__.return_value.post.side_effect = post
|
|
|
- self.reply.side_effect = reply
|
|
|
+ http.__aenter__.return_value.post.side_effect = [
|
|
|
+ httpx.Response(200, json={'text': text}, request=httpx.Request('POST', 'http://stt'))
|
|
|
+ for text in ('Bonjour', 'Minecraft ?', 'Avec des mods')
|
|
|
+ ]
|
|
|
other = SimpleNamespace(id=2)
|
|
|
with patch('voice_stt.httpx.AsyncClient', return_value=http):
|
|
|
- self.sink = STTSink(self.reply, 'http://stt/transcribe')
|
|
|
- with self.sink.lock:
|
|
|
- self.sink.add_pcm(self.user, self.pcm, time.monotonic() - 2)
|
|
|
- self.sink.add_pcm(other, self.pcm, time.monotonic() - 2)
|
|
|
- await asyncio.wait_for(stt_started.wait(), 2)
|
|
|
- with self.sink.lock:
|
|
|
- self.sink.add_pcm(other, self.pcm, time.monotonic())
|
|
|
- release_stt.set()
|
|
|
- await asyncio.wait_for(reply_started.wait(), 2)
|
|
|
- with self.sink.lock:
|
|
|
- self.sink.add_pcm(other, self.pcm, time.monotonic())
|
|
|
- self.assertTrue(self.sink.queue.empty())
|
|
|
- self.assertFalse(self.sink.buffers)
|
|
|
- release_reply.set()
|
|
|
+ self.sink = STTSink(reply, 'http://stt/transcribe', on_transcription=displayed)
|
|
|
+ self.sink.queue.put_nowait((self.user, self.pcm))
|
|
|
+ await asyncio.wait_for(started.wait(), 2)
|
|
|
+ self.sink.queue.put_nowait((other, self.pcm))
|
|
|
+ self.sink.queue.put_nowait((self.user, self.pcm))
|
|
|
await asyncio.wait_for(self.sink.queue.join(), 2)
|
|
|
- self.assertFalse(self.sink.busy)
|
|
|
- self.reply.assert_awaited_once_with(self.user, 'Bonjour')
|
|
|
+ self.assertEqual(displayed.await_count, 3)
|
|
|
+ self.assertEqual(len(batches), 1)
|
|
|
with self.sink.lock:
|
|
|
- now = time.monotonic()
|
|
|
- self.sink.add_pcm(other, self.pcm, now)
|
|
|
- self.assertFalse(self.sink.buffers) # Ne pas récupérer la fin d'une phrase ignorée.
|
|
|
- self.sink.add_pcm(other, self.pcm, now + 1)
|
|
|
+ self.sink.add_pcm(other, self.pcm, time.monotonic())
|
|
|
self.assertIn(2, self.sink.buffers)
|
|
|
+ release.set()
|
|
|
+ await asyncio.wait_for(second.wait(), 2)
|
|
|
+ self.assertEqual(batches[1], [(other, 'Minecraft ?'), (self.user, 'Avec des mods')])
|
|
|
+ self.assertFalse(self.sink.pending)
|
|
|
|
|
|
async def test_close_discards_pending_audio(self):
|
|
|
self.sink.queue.put_nowait((self.user, self.pcm))
|
|
|
@@ -133,6 +125,7 @@ class VoiceTests(unittest.IsolatedAsyncioTestCase):
|
|
|
self.assertTrue(self.sink.queue.empty())
|
|
|
self.assertTrue(self.sink.flush_task.done())
|
|
|
self.assertTrue(self.sink.worker_task.done())
|
|
|
+ self.assertTrue(self.sink.reply_task.done())
|
|
|
self.reply.assert_not_awaited()
|
|
|
|
|
|
async def test_diagnostics_distinguish_ignored_audio_and_dave_failure(self):
|
|
|
@@ -198,8 +191,9 @@ class CommandTests(unittest.IsolatedAsyncioTestCase):
|
|
|
await namespace['handle_voice_command'](message)
|
|
|
voice.listen.assert_called_once_with(sink, after=sink.receiver_stopped)
|
|
|
callback = make_sink.call_args.args[0]
|
|
|
- await callback(SimpleNamespace(display_name='Alice'), 'Bonjour')
|
|
|
- call_api.assert_awaited_once_with('Bonjour', 'Alice')
|
|
|
+ await make_sink.call_args.kwargs['on_transcription'](SimpleNamespace(display_name='Alice'), 'Bonjour')
|
|
|
+ await callback([(SimpleNamespace(display_name='Alice'), 'Bonjour')])
|
|
|
+ call_api.assert_awaited_once_with('Alice : Bonjour', 'Conversation vocale', voice=True)
|
|
|
self.assertEqual(message.channel.send.call_args.args[0], 'Réponse')
|
|
|
quote = message.channel.send.call_args_list[-2]
|
|
|
self.assertEqual(quote.args[0], '> 🎙 Alice : Bonjour')
|