diff --git a/tfw/components/frontend/message_queue_handler/message_queue_handler.py b/tfw/components/frontend/message_queue_handler/message_queue_handler.py index 5843a38..17fedc1 100644 --- a/tfw/components/frontend/message_queue_handler/message_queue_handler.py +++ b/tfw/components/frontend/message_queue_handler/message_queue_handler.py @@ -6,6 +6,7 @@ from threading import Thread class MessageQueueHandler: keys = ['message.queue'] type_id = 'ControlEventHandler' + avg_word_len = 5 def __init__(self, wpm): self.connector = None @@ -15,11 +16,17 @@ class MessageQueueHandler: def _dispatch_messages(self): for message in iter(self._queue.get, None): - wpm = message['wpm'] if 'wpm' in message else self.wpm - cps = 5 * wpm / 60 message['typing'] = not self._queue.empty() self.connector.send_message(message) - sleep(len(message['message']) / cps) + self._sleep(self._get_sleep_time(message)) + + def _get_sleep_time(self, message): + words_per_min = message['wpm'] if 'wpm' in message else self.wpm + chars_per_min = self.avg_word_len * words_per_min / 60 + return len(message['message']) / chars_per_min + + def _sleep(self, seconds): # pylint: disable=no-self-use + sleep(seconds) def handle_event(self, message, _): for unpacked in self._generate_messages_from_queue(message): diff --git a/tfw/components/frontend/message_queue_handler/test_message_queue.py b/tfw/components/frontend/message_queue_handler/test_message_queue.py index c0f59f3..8910981 100644 --- a/tfw/components/frontend/message_queue_handler/test_message_queue.py +++ b/tfw/components/frontend/message_queue_handler/test_message_queue.py @@ -1,8 +1,8 @@ # pylint: disable=redefined-outer-name -from math import inf -from time import sleep +from time import time from os import urandom from random import randint +from queue import Queue import pytest @@ -12,56 +12,97 @@ from .message_queue_handler import MessageQueueHandler class MockConnector: def __init__(self): self.callback = None - self.messages = [] + self.messages = Queue() + self.send_times = Queue() def raise_event(self, message): self.callback(message, self) - sleep(0.01) def send_message(self, message): - self.messages.append(message) + self.messages.put(message) + self.send_times.put(time()) @pytest.fixture def handler(): - connector = MockConnector() - handler = MessageQueueHandler(inf) - handler.connector = connector - connector.callback = handler.handle_event + class NoSleepMessageQueueHandler(MessageQueueHandler): + sleep_start_times = Queue() + sleep_seconds = Queue() + sleep_end_times = Queue() + + def _sleep(self, seconds): + self.sleep_start_times.put(time()) + self.sleep_seconds.put(seconds) + super()._sleep(seconds) + self.sleep_end_times.put(time()) + + handler = NoSleepMessageQueueHandler(100000) + handler.connector = MockConnector() + handler.connector.callback = handler.handle_event + handler.start() yield handler handler.cleanup() -@pytest.fixture -def queue(): - yield { +def get_message_queue(*, size=None): + size = randint(5, 10) if not size else size + return { 'key': 'message.queue', 'messages': [ - {'originator': urandom(4).hex(), 'message': urandom(16).hex()} - for _ in range(randint(5, 10)) + {'originator': urandom(4).hex(), 'message': urandom(randint(10, 20)).hex()} + for _ in range(size) ] } -def test_message_order(handler, queue): +def test_order(handler): + queue = get_message_queue() handler.connector.raise_event(queue) - old_list = queue['messages'] - new_list = handler.connector.messages - length = len(old_list) - assert len(new_list) == length - for i in range(length): - unpacked = new_list[i] - assert unpacked['key'] == 'message.send' - assert unpacked['originator'] == old_list[i]['originator'] - assert unpacked['typing'] == (i < length-1) + expected_messages = queue['messages'] + + actual_messages = [] + for _ in expected_messages: + actual_messages.append(handler.connector.messages.get()) + + assert len(actual_messages) == len(expected_messages) + for i in range(len(expected_messages)): # pylint: disable=consider-using-enumerate + message = actual_messages[i] + assert message['key'] == 'message.send' + assert message['originator'] == expected_messages[i]['originator'] + assert message['typing'] == (i < len(expected_messages)-1) -def test_wpm(handler, queue): - handler.wpm = 10000 - handler.connector.raise_event(queue) - assert len(handler.connector.messages) == 1 - handler.wpm = 100000000 - handler.connector.raise_event(queue) - sleep(0.25) - assert len(handler.connector.messages) == 2*len(queue['messages']) +def test_timing(handler): + q1 = get_message_queue(size=2) + q2 = get_message_queue(size=2) + handler.connector.raise_event(q1) + handler.connector.raise_event(q2) + + messages = [] + send_times = [] + sleep_start_times = [] + sleep_seconds = [] + sleep_end_times = [] + for _ in range(len(q1['messages']) + len(q2['messages'])): + messages.append(handler.connector.messages.get()) + send_times.append(handler.connector.send_times.get()) + sleep_start_times.append(handler.sleep_start_times.get()) + sleep_seconds.append(handler.sleep_seconds.get()) + sleep_end_times.append(handler.sleep_end_times.get()) + + # no sleep before first message + assert sleep_start_times[0] > send_times[0] + assert messages[0]['typing'] + + # at least 'seconds' sleep before sending next messages + assert (send_times[0] + sleep_seconds[0]) < sleep_end_times[0] + assert (send_times[0] + sleep_seconds[0]) < send_times[1] + assert messages[1]['typing'] + assert (send_times[1] + sleep_seconds[1]) < sleep_end_times[1] + assert (send_times[1] + sleep_seconds[1]) < send_times[2] + assert messages[2]['typing'] + + # at least 'seconds' sleep after last message + assert (send_times[2] + sleep_seconds[2]) < sleep_end_times[3] + assert not messages[3]['typing']