def setUp(self): self.poller = PollerSpy() self.scale_workers = ScaleWorkersSpy() self.create_poller = lambda: self.poller self.monitor = Monitor(self.create_poller, self.emit, self.scale_workers, self.poller.has_next_message) self.emmited_messages = []
def setUp(self): self.model = "en-GB" self.worker_address = "tcp://127.0.0.1:5678" self.master_socket = SocketSpy() self.saver = SaverSpy() self.heartbeat = Heartbeat(self.model, self.worker_address, self.master_socket) self.poller = PollerSpy() self.asr = ASRSpy([(1.0, "Hello World!")], (1.0, "Interim result")) self.audio = DummyAudio() self.worker = Worker(self.poller, self.heartbeat, self.asr, self.audio, self.saver, self.poller.has_next_message)
def setUp(self): self.poller = PollerSpy() self.monitor_socket = SocketSpy() self.master = Master(self.poller, self.monitor_socket, self.poller.has_next_message)
class TestMaster(unittest.TestCase): def setUp(self): self.poller = PollerSpy() self.monitor_socket = SocketSpy() self.master = Master(self.poller, self.monitor_socket, self.poller.has_next_message) def test_when_no_worker_is_available_master_responds_with_error(self): messages = [ {"frontend": self.make_frontend_request()}, ] self.run_master(messages) expected_message = self.make_frontend_error_response("No worker available") self.assertThatMessagesWereSendToFrontend([expected_message]) def test_when_no_appropriate_worker_is_available_master_responds_with_error(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-US", "STARTED")}, {"frontend": self.make_frontend_request("en-GB")} ] self.run_master(messages) expected_message = self.make_frontend_error_response("No worker available") self.assertThatMessagesWereSendToFrontend([expected_message]) def test_when_appropriate_worker_is_available_master_sends_its_address_to_client(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, {"frontend": self.make_frontend_request()}, ] self.run_master(messages) expected_message = self.make_frontend_successfull_response("tcp://127.0.0.1:1") self.assertThatMessagesWereSendToFrontend([expected_message]) def test_worker_cant_be_assigned_to_another_client_before_finishing_its_task(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, {"frontend": self.make_frontend_request()}, {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WORKING")}, {"frontend": self.make_frontend_request()} ] self.run_master(messages) expected_message1 = self.make_frontend_successfull_response("tcp://127.0.0.1:1") expected_message2 = self.make_frontend_error_response("No worker available") self.assertThatMessagesWereSendToFrontend([expected_message1, expected_message2]) def test_when_worker_finished_its_task_it_can_be_assigned_to_another_client(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, {"frontend": self.make_frontend_request()}, {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "FINISHED")}, {"frontend": self.make_frontend_request()} ] self.run_master(messages) expected_message = self.make_frontend_successfull_response("tcp://127.0.0.1:1") self.assertThatMessagesWereSendToFrontend([expected_message, expected_message]) def test_when_worker_sends_two_heartbeats_it_is_available_only_to_first_frontend_request(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING")}, {"frontend": self.make_frontend_request()}, {"frontend": self.make_frontend_request()} ] self.run_master(messages) expected_message1 = self.make_frontend_successfull_response("tcp://127.0.0.1:1") expected_message2 = self.make_frontend_error_response("No worker available") self.assertThatMessagesWereSendToFrontend([expected_message1, expected_message2]) def test_when_worker_sent_heartbeat_and_went_silent_for_10secs_then_it_is_not_available_anymore(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, {"frontend": self.make_frontend_request(), "time": +10} ] self.run_master(messages) expected_message = self.make_frontend_error_response("No worker available") self.assertThatMessagesWereSendToFrontend([expected_message]) def test_when_worker_was_not_responding_and_then_it_sent_heartbeat_it_should_be_available_again(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING"), "time": +100}, {"frontend": self.make_frontend_request()} ] self.run_master(messages) expected_message = self.make_frontend_successfull_response("tcp://127.0.0.1:1") self.assertThatMessagesWereSendToFrontend([expected_message]) def test_when_worker_crashed_and_then_it_sent_running_heartbeat_it_should_be_available_again(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, {"frontend": self.make_frontend_request()}, {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WORKING")}, {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, {"frontend": self.make_frontend_request()} ] self.run_master(messages) expected_message = self.make_frontend_successfull_response("tcp://127.0.0.1:1") self.assertThatMessagesWereSendToFrontend([expected_message, expected_message]) def test_when_worker_did_not_receive_first_chunk_for_10_secs_it_should_be_available_again(self): messages1 = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, {"frontend": self.make_frontend_request()} ] messages2 = [{"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING")}] * 10 messages3 = [{"frontend": self.make_frontend_request()}] self.run_master(messages1 + messages2 + messages3) expected_message = self.make_frontend_successfull_response("tcp://127.0.0.1:1") self.assertThatMessagesWereSendToFrontend([expected_message, expected_message]) def test_when_worker_sent_running_heartbeat_master_informs_monitor_that_the_worker_has_been_started(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, ] self.run_master(messages) expected_message = self.make_worker_status_message("tcp://127.0.0.1:1", "en-GB", "STARTED", 1) self.assertThatMessagesWereSendToMonitor([expected_message]) def test_when_worker_sent_ready_heartbeat_master_informs_monitor_that_the_worker_is_waiting(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED")}, {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING")}, ] self.run_master(messages) expected_message1 = self.make_worker_status_message("tcp://127.0.0.1:1", "en-GB", "STARTED", 1) expected_message2 = self.make_worker_status_message("tcp://127.0.0.1:1", "en-GB", "WAITING", 2) self.assertThatMessagesWereSendToMonitor([expected_message1, expected_message2]) def test_when_worker_is_assigned_to_frontend_master_informs_monitor_that_the_worker_is_working(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING")}, {"frontend": self.make_frontend_request()}, ] self.run_master(messages) expected_message1 = self.make_worker_status_message("tcp://127.0.0.1:1", "en-GB", "STARTED", 1) expected_message2 = self.make_worker_status_message("tcp://127.0.0.1:1", "en-GB", "WORKING", 2) self.assertThatMessagesWereSendToMonitor([expected_message1, expected_message2]) def test_when_worker_is_finished_master_informs_monitor_that_the_worker_is_waiting(self): messages = [ {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING")}, {"frontend": self.make_frontend_request()}, {"worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "FINISHED")}, ] self.run_master(messages) expected_message1 = self.make_worker_status_message("tcp://127.0.0.1:1", "en-GB", "STARTED", 1) expected_message2 = self.make_worker_status_message("tcp://127.0.0.1:1", "en-GB", "WORKING", 2) expected_message3 = self.make_worker_status_message("tcp://127.0.0.1:1", "en-GB", "WAITING", 3) self.assertThatMessagesWereSendToMonitor([expected_message1, expected_message2, expected_message3]) def run_master(self, messages): self.poller.add_messages(messages) self.master.run() def assertThatMessagesWereSendToFrontend(self, messages): sent_messages = [parseMasterResponseMessage(message) for message in self.poller.sent_messages["frontend"]] self.assertEquals(messages, sent_messages) def assertThatMessagesWereSendToMonitor(self, messages): sent_messages = [parseWorkerStatusMessage(message) for message in self.monitor_socket.sent_messages] self.assertEquals(messages, sent_messages) def make_heartbeat_request(self, worker_address, model, status): return createHeartbeatMessage(worker_address, model, status).SerializeToString() def make_frontend_request(self, model="en-GB"): return createWorkerRequestMessage(model).SerializeToString() def make_frontend_successfull_response(self, address): return createMasterResponseMessage("SUCCESS", address) def make_frontend_error_response(self, message): return createMasterResponseMessage("ERROR") def make_worker_status_message(self, address, model, status, time): return createWorkerStatusMessage(address, model, status, time)
class TestWorker(unittest.TestCase): def setUp(self): self.model = "en-GB" self.worker_address = "tcp://127.0.0.1:5678" self.master_socket = SocketSpy() self.saver = SaverSpy() self.vad = VADDummy() self.id_generator = IDGeneratorDummy() self.heartbeat = Heartbeat(self.model, self.worker_address, self.master_socket) self.poller = PollerSpy() self.asr = ASRSpy([(1.0, "Hello World!")], (1.0, "Interim result")) self.audio = DummyAudio() self.worker = Worker(self.poller, self.heartbeat, self.asr, self.audio, self.saver, self.vad, self.id_generator, self.poller.has_next_message) def test_worker_forwards_resampled_wav_from_every_message_to_asr_as_pcm( self): messages = [{ "frontend": self.make_frontend_request("message 1") }, { "frontend": self.make_frontend_request("message 2") }] self.run_worker(messages) self.assertThatAsrProcessedChunks( ["resampled pcm message 1", "resampled pcm message 2"]) def test_worker_reads_final_hypothesis_from_asr(self): messages = [{ "frontend": self.make_frontend_request("message 1") }, { "frontend": self.make_frontend_request("message 2") }] self.run_worker(messages) expected_message = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend( [expected_message, expected_message]) def test_worker_sends_interim_results_after_each_chunk(self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE") }, { "frontend": self.make_frontend_request("message 2", "ONLINE") }] self.run_worker(messages) expected_message = createResultsMessage([(0, False, [(1.0, "Interim result")])]) self.assertThatMessagesWereSendToFrontend( [expected_message, expected_message]) def test_worker_sends_final_results_after_last_chunk(self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "frontend": self.make_frontend_request("", "ONLINE", has_next=False) }] self.run_worker(messages) expected_message1 = createResultsMessage([(0, False, [(1.0, "Interim result")])]) expected_message2 = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend( [expected_message1, expected_message2]) def test_when_worker_receives_chunk_with_bad_id_it_should_return_error_message( self): messages = [ { "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True, id=1) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", has_next=True, id=2) }, { "frontend": self.make_frontend_request("", "ONLINE", has_next=False, id=1) }, ] self.run_worker(messages) expected_message1 = createResultsMessage([(0, False, [(1.0, "Interim result")])]) expected_message2 = createErrorResultsMessage() expected_message3 = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend( [expected_message1, expected_message2, expected_message3]) def test_worker_forwards_resampled_pcm_chunks_from_every_message_to_asr( self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", has_next=True) }] self.run_worker(messages) self.assertThatAsrProcessedChunks( ["resampled message 1", "resampled message 2"]) def test_worker_sends_heartbeat_to_master_when_ready_to_work(self): messages = [] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED"]) def test_worker_sends_heartbeat_after_finishing_task(self): messages = [{"frontend": self.make_frontend_request("message 1")}] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "FINISHED"]) def test_worker_sends_working_heartbeats_during_online_recognition(self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", has_next=True) }, { "frontend": self.make_frontend_request("", "ONLINE", has_next=False) }] self.run_worker(messages) self.assertThatHeartbeatsWereSent( ["STARTED", "WORKING", "WORKING", "FINISHED"]) def test_worker_sends_finished_heartbeat_after_end_of_online_recognition( self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "frontend": self.make_frontend_request("", "ONLINE", has_next=False) }] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "FINISHED"]) def test_worker_sends_finished_heartbeat_when_it_doesnt_receive_any_chunk_for_1sec( self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "time": +1 }] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "FINISHED"]) def test_worker_sends_resets_asr_engine_when_it_doesnt_receive_any_chunk_for_1sec( self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "time": +1 }] self.run_worker(messages) self.assertTrue(self.asr.resetted) def test_worker_sends_ready_heartbeat_when_it_doesnt_receive_any_task( self): messages = [{}] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WAITING"]) def test_worker_saves_pcm_data_from_batch_request(self): messages = [ { "frontend": self.make_frontend_request("message", "BATCH", id=1, has_next=False) }, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: { "frame_rate": 16000, "chunks": [{ "chunk_id": 0, "pcm": "pcm message", "hypothesis": [(1.0, "Hello World!")] }] } }) def test_worker_saves_pcm_data_from_online_request_in_original_frame_rate( self): messages = [ { "frontend": self.make_frontend_request("message 1", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("", "ONLINE", id=1, has_next=False) }, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: { "frame_rate": 44100, "chunks": [{ "chunk_id": 0, "pcm": "message 1message 2", "hypothesis": [(1.0, "Hello World!")] }] } }) def test_worker_forwards_pcm_data_to_vad(self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", id=1, has_next=True) }] self.run_worker(messages) self.assertThatVadReceivedChunks([("message 1", "resampled message 1") ]) def test_worker_sends_empty_hypothesis_when_vad_detects_silence(self): self.vad.set_messages([ (False, None, "", ""), (False, None, "", ""), ]) messages = [ { "frontend": self.make_frontend_request("silence 1", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("silence 2", "ONLINE", id=1, has_next=True) }, ] self.run_worker(messages) expected_message = createResultsMessage([(0, False, [(1.0, "")])]) self.assertThatMessagesWereSendToFrontend( [expected_message, expected_message]) def test_worker_sends_hypothesis_when_vad_detects_speech(self): self.vad.set_messages([ (True, None, "message 1", "resampled message 1"), (True, None, "message 2", "resampled message 2") ]) messages = [ { "frontend": self.make_frontend_request("speech 1", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("speech 2", "ONLINE", id=1, has_next=True) }, ] self.run_worker(messages) expected_message = createResultsMessage([(0, False, [(1.0, "Interim result")])]) self.assertThatMessagesWereSendToFrontend( [expected_message, expected_message]) def test_worker_sends_final_hypothesis_when_vad_detects_change_to_silence( self): self.vad.set_messages([(True, None, "speech 1", "resampled speech 1"), (False, "non-speech", "", "")]) messages = [ { "frontend": self.make_frontend_request("speech 1", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("silence 1", "ONLINE", id=1, has_next=True) }, ] self.run_worker(messages) expected_message1 = createResultsMessage([(0, False, [(1.0, "Interim result")])]) expected_message2 = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend( [expected_message1, expected_message2]) def test_worker_sends_working_heartbeat_when_vad_detects_change_to_silence( self): self.vad.set_messages([(True, None, "speech 1", "resampled speech 1"), (False, "non-speech", "", "")]) messages = [ { "frontend": self.make_frontend_request("speech 1", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("silence 1", "ONLINE", id=1, has_next=True) }, ] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "WORKING"]) def test_worker_resets_asr_when_vad_detects_change_to_silence(self): self.vad.set_messages([(True, None, "speech 1", "resampled speech 1"), (False, "non-speech", "", "")]) messages = [ { "frontend": self.make_frontend_request("speech 1", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("silence 1", "ONLINE", id=1, has_next=True) }, ] self.run_worker(messages) self.assertTrue(self.asr.resetted) def test_worker_saves_pcm_as_part_of_original_request(self): self.vad.set_messages([(True, None, "speech 1", "resampled speech 1"), (False, "non-speech", "", ""), (True, None, "speech 2", "resampled speech 2"), (False, "non-speech", "", "")]) messages = [ { "frontend": self.make_frontend_request("speech 1", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("silence 1", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("speech 2", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("silence 2", "ONLINE", id=1, has_next=True) }, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: { "frame_rate": 44100, "chunks": [{ "chunk_id": 0, "pcm": "speech 1", "hypothesis": [(1.0, "Hello World!")] }, { "chunk_id": 0, "pcm": "speech 2", "hypothesis": [(1.0, "Hello World!")] }] } }) def test_worker_sends_interim_result_for_last_speech_in_splitted_chunks( self): self.audio.set_chunks(["chunk 1", "chunk 2"]) self.vad.set_messages([ (True, None, "chunk 1", "resampled chunk 1"), (True, None, "chunk 2", "resampled chunk 2"), ]) messages = [ { "frontend": self.make_frontend_request("speech 1", "ONLINE", id=1, has_next=True) }, ] self.run_worker(messages) expected_messages = createResultsMessage([(0, False, [(1.0, "Interim result")])]) self.assertThatMessagesWereSendToFrontend([expected_messages]) def test_worker_sends_final_results_for_each_speech_in_splitted_chunks( self): self.audio.set_chunks(["chunk 1", "chunk 2", "chunk 3", "chunk 4"]) self.vad.set_messages([ (True, None, "chunk 1", "resampled chunk 1"), (False, "non-speech", "", ""), (True, None, "chunk 2", "resampled chunk 2"), (False, "non-speech", "", ""), ]) messages = [ { "frontend": self.make_frontend_request("speech 1", "ONLINE", id=1, has_next=True) }, ] self.run_worker(messages) expected_messages = createResultsMessage( [(0, True, [(1.0, "Hello World!")])] * 2) self.assertThatMessagesWereSendToFrontend([expected_messages]) def test_worker_sends_buffered_chunks_to_saver_when_speech_is_detected( self): self.vad.set_messages([(True, "speech", "buffered chunk", "resampled buffered chunk"), (False, "non-speech", "", "")]) messages = [ { "frontend": self.make_frontend_request("speech 1", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("speech 2", "ONLINE", id=1, has_next=True) }, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: { "frame_rate": 44100, "chunks": [ { "chunk_id": 0, "pcm": "buffered chunk", "hypothesis": [(1.0, "Hello World!")] }, ] } }) def test_worker_resets_vad_at_the_end_of_recognition(self): messages = [{ "frontend": self.make_frontend_request("", "ONLINE", has_next=False) }] self.run_worker(messages) self.assertTrue(self.vad.resetted) def test_worker_resets_audio_utils_at_the_end_of_online_recognition(self): messages = [{ "frontend": self.make_frontend_request("", "ONLINE", has_next=False) }] self.run_worker(messages) self.assertTrue(self.audio.resetted) def test_worker_change_lm_when_new_lm_is_set(self): messages = [{ "frontend": self.make_frontend_request("", "ONLINE", new_lm="new_lm") }] self.run_worker(messages) self.assertEquals("new_lm", self.asr.lm) def test_worker_sends_final_result_when_lm_is_changed(self): self.audio.set_chunks([]) messages = [{ "frontend": self.make_frontend_request("", "ONLINE", new_lm="new_lm") }] self.run_worker(messages) expected_messages = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend([expected_messages]) def test_worker_sets_lm_to_default_at_the_end_of_recognition(self): messages = [{ "frontend": self.make_frontend_request("", "ONLINE", has_next=False) }] self.run_worker(messages) self.assertEquals("default", self.asr.lm) def test_worker_assings_unique_id_to_each_chunk(self): self.id_generator.set_ids([0, 1]) self.audio.set_chunks(["chunk 1", "chunk 2", "chunk 3", "chunk 4"]) self.vad.set_messages([ (True, None, "chunk 1", "resampled chunk 1"), (False, "non-speech", "", ""), (True, None, "chunk 2", "resampled chunk 2"), (False, "non-speech", "", ""), ]) messages = [ { "frontend": self.make_frontend_request("speech 1", "ONLINE", id=1, has_next=True) }, ] self.run_worker(messages) expected_messages = createResultsMessage([ (0, True, [(1.0, "Hello World!")]), (1, True, [(1.0, "Hello World!")]) ]) self.assertThatMessagesWereSendToFrontend([expected_messages]) def test_worker_assings_unique_id_to_each_batch_request(self): self.id_generator.set_ids([0, 1]) messages = [ { "frontend": self.make_frontend_request("speech 1", "BATCH", id=1, has_next=False) }, { "frontend": self.make_frontend_request("speech 2", "BATCH", id=1, has_next=False) }, ] self.run_worker(messages) expected_message1 = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) expected_message2 = createResultsMessage([(1, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend( [expected_message1, expected_message2]) def run_worker(self, messages): self.poller.add_messages(messages) self.worker.run() def assertThatAsrProcessedChunks(self, chunks): self.assertEquals(chunks, self.asr.processed_chunks) def assertThatMessagesWereSendToFrontend(self, messages): sent_messages = [ parseResultsMessage(message) for message in self.poller.sent_messages["frontend"] ] self.assertEquals(messages, sent_messages) def assertThatHeartbeatsWereSent(self, heartbeats): heartbeats = [self.make_heartbeat(state) for state in heartbeats] sent_heartbeats = [ parseHeartbeatMessage(message) for message in self.master_socket.sent_messages ] self.assertEquals(heartbeats, sent_heartbeats) def assertThatDataWasStored(self, data): self.assertEquals(data, self.saver.saved_data) def assertThatVadReceivedChunks(self, data): self.assertEquals(data, self.vad.data) def make_frontend_request(self, message, type="BATCH", has_next=True, id=0, new_lm=""): return createRecognitionRequestMessage(type, message, has_next, id, 44100, new_lm).SerializeToString() def make_heartbeat(self, status): return createHeartbeatMessage(self.worker_address, self.model, status)
class TestMaster(unittest.TestCase): def setUp(self): self.poller = PollerSpy() self.monitor_socket = SocketSpy() self.master = Master(self.poller, self.monitor_socket, self.poller.has_next_message) def test_when_no_worker_is_available_master_responds_with_error(self): messages = [ { "frontend": self.make_frontend_request() }, ] self.run_master(messages) expected_message = self.make_frontend_error_response( "No worker available") self.assertThatMessagesWereSendToFrontend([expected_message]) def test_when_no_appropriate_worker_is_available_master_responds_with_error( self): messages = [{ "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-US", "STARTED") }, { "frontend": self.make_frontend_request("en-GB") }] self.run_master(messages) expected_message = self.make_frontend_error_response( "No worker available") self.assertThatMessagesWereSendToFrontend([expected_message]) def test_when_appropriate_worker_is_available_master_sends_its_address_to_client( self): messages = [ { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, { "frontend": self.make_frontend_request() }, ] self.run_master(messages) expected_message = self.make_frontend_successfull_response( "tcp://127.0.0.1:1") self.assertThatMessagesWereSendToFrontend([expected_message]) def test_worker_cant_be_assigned_to_another_client_before_finishing_its_task( self): messages = [{ "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, { "frontend": self.make_frontend_request() }, { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WORKING") }, { "frontend": self.make_frontend_request() }] self.run_master(messages) expected_message1 = self.make_frontend_successfull_response( "tcp://127.0.0.1:1") expected_message2 = self.make_frontend_error_response( "No worker available") self.assertThatMessagesWereSendToFrontend( [expected_message1, expected_message2]) def test_when_worker_finished_its_task_it_can_be_assigned_to_another_client( self): messages = [{ "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, { "frontend": self.make_frontend_request() }, { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "FINISHED") }, { "frontend": self.make_frontend_request() }] self.run_master(messages) expected_message = self.make_frontend_successfull_response( "tcp://127.0.0.1:1") self.assertThatMessagesWereSendToFrontend( [expected_message, expected_message]) def test_when_worker_sends_two_heartbeats_it_is_available_only_to_first_frontend_request( self): messages = [{ "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING") }, { "frontend": self.make_frontend_request() }, { "frontend": self.make_frontend_request() }] self.run_master(messages) expected_message1 = self.make_frontend_successfull_response( "tcp://127.0.0.1:1") expected_message2 = self.make_frontend_error_response( "No worker available") self.assertThatMessagesWereSendToFrontend( [expected_message1, expected_message2]) def test_when_worker_sent_heartbeat_and_went_silent_for_10secs_then_it_is_not_available_anymore( self): messages = [{ "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, { "frontend": self.make_frontend_request(), "time": +10 }] self.run_master(messages) expected_message = self.make_frontend_error_response( "No worker available") self.assertThatMessagesWereSendToFrontend([expected_message]) def test_when_worker_was_not_responding_and_then_it_sent_heartbeat_it_should_be_available_again( self): messages = [{ "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING"), "time": +100 }, { "frontend": self.make_frontend_request() }] self.run_master(messages) expected_message = self.make_frontend_successfull_response( "tcp://127.0.0.1:1") self.assertThatMessagesWereSendToFrontend([expected_message]) def test_when_worker_crashed_and_then_it_sent_running_heartbeat_it_should_be_available_again( self): messages = [{ "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, { "frontend": self.make_frontend_request() }, { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WORKING") }, { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, { "frontend": self.make_frontend_request() }] self.run_master(messages) expected_message = self.make_frontend_successfull_response( "tcp://127.0.0.1:1") self.assertThatMessagesWereSendToFrontend( [expected_message, expected_message]) def test_when_worker_did_not_receive_first_chunk_for_10_secs_it_should_be_available_again( self): messages1 = [{ "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, { "frontend": self.make_frontend_request() }] messages2 = [{ "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING") }] * 10 messages3 = [{"frontend": self.make_frontend_request()}] self.run_master(messages1 + messages2 + messages3) expected_message = self.make_frontend_successfull_response( "tcp://127.0.0.1:1") self.assertThatMessagesWereSendToFrontend( [expected_message, expected_message]) def test_when_worker_sent_running_heartbeat_master_informs_monitor_that_the_worker_has_been_started( self): messages = [ { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, ] self.run_master(messages) expected_message = self.make_worker_status_message( "tcp://127.0.0.1:1", "en-GB", "STARTED", 1) self.assertThatMessagesWereSendToMonitor([expected_message]) def test_when_worker_sent_ready_heartbeat_master_informs_monitor_that_the_worker_is_waiting( self): messages = [ { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "STARTED") }, { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING") }, ] self.run_master(messages) expected_message1 = self.make_worker_status_message( "tcp://127.0.0.1:1", "en-GB", "STARTED", 1) expected_message2 = self.make_worker_status_message( "tcp://127.0.0.1:1", "en-GB", "WAITING", 2) self.assertThatMessagesWereSendToMonitor( [expected_message1, expected_message2]) def test_when_worker_is_assigned_to_frontend_master_informs_monitor_that_the_worker_is_working( self): messages = [ { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING") }, { "frontend": self.make_frontend_request() }, ] self.run_master(messages) expected_message1 = self.make_worker_status_message( "tcp://127.0.0.1:1", "en-GB", "STARTED", 1) expected_message2 = self.make_worker_status_message( "tcp://127.0.0.1:1", "en-GB", "WORKING", 2) self.assertThatMessagesWereSendToMonitor( [expected_message1, expected_message2]) def test_when_worker_is_finished_master_informs_monitor_that_the_worker_is_waiting( self): messages = [ { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "WAITING") }, { "frontend": self.make_frontend_request() }, { "worker": self.make_heartbeat_request("tcp://127.0.0.1:1", "en-GB", "FINISHED") }, ] self.run_master(messages) expected_message1 = self.make_worker_status_message( "tcp://127.0.0.1:1", "en-GB", "STARTED", 1) expected_message2 = self.make_worker_status_message( "tcp://127.0.0.1:1", "en-GB", "WORKING", 2) expected_message3 = self.make_worker_status_message( "tcp://127.0.0.1:1", "en-GB", "WAITING", 3) self.assertThatMessagesWereSendToMonitor( [expected_message1, expected_message2, expected_message3]) def run_master(self, messages): self.poller.add_messages(messages) self.master.run() def assertThatMessagesWereSendToFrontend(self, messages): sent_messages = [ parseMasterResponseMessage(message) for message in self.poller.sent_messages["frontend"] ] self.assertEquals(messages, sent_messages) def assertThatMessagesWereSendToMonitor(self, messages): sent_messages = [ parseWorkerStatusMessage(message) for message in self.monitor_socket.sent_messages ] self.assertEquals(messages, sent_messages) def make_heartbeat_request(self, worker_address, model, status): return createHeartbeatMessage(worker_address, model, status).SerializeToString() def make_frontend_request(self, model="en-GB"): return createWorkerRequestMessage(model).SerializeToString() def make_frontend_successfull_response(self, address): return createMasterResponseMessage("SUCCESS", address) def make_frontend_error_response(self, message): return createMasterResponseMessage("ERROR") def make_worker_status_message(self, address, model, status, time): return createWorkerStatusMessage(address, model, status, time)
class TestWorker(unittest.TestCase): def setUp(self): self.model = "en-GB" self.worker_address = "tcp://127.0.0.1:5678" self.master_socket = SocketSpy() self.saver = SaverSpy() self.heartbeat = Heartbeat(self.model, self.worker_address, self.master_socket) self.poller = PollerSpy() self.asr = ASRSpy([(1.0, "Hello World!")], (1.0, "Interim result")) self.audio = DummyAudio() self.worker = Worker(self.poller, self.heartbeat, self.asr, self.audio, self.saver, self.poller.has_next_message) def test_worker_forwards_resampled_wav_from_every_message_to_asr_as_pcm(self): messages = [ {"frontend": self.make_frontend_request("message 1")}, {"frontend": self.make_frontend_request("message 2")} ] self.run_worker(messages) self.assertThatAsrProcessedChunks(["resampled pcm message 1", "resampled pcm message 2"]) def test_worker_reads_final_hypothesis_from_asr(self): messages = [ {"frontend": self.make_frontend_request("message 1")}, {"frontend": self.make_frontend_request("message 2")} ] self.run_worker(messages) expected_message = createResultsMessage(True, [(1.0, "Hello World!")]) self.assertThatMessagesWereSendToFrontend([expected_message, expected_message]) def test_worker_sends_interim_results_after_each_chunk(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE")}, {"frontend": self.make_frontend_request("message 2", "ONLINE")} ] self.run_worker(messages) expected_message = createResultsMessage(False, [(1.0, "Interim result")]) self.assertThatMessagesWereSendToFrontend([expected_message, expected_message]) def test_worker_sends_final_results_after_last_chunk(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", has_next = False)} ] self.run_worker(messages) expected_message1 = createResultsMessage(False, [(1.0, "Interim result")]) expected_message2 = createResultsMessage(True, [(1.0, "Hello World!")]) self.assertThatMessagesWereSendToFrontend([expected_message1, expected_message2]) def test_when_worker_receives_chunk_with_bad_id_it_should_return_error_message(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True, id = 1)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", has_next = True, id = 2)}, {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = False, id = 1)}, ] self.run_worker(messages) expected_message1 = createResultsMessage(False, [(1.0, "Interim result")]) expected_message2 = createErrorResultsMessage() expected_message3 = createResultsMessage(True, [(1.0, "Hello World!")]) self.assertThatMessagesWereSendToFrontend([expected_message1, expected_message2, expected_message3]) def test_worker_forwards_resampled_pcm_chunks_from_every_message_to_asr(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", has_next = True)} ] self.run_worker(messages) self.assertThatAsrProcessedChunks(["resampled message 1", "resampled message 2"]) def test_worker_sends_heartbeat_to_master_when_ready_to_work(self): messages = [] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED"]) def test_worker_sends_heartbeat_after_finishing_task(self): messages = [ {"frontend": self.make_frontend_request("message 1")} ] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "FINISHED"]) def test_worker_sends_working_heartbeats_during_online_recognition(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", has_next = True)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", has_next = False)} ] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "WORKING", "FINISHED"]) def test_worker_sends_finished_heartbeat_after_end_of_online_recognition(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", has_next = False)} ] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "FINISHED"]) def test_worker_sends_finished_heartbeat_when_it_doesnt_receive_any_chunk_for_1sec(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"time": +1} ] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "FINISHED"]) def test_worker_sends_resets_asr_engine_when_it_doesnt_receive_any_chunk_for_1sec(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"time": +1} ] self.run_worker(messages) self.assertTrue(self.asr.resetted) def test_worker_sends_ready_heartbeat_when_it_doesnt_receive_any_task(self): messages = [{}] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WAITING"]) def test_worker_saves_pcm_data_from_batch_request(self): messages = [ {"frontend": self.make_frontend_request("message", "BATCH", id = 1, has_next = False)}, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: {"frame_rate": 16000, "pcm": "pcm message", "hypothesis": [(1.0, "Hello World!")]} }) def test_worker_saves_pcm_data_from_online_request_in_original_frame_rate(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("message 3", "ONLINE", id = 1, has_next = False)}, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: {"frame_rate": 44100, "pcm": "message 1message 2message 3", "hypothesis": [(1.0, "Hello World!")]} }) def run_worker(self, messages): self.poller.add_messages(messages) self.worker.run() def assertThatAsrProcessedChunks(self, chunks): self.assertEquals(chunks, self.asr.processed_chunks) def assertThatMessagesWereSendToFrontend(self, messages): sent_messages = [parseResultsMessage(message) for message in self.poller.sent_messages["frontend"]] self.assertEquals(messages, sent_messages) def assertThatHeartbeatsWereSent(self, heartbeats): heartbeats = [self.make_heartbeat(state) for state in heartbeats] sent_heartbeats = [parseHeartbeatMessage(message) for message in self.master_socket.sent_messages] self.assertEquals(heartbeats, sent_heartbeats) def assertThatDataWasStored(self, data): self.assertEquals(data, self.saver.saved_data) def make_frontend_request(self, message, type = "BATCH", has_next = True, id = 0): return createRecognitionRequestMessage(type, message, has_next, id, 44100).SerializeToString() def make_heartbeat(self, status): return createHeartbeatMessage(self.worker_address, self.model, status)
class TestWorker(unittest.TestCase): def setUp(self): self.model = "en-GB" self.worker_address = "tcp://127.0.0.1:5678" self.master_socket = SocketSpy() self.saver = SaverSpy() self.vad = VADDummy() self.id_generator = IDGeneratorDummy() self.heartbeat = Heartbeat(self.model, self.worker_address, self.master_socket) self.poller = PollerSpy() self.asr = ASRSpy([(1.0, "Hello World!")], (1.0, "Interim result")) self.audio = DummyAudio() self.worker = Worker(self.poller, self.heartbeat, self.asr, self.audio, self.saver, self.vad, self.id_generator, self.poller.has_next_message) def test_worker_forwards_resampled_wav_from_every_message_to_asr_as_pcm(self): messages = [ {"frontend": self.make_frontend_request("message 1")}, {"frontend": self.make_frontend_request("message 2")} ] self.run_worker(messages) self.assertThatAsrProcessedChunks(["resampled pcm message 1", "resampled pcm message 2"]) def test_worker_reads_final_hypothesis_from_asr(self): messages = [ {"frontend": self.make_frontend_request("message 1")}, {"frontend": self.make_frontend_request("message 2")} ] self.run_worker(messages) expected_message = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend([expected_message, expected_message]) def test_worker_sends_interim_results_after_each_chunk(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE")}, {"frontend": self.make_frontend_request("message 2", "ONLINE")} ] self.run_worker(messages) expected_message = createResultsMessage([(0, False, [(1.0, "Interim result")])]) self.assertThatMessagesWereSendToFrontend([expected_message, expected_message]) def test_worker_sends_final_results_after_last_chunk(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"frontend": self.make_frontend_request("", "ONLINE", has_next = False)} ] self.run_worker(messages) expected_message1 = createResultsMessage([(0, False, [(1.0, "Interim result")])]) expected_message2 = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend([expected_message1, expected_message2]) def test_when_worker_receives_chunk_with_bad_id_it_should_return_error_message(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True, id = 1)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", has_next = True, id = 2)}, {"frontend": self.make_frontend_request("", "ONLINE", has_next = False, id = 1)}, ] self.run_worker(messages) expected_message1 = createResultsMessage([(0, False, [(1.0, "Interim result")])]) expected_message2 = createErrorResultsMessage() expected_message3 = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend([expected_message1, expected_message2, expected_message3]) def test_worker_forwards_resampled_pcm_chunks_from_every_message_to_asr(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", has_next = True)} ] self.run_worker(messages) self.assertThatAsrProcessedChunks(["resampled message 1", "resampled message 2"]) def test_worker_sends_heartbeat_to_master_when_ready_to_work(self): messages = [] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED"]) def test_worker_sends_heartbeat_after_finishing_task(self): messages = [ {"frontend": self.make_frontend_request("message 1")} ] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "FINISHED"]) def test_worker_sends_working_heartbeats_during_online_recognition(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", has_next = True)}, {"frontend": self.make_frontend_request("", "ONLINE", has_next = False)} ] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "WORKING", "FINISHED"]) def test_worker_sends_finished_heartbeat_after_end_of_online_recognition(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"frontend": self.make_frontend_request("", "ONLINE", has_next = False)} ] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "FINISHED"]) def test_worker_sends_finished_heartbeat_when_it_doesnt_receive_any_chunk_for_1sec(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"time": +1} ] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "FINISHED"]) def test_worker_sends_resets_asr_engine_when_it_doesnt_receive_any_chunk_for_1sec(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", has_next = True)}, {"time": +1} ] self.run_worker(messages) self.assertTrue(self.asr.resetted) def test_worker_sends_ready_heartbeat_when_it_doesnt_receive_any_task(self): messages = [{}] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WAITING"]) def test_worker_saves_pcm_data_from_batch_request(self): messages = [ {"frontend": self.make_frontend_request("message", "BATCH", id = 1, has_next = False)}, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: {"frame_rate": 16000, "chunks": [{"chunk_id": 0, "pcm": "pcm message", "hypothesis": [(1.0, "Hello World!")]}]} }) def test_worker_saves_pcm_data_from_online_request_in_original_frame_rate(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("message 2", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("", "ONLINE", id = 1, has_next = False)}, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: {"frame_rate": 44100, "chunks": [{"chunk_id": 0, "pcm": "message 1message 2", "hypothesis": [(1.0, "Hello World!")]}]} }) def test_worker_forwards_pcm_data_to_vad(self): messages = [ {"frontend": self.make_frontend_request("message 1", "ONLINE", id = 1, has_next = True)} ] self.run_worker(messages) self.assertThatVadReceivedChunks([("message 1", "resampled message 1")]) def test_worker_sends_empty_hypothesis_when_vad_detects_silence(self): self.vad.set_messages([ (False, None, "", ""), (False, None, "", ""), ]) messages = [ {"frontend": self.make_frontend_request("silence 1", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("silence 2", "ONLINE", id = 1, has_next = True)}, ] self.run_worker(messages) expected_message = createResultsMessage([(0, False, [(1.0, "")])]) self.assertThatMessagesWereSendToFrontend([expected_message, expected_message]) def test_worker_sends_hypothesis_when_vad_detects_speech(self): self.vad.set_messages([ (True, None, "message 1", "resampled message 1"), (True, None, "message 2", "resampled message 2") ]) messages = [ {"frontend": self.make_frontend_request("speech 1", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("speech 2", "ONLINE", id = 1, has_next = True)}, ] self.run_worker(messages) expected_message = createResultsMessage([(0, False, [(1.0, "Interim result")])]) self.assertThatMessagesWereSendToFrontend([expected_message, expected_message]) def test_worker_sends_final_hypothesis_when_vad_detects_change_to_silence(self): self.vad.set_messages([ (True, None, "speech 1", "resampled speech 1"), (False, "non-speech", "", "") ]) messages = [ {"frontend": self.make_frontend_request("speech 1", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("silence 1", "ONLINE", id = 1, has_next = True)}, ] self.run_worker(messages) expected_message1 = createResultsMessage([(0, False, [(1.0, "Interim result")])]) expected_message2 = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend([expected_message1, expected_message2]) def test_worker_sends_working_heartbeat_when_vad_detects_change_to_silence(self): self.vad.set_messages([ (True, None, "speech 1", "resampled speech 1"), (False, "non-speech", "", "") ]) messages = [ {"frontend": self.make_frontend_request("speech 1", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("silence 1", "ONLINE", id = 1, has_next = True)}, ] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "WORKING"]) def test_worker_resets_asr_when_vad_detects_change_to_silence(self): self.vad.set_messages([ (True, None, "speech 1", "resampled speech 1"), (False, "non-speech", "", "") ]) messages = [ {"frontend": self.make_frontend_request("speech 1", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("silence 1", "ONLINE", id = 1, has_next = True)}, ] self.run_worker(messages) self.assertTrue(self.asr.resetted) def test_worker_saves_pcm_as_part_of_original_request(self): self.vad.set_messages([ (True, None, "speech 1", "resampled speech 1"), (False, "non-speech", "", ""), (True, None, "speech 2", "resampled speech 2"), (False, "non-speech", "", "") ]) messages = [ {"frontend": self.make_frontend_request("speech 1", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("silence 1", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("speech 2", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("silence 2", "ONLINE", id = 1, has_next = True)}, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: {"frame_rate": 44100, "chunks": [ {"chunk_id": 0, "pcm": "speech 1", "hypothesis": [(1.0, "Hello World!")]}, {"chunk_id": 0, "pcm": "speech 2", "hypothesis": [(1.0, "Hello World!")]} ]} }) def test_worker_sends_interim_result_for_last_speech_in_splitted_chunks(self): self.audio.set_chunks(["chunk 1", "chunk 2"]) self.vad.set_messages([ (True, None, "chunk 1", "resampled chunk 1"), (True, None, "chunk 2", "resampled chunk 2"), ]) messages = [ {"frontend": self.make_frontend_request("speech 1", "ONLINE", id = 1, has_next = True)}, ] self.run_worker(messages) expected_messages = createResultsMessage([(0, False, [(1.0, "Interim result")])]) self.assertThatMessagesWereSendToFrontend([expected_messages]) def test_worker_sends_final_results_for_each_speech_in_splitted_chunks(self): self.audio.set_chunks(["chunk 1", "chunk 2", "chunk 3", "chunk 4"]) self.vad.set_messages([ (True, None, "chunk 1", "resampled chunk 1"), (False, "non-speech", "", ""), (True, None, "chunk 2", "resampled chunk 2"), (False, "non-speech", "", ""), ]) messages = [ {"frontend": self.make_frontend_request("speech 1", "ONLINE", id = 1, has_next = True)}, ] self.run_worker(messages) expected_messages = createResultsMessage([(0, True, [(1.0, "Hello World!")])] * 2) self.assertThatMessagesWereSendToFrontend([expected_messages]) def test_worker_sends_buffered_chunks_to_saver_when_speech_is_detected(self): self.vad.set_messages([ (True, "speech", "buffered chunk", "resampled buffered chunk"), (False, "non-speech", "", "") ]) messages = [ {"frontend": self.make_frontend_request("speech 1", "ONLINE", id = 1, has_next = True)}, {"frontend": self.make_frontend_request("speech 2", "ONLINE", id = 1, has_next = True)}, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: {"frame_rate": 44100, "chunks": [ {"chunk_id": 0, "pcm": "buffered chunk", "hypothesis": [(1.0, "Hello World!")]}, ]} }) def test_worker_resets_vad_at_the_end_of_recognition(self): messages = [ {"frontend": self.make_frontend_request("", "ONLINE", has_next = False)} ] self.run_worker(messages) self.assertTrue(self.vad.resetted) def test_worker_resets_audio_utils_at_the_end_of_online_recognition(self): messages = [ {"frontend": self.make_frontend_request("", "ONLINE", has_next = False)} ] self.run_worker(messages) self.assertTrue(self.audio.resetted) def test_worker_change_lm_when_new_lm_is_set(self): messages = [ {"frontend": self.make_frontend_request("", "ONLINE", new_lm = "new_lm")} ] self.run_worker(messages) self.assertEquals("new_lm", self.asr.lm) def test_worker_sends_final_result_when_lm_is_changed(self): self.audio.set_chunks([]) messages = [ {"frontend": self.make_frontend_request("", "ONLINE", new_lm = "new_lm")} ] self.run_worker(messages) expected_messages = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend([expected_messages]) def test_worker_sets_lm_to_default_at_the_end_of_recognition(self): messages = [ {"frontend": self.make_frontend_request("", "ONLINE", has_next = False)} ] self.run_worker(messages) self.assertEquals("default", self.asr.lm) def test_worker_assings_unique_id_to_each_chunk(self): self.id_generator.set_ids([0,1]) self.audio.set_chunks(["chunk 1", "chunk 2", "chunk 3", "chunk 4"]) self.vad.set_messages([ (True, None, "chunk 1", "resampled chunk 1"), (False, "non-speech", "", ""), (True, None, "chunk 2", "resampled chunk 2"), (False, "non-speech", "", ""), ]) messages = [ {"frontend": self.make_frontend_request("speech 1", "ONLINE", id = 1, has_next = True)}, ] self.run_worker(messages) expected_messages = createResultsMessage([(0, True, [(1.0, "Hello World!")]), (1, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend([expected_messages]) def test_worker_assings_unique_id_to_each_batch_request(self): self.id_generator.set_ids([0,1]) messages = [ {"frontend": self.make_frontend_request("speech 1", "BATCH", id = 1, has_next = False)}, {"frontend": self.make_frontend_request("speech 2", "BATCH", id = 1, has_next = False)}, ] self.run_worker(messages) expected_message1 = createResultsMessage([(0, True, [(1.0, "Hello World!")])]) expected_message2 = createResultsMessage([(1, True, [(1.0, "Hello World!")])]) self.assertThatMessagesWereSendToFrontend([expected_message1, expected_message2]) def run_worker(self, messages): self.poller.add_messages(messages) self.worker.run() def assertThatAsrProcessedChunks(self, chunks): self.assertEquals(chunks, self.asr.processed_chunks) def assertThatMessagesWereSendToFrontend(self, messages): sent_messages = [parseResultsMessage(message) for message in self.poller.sent_messages["frontend"]] self.assertEquals(messages, sent_messages) def assertThatHeartbeatsWereSent(self, heartbeats): heartbeats = [self.make_heartbeat(state) for state in heartbeats] sent_heartbeats = [parseHeartbeatMessage(message) for message in self.master_socket.sent_messages] self.assertEquals(heartbeats, sent_heartbeats) def assertThatDataWasStored(self, data): self.assertEquals(data, self.saver.saved_data) def assertThatVadReceivedChunks(self, data): self.assertEquals(data, self.vad.data) def make_frontend_request(self, message, type = "BATCH", has_next = True, id = 0, new_lm = ""): return createRecognitionRequestMessage(type, message, has_next, id, 44100, new_lm).SerializeToString() def make_heartbeat(self, status): return createHeartbeatMessage(self.worker_address, self.model, status)
class TestMonitor(unittest.TestCase): def setUp(self): self.poller = PollerSpy() self.scale_workers = ScaleWorkersSpy() self.create_poller = lambda: self.poller self.monitor = Monitor(self.create_poller, self.emit, self.scale_workers, self.poller.has_next_message) self.emmited_messages = [] def test_monitor_forwards_messages_to_socketio(self): messages = [ createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "STARTED", 1).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 2).SerializeToString(), ] self.run_monitor(messages) expected_messages = [ { "address": "tcp://127.0.0.1:1", "model": "en-GB", "status": "STARTED", "time": 1 }, { "address": "tcp://127.0.0.1:1", "model": "en-GB", "status": "WORKING", "time": 2 }, ] self.assertThatMonitorForwardedMessages(expected_messages) def test_monitor_saves_worker_statuses(self): messages = [ createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "STARTED", 1).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:2", "en-GB", "WORKING", 2).SerializeToString(), ] self.run_monitor(messages) expected_messages = [ { "address": "tcp://127.0.0.1:1", "model": "en-GB", "status": "STARTED", "time": 1 }, { "address": "tcp://127.0.0.1:2", "model": "en-GB", "status": "WORKING", "time": 2 }, ] self.assertEqual(expected_messages, self.monitor.get_statuses()) def test_monitor_will_add_new_workers_when_all_workers_are_working(self): messages = [ createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 1).SerializeToString() ] self.run_monitor(messages) expected_messages = [{"en-GB": +1}] self.assertEqual(expected_messages, self.scale_workers.scaling_history) def test_monitor_will_not_add_new_workers_when_it_is_currently_adding_new_workers( self): messages = [ createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 1).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 2).SerializeToString() ] self.run_monitor(messages) expected_messages = [{"en-GB": +1}, {}] self.assertEqual(expected_messages, self.scale_workers.scaling_history) def test_monitor_will_add_new_workers_when_it_finished_scaling_and_it_needs_new_workers( self): messages = [ createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 1).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 2).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:2", "en-GB", "STARTED", 3).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 4).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:2", "en-GB", "WORKING", 5).SerializeToString() ] self.run_monitor(messages) expected_messages = [{"en-GB": +1}, {}, {}, {}, {"en-GB": +1}] self.assertEqual(expected_messages, self.scale_workers.scaling_history) def run_monitor(self, messages): self.poller.add_messages([{"master": message} for message in messages]) self.monitor.run() def assertThatMonitorForwardedMessages(self, messages): forwarded_messages = self.emmited_messages self.assertEqual(messages, forwarded_messages) def emit(self, message): self.emmited_messages.append(message)
class TestWorker(unittest.TestCase): def setUp(self): self.model = "en-GB" self.worker_address = "tcp://127.0.0.1:5678" self.master_socket = SocketSpy() self.saver = SaverSpy() self.heartbeat = Heartbeat(self.model, self.worker_address, self.master_socket) self.poller = PollerSpy() self.asr = ASRSpy([(1.0, "Hello World!")], (1.0, "Interim result")) self.audio = DummyAudio() self.worker = Worker(self.poller, self.heartbeat, self.asr, self.audio, self.saver, self.poller.has_next_message) def test_worker_forwards_resampled_wav_from_every_message_to_asr_as_pcm( self): messages = [{ "frontend": self.make_frontend_request("message 1") }, { "frontend": self.make_frontend_request("message 2") }] self.run_worker(messages) self.assertThatAsrProcessedChunks( ["resampled pcm message 1", "resampled pcm message 2"]) def test_worker_reads_final_hypothesis_from_asr(self): messages = [{ "frontend": self.make_frontend_request("message 1") }, { "frontend": self.make_frontend_request("message 2") }] self.run_worker(messages) expected_message = createResultsMessage(True, [(1.0, "Hello World!")]) self.assertThatMessagesWereSendToFrontend( [expected_message, expected_message]) def test_worker_sends_interim_results_after_each_chunk(self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE") }, { "frontend": self.make_frontend_request("message 2", "ONLINE") }] self.run_worker(messages) expected_message = createResultsMessage(False, [(1.0, "Interim result")]) self.assertThatMessagesWereSendToFrontend( [expected_message, expected_message]) def test_worker_sends_final_results_after_last_chunk(self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", has_next=False) }] self.run_worker(messages) expected_message1 = createResultsMessage(False, [(1.0, "Interim result")]) expected_message2 = createResultsMessage(True, [(1.0, "Hello World!")]) self.assertThatMessagesWereSendToFrontend( [expected_message1, expected_message2]) def test_when_worker_receives_chunk_with_bad_id_it_should_return_error_message( self): messages = [ { "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True, id=1) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", has_next=True, id=2) }, { "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=False, id=1) }, ] self.run_worker(messages) expected_message1 = createResultsMessage(False, [(1.0, "Interim result")]) expected_message2 = createErrorResultsMessage() expected_message3 = createResultsMessage(True, [(1.0, "Hello World!")]) self.assertThatMessagesWereSendToFrontend( [expected_message1, expected_message2, expected_message3]) def test_worker_forwards_resampled_pcm_chunks_from_every_message_to_asr( self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", has_next=True) }] self.run_worker(messages) self.assertThatAsrProcessedChunks( ["resampled message 1", "resampled message 2"]) def test_worker_sends_heartbeat_to_master_when_ready_to_work(self): messages = [] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED"]) def test_worker_sends_heartbeat_after_finishing_task(self): messages = [{"frontend": self.make_frontend_request("message 1")}] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "FINISHED"]) def test_worker_sends_working_heartbeats_during_online_recognition(self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", has_next=True) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", has_next=False) }] self.run_worker(messages) self.assertThatHeartbeatsWereSent( ["STARTED", "WORKING", "WORKING", "FINISHED"]) def test_worker_sends_finished_heartbeat_after_end_of_online_recognition( self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", has_next=False) }] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "FINISHED"]) def test_worker_sends_finished_heartbeat_when_it_doesnt_receive_any_chunk_for_1sec( self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "time": +1 }] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WORKING", "FINISHED"]) def test_worker_sends_resets_asr_engine_when_it_doesnt_receive_any_chunk_for_1sec( self): messages = [{ "frontend": self.make_frontend_request("message 1", "ONLINE", has_next=True) }, { "time": +1 }] self.run_worker(messages) self.assertTrue(self.asr.resetted) def test_worker_sends_ready_heartbeat_when_it_doesnt_receive_any_task( self): messages = [{}] self.run_worker(messages) self.assertThatHeartbeatsWereSent(["STARTED", "WAITING"]) def test_worker_saves_pcm_data_from_batch_request(self): messages = [ { "frontend": self.make_frontend_request("message", "BATCH", id=1, has_next=False) }, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: { "frame_rate": 16000, "pcm": "pcm message", "hypothesis": [(1.0, "Hello World!")] } }) def test_worker_saves_pcm_data_from_online_request_in_original_frame_rate( self): messages = [ { "frontend": self.make_frontend_request("message 1", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("message 2", "ONLINE", id=1, has_next=True) }, { "frontend": self.make_frontend_request("message 3", "ONLINE", id=1, has_next=False) }, ] self.run_worker(messages) self.assertThatDataWasStored({ 1: { "frame_rate": 44100, "pcm": "message 1message 2message 3", "hypothesis": [(1.0, "Hello World!")] } }) def run_worker(self, messages): self.poller.add_messages(messages) self.worker.run() def assertThatAsrProcessedChunks(self, chunks): self.assertEquals(chunks, self.asr.processed_chunks) def assertThatMessagesWereSendToFrontend(self, messages): sent_messages = [ parseResultsMessage(message) for message in self.poller.sent_messages["frontend"] ] self.assertEquals(messages, sent_messages) def assertThatHeartbeatsWereSent(self, heartbeats): heartbeats = [self.make_heartbeat(state) for state in heartbeats] sent_heartbeats = [ parseHeartbeatMessage(message) for message in self.master_socket.sent_messages ] self.assertEquals(heartbeats, sent_heartbeats) def assertThatDataWasStored(self, data): self.assertEquals(data, self.saver.saved_data) def make_frontend_request(self, message, type="BATCH", has_next=True, id=0): return createRecognitionRequestMessage(type, message, has_next, id, 44100).SerializeToString() def make_heartbeat(self, status): return createHeartbeatMessage(self.worker_address, self.model, status)
class TestMonitor(unittest.TestCase): def setUp(self): self.poller = PollerSpy() self.scale_workers = ScaleWorkersSpy() self.create_poller = lambda: self.poller self.monitor = Monitor(self.create_poller, self.emit, self.scale_workers, self.poller.has_next_message) self.emmited_messages = [] def test_monitor_forwards_messages_to_socketio(self): messages = [ createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "STARTED", 1).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 2).SerializeToString(), ] self.run_monitor(messages) expected_messages = [ {"address": "tcp://127.0.0.1:1", "model": "en-GB", "status": "STARTED", "time": 1}, {"address": "tcp://127.0.0.1:1", "model": "en-GB", "status": "WORKING", "time": 2}, ] self.assertThatMonitorForwardedMessages(expected_messages) def test_monitor_saves_worker_statuses(self): messages = [ createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "STARTED", 1).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:2", "en-GB", "WORKING", 2).SerializeToString(), ] self.run_monitor(messages) expected_messages = [ {"address": "tcp://127.0.0.1:1", "model": "en-GB", "status": "STARTED", "time": 1}, {"address": "tcp://127.0.0.1:2", "model": "en-GB", "status": "WORKING", "time": 2}, ] self.assertEqual(expected_messages, self.monitor.get_statuses()) def test_monitor_will_add_new_workers_when_all_workers_are_working(self): messages = [ createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 1).SerializeToString() ] self.run_monitor(messages) expected_messages = [ {"en-GB": +1} ] self.assertEqual(expected_messages, self.scale_workers.scaling_history) def test_monitor_will_not_add_new_workers_when_it_is_currently_adding_new_workers(self): messages = [ createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 1).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 2).SerializeToString() ] self.run_monitor(messages) expected_messages = [{"en-GB": +1}, {}] self.assertEqual(expected_messages, self.scale_workers.scaling_history) def test_monitor_will_add_new_workers_when_it_finished_scaling_and_it_needs_new_workers(self): messages = [ createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 1).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 2).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:2", "en-GB", "STARTED", 3).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:1", "en-GB", "WORKING", 4).SerializeToString(), createWorkerStatusMessage("tcp://127.0.0.1:2", "en-GB", "WORKING", 5).SerializeToString() ] self.run_monitor(messages) expected_messages = [{"en-GB": +1}, {}, {}, {}, {"en-GB": +1}] self.assertEqual(expected_messages, self.scale_workers.scaling_history) def run_monitor(self, messages): self.poller.add_messages([{"master": message} for message in messages]) self.monitor.run() def assertThatMonitorForwardedMessages(self, messages): forwarded_messages = self.emmited_messages self.assertEqual(messages, forwarded_messages) def emit(self, message): self.emmited_messages.append(message)