def test_get_messages_correctly_return_a_list_of_message_to_send_that_represent_the_current_image( ): # Given new_image = np.array(4 * [4 * 4 * [[0, 0, 0]]]) im = ImageManager(max_packet_size=64) im.refresh_image(new_image) nb_packet = 3 total_bytes = 192 height = 4 length = 16 pixel_size = 3 expected_topic = 10 encoding = 0 expected_header = nb_packet.to_bytes( ImageManager.NB_PACKET_SIZE, 'little') + total_bytes.to_bytes( ImageManager.TOTAL_BYTES_SIZE, 'little') + height.to_bytes( ImageManager.HEIGHT_SIZE, 'little') + length.to_bytes( ImageManager.LENGTH_SIZE, 'little') + pixel_size.to_bytes( ImageManager.SIZE_PIXEL_SIZE, 'little') + encoding.to_bytes( ImageManager.ENCODING_SIZE, 'little') # When result = list(im.get_messages(expected_topic)) # Then assert collections.Counter(UDPMessage.from_bytes( result[0]).payload) == collections.Counter(list(expected_header)) for i in range(1, len(result)): msg = UDPMessage.from_bytes(result[i]) assert int.from_bytes(msg.message_nb, 'little') == i assert int.from_bytes(msg.topic, 'little') == expected_topic assert msg.payload == bytes(64 * [0])
def test_add_msg_correctly_create_a_topic_if_a_new_topic_need_to_be_created(get_msg_sample): # Given messages = get_msg_sample tm = TopicManager() # When tm.add_message(UDPMessage.from_bytes(messages[0])) # Then assert type(tm.open_topic[int.from_bytes(UDPMessage.from_bytes(messages[0]).topic, 'little')]) is VideoTopic
def test_add_msg_add_data_messages_to_dead_letter_queue_when_its_topic_does_not_exist(get_msg_sample): # Given messages = get_msg_sample tm = TopicManager() # When tm.add_message(UDPMessage.from_bytes(messages[1])) # Then assert tm.dead_letter_queue[0].payload == UDPMessage.from_bytes(messages[1]).payload assert tm.dead_letter_queue[0].message_nb == UDPMessage.from_bytes(messages[1]).message_nb
def test_add_msg_remove_completed_topics_from_open_topic(get_msg_sample): # Given messages = get_msg_sample tm = TopicManager() tm.add_message(UDPMessage.from_bytes(messages[0])) # When for i in messages[1:]: tm.add_message(UDPMessage.from_bytes(i)) # Then assert len(tm.open_topic.keys()) == 0
def test_add_msg_correctly_add_msg_to_an_existing_topic(get_msg_sample): # Given messages = get_msg_sample tm = TopicManager() # When tm.add_message(UDPMessage.from_bytes(messages[0])) tm.add_message(UDPMessage.from_bytes(messages[1])) # Then assert tm.open_topic[1].rcv_messages[0].payload == UDPMessage.from_bytes(messages[1]).payload assert tm.open_topic[1].rcv_messages[0].message_nb == UDPMessage.from_bytes(messages[1]).message_nb
def test_add_msg_add_message_from_dlq_to_its_topic_when_it_is_created_and_remove_msg_from_dlq(get_msg_sample): # Given messages = get_msg_sample tm = TopicManager() # When tm.add_message(UDPMessage.from_bytes(messages[1])) tm.add_message(UDPMessage.from_bytes(messages[0])) # Then assert tm.open_topic[1].rcv_messages[0].payload == UDPMessage.from_bytes(messages[1]).payload assert tm.open_topic[1].rcv_messages[0].message_nb == UDPMessage.from_bytes(messages[1]).message_nb assert len(tm.dead_letter_queue) == 0
def test_check_topic_add_topic_image_to_img_queue_when_topic_is_complete(get_msg_sample): # Given expected_img = np.array(4 * [4 * 4 * [[0, 0, 0]]]) messages = get_msg_sample tm = TopicManager() tm.add_message(UDPMessage.from_bytes(messages[0])) # When for i in messages[1:]: tm.add_message(UDPMessage.from_bytes(i)) # Then assert np.array_equiv(expected_img, tm.img_queue[0])
def test_check_crc_returns_true_when_crc_correct(): # Given message_nb = bytes([0, 0]) payload = bytes([48, 48, 48, 48]) msg_id = bytes([48, 48, 48, 48]) topic = bytes([1, 0, 0, 0]) # When msg = UDPMessage(subtopic=message_nb, payload=payload, code=msg_id, topic=topic) # Then assert msg.check_crc() is True
def test_to_bytes_returns_full_message_as_bytes(): # Given message_nb = bytes([0, 0]) payload = bytes([48, 48, 48, 48]) msg_id = bytes([48, 48, 48, 48]) topic = bytes([1, 0, 0, 0]) # When msg = UDPMessage(subtopic=message_nb, payload=payload, code=msg_id, topic=topic) expected_result = msg.full_content + msg.crc # Then assert msg.to_bytes() == expected_result
def get_messages(self, topic: int, force: Optional[bool] = False) -> iter: """Return a list of bytes representing the messages to send. :param topic: The topic associated to the image. :param force: Specify if tje messages must be re-computed. :return messages: An iterator containing the the messages to send as bytes. """ if self.async_msg_generation and (force is False): return self.messages img_split = self.split_image() to_msg = lambda enum: UDPMessage(code=codes.VIDEO_STREAM, payload=enum[1], topic=topic, subtopic=enum[0] + 1).to_bytes() img_messages = map(to_msg, enumerate(img_split)) header = ImageManager.get_header_msg( topic, math.ceil( np.array(self.current_image.shape).prod() / self.max_packet_size), int(np.array(self.current_image.shape).prod()), self.current_image.shape[0], self.current_image.shape[1], self.get_pixel_size(), encoding=self.encoding) return chain([header], img_messages)
def get_header_msg(topic: int, nb_packet: int, total_bytes: int, height: int, length: int, pixel_size: int, encoding: Optional[int] = 0) -> bytes: """Return a UDPMessage with image metadata. :param topic: The topic associated to the image. :param nb_packet: The total number of data packet that will be send. :param total_bytes: The total number of bytes of the image. :param height: The height of the image. :param length: The length of the image. :param pixel_size: The size of a pixel. :param encoding: The encoding of the pixel (default 0 = None). :return header_msg: The UDPMessage containing image metadata. """ return UDPMessage( code=codes.VIDEO_STREAM, topic=topic, subtopic=ImageManager.NB_MSG_HEADER, payload=nb_packet.to_bytes(ImageManager.NB_PACKET_SIZE, 'little') + total_bytes.to_bytes(ImageManager.TOTAL_BYTES_SIZE, 'little') + height.to_bytes(ImageManager.HEIGHT_SIZE, 'little') + length.to_bytes(ImageManager.LENGTH_SIZE, 'little') + pixel_size.to_bytes(ImageManager.SIZE_PIXEL_SIZE, 'little') + encoding.to_bytes(ImageManager.ENCODING_SIZE, 'little')).to_bytes()
def test_next_message_return_authentication_required_message_when_connection_step_4_and_role_is_server_with_password( ): # Given password_to_derive = b"test" password_salt = os.urandom(16) derived_password = derive_password_scrypt( password_salt=password_salt, password_to_derive=password_to_derive) allowed_authentication_method = ["password"] authentication_information_server = { "password": { Handshake.PASSWORD_AUTH_METHOD_DERIVED_PASSWORD_KEY: derived_password, Handshake.PASSWORD_AUTH_METHOD_SALT_KEY: password_salt } } server = Handshake( role=Handshake.SERVER, allowed_authentication_methods=allowed_authentication_method, authentication_information=authentication_information_server) client = Handshake( role=Handshake.CLIENT, allowed_authentication_methods=allowed_authentication_method) expected_message = UDPMessage( code=codes.HANDSHAKE, topic=Handshake.AUTHENTICATION_REQUIRED_TOPIC) server.add_message(client.next_message()) client.add_message(server.next_message()) server.add_message(client.next_message()) # When result = server.next_message() # Then assert result.msg_id == expected_message.msg_id assert result.topic == expected_message.topic
def test_from_message_correctly_create_a_new_video_topic(): # Given nb_packet = 3 total_bytes = 90 height = 10 length = 3 pixel_size = 3 expected_payload = nb_packet.to_bytes(ImageManager.NB_PACKET_SIZE, 'little') + total_bytes.to_bytes( ImageManager.TOTAL_BYTES_SIZE, 'little') + height.to_bytes(ImageManager.HEIGHT_SIZE, 'little') + length.to_bytes( ImageManager.LENGTH_SIZE, 'little') + pixel_size.to_bytes(ImageManager.SIZE_PIXEL_SIZE, 'little') expected_topic = 10 # When header = ImageManager.get_header_msg(expected_topic, nb_packet, total_bytes, height, length, pixel_size) header_message = UDPMessage.from_bytes(header) result = VideoTopic.from_message(header_message) # Then assert result.nb_packet == nb_packet assert result.total_bytes == total_bytes assert result.height == height assert result.length == length assert result.pixel_size == pixel_size assert result.time_creation == int.from_bytes(header_message.time_creation, 'little')
def test_get_header_msg_correctly_return_an_array_of_bytes_with_correct_metadata( ): # Given nb_packet = 2 total_bytes = 50 height = 2 length = 25 pixel_size = 3 encoding = 0 expected_payload = nb_packet.to_bytes( ImageManager.NB_PACKET_SIZE, 'little') + total_bytes.to_bytes( ImageManager.TOTAL_BYTES_SIZE, 'little') + height.to_bytes( ImageManager.HEIGHT_SIZE, 'little') + length.to_bytes( ImageManager.LENGTH_SIZE, 'little') + pixel_size.to_bytes( ImageManager.SIZE_PIXEL_SIZE, 'little') + encoding.to_bytes( ImageManager.ENCODING_SIZE, 'little') expected_topic = 10 # When result = ImageManager.get_header_msg(expected_topic, nb_packet, total_bytes, height, length, pixel_size) result_message = UDPMessage.from_bytes(result) # Then assert result_message.payload == expected_payload assert int.from_bytes(result_message.topic, 'little') == expected_topic
def _loop(self) -> NoReturn: """The main loop of the process.""" max_topic = 2 ** (8 * UDPMessage.TOPIC_LENGTH) img_topic = 0 while self.is_running: # Manage external call of class method when using Process class. if self.run_new_process and self.internal_pipe.poll(): command = self.internal_pipe.recv() if type(command) is tuple: self.internal_pipe.send(command[0](self, **command[1])) # Send image packets if the VideoStream object is emitter. if self.role == VideoStream.EMITTER: if self.eye is not None: self.im.refresh_image(self.eye.read()) self.cast(img_topic) img_topic = (img_topic + 1) % max_topic if self.run_new_process: VideoStream.delay(1) # Receive packets if the VideoStream object is consumer. if self.role == VideoStream.CONSUMER: while self.udp_socket.in_waiting(): msg = UDPMessage.from_bytes(self.udp_socket.pull()[0]) if type(msg) is not UDPMessage: continue self.tm.add_message(msg) if self.tm.in_waiting(): if self.use_rcv_img_buffer: self.rcv_img_buffer.append(self.tm.pull()) else: self.rcv_img_buffer[0] = self.tm.pull()
def test_pull_return_the_first_image_of_the_list_if_an_image_is_waiting_else_none(get_msg_sample): # Given expected_img = np.array(4 * [4 * 4 * [[0, 0, 0]]]) messages = get_msg_sample tm = TopicManager() tm.add_message(UDPMessage.from_bytes(messages[0])) # When for i in messages[1:]: tm.add_message(UDPMessage.from_bytes(i)) pull1 = tm.pull() pull2 = tm.pull() # Then assert np.array_equiv(expected_img, pull1) assert pull2 is None
def test_from_bytes_returns_none_if_message_is_corrupted(): # Given message_nb = bytes([2, 0]) payload = bytes([49, 49, 49, 49]) msg_id = bytes([48, 48, 48, 48]) topic = bytes([1, 0, 0, 0]) # When msg = UDPMessage(subtopic=message_nb, payload=payload, code=msg_id, topic=topic) msg.crc = bytes() result = UDPMessage.from_bytes(msg.to_bytes()) # Then assert result is None
def test_from_bytes_returns_none_if_input_too_small(): # Given # When result = UDPMessage.from_bytes(bytes()) # Then assert result is None
def _nxt_msg_srv_approve_connection(self) -> UDPMessage: """Return connection approve message . :return next_message: A UDPMessage to send to remote host to continue handshake process. """ self._connection_status = Handshake.CONNECTION_STATUS_APPROVED return UDPMessage(code=codes.HANDSHAKE, topic=Handshake.CONNECTION_APPROVED_TOPIC)
def _nxt_msg_connection_failed(self) -> UDPMessage: """Return connection failed message . :return next_message: A UDPMessage to send to remote host to continue handshake process. """ self._connection_status = Handshake.CONNECTION_STATUS_FAILED return UDPMessage(code=codes.HANDSHAKE, topic=Handshake.CONNECTION_FAILED_TOPIC)
def test_new_udp_message_created_with_correct_payload_when_type_bytes(): # Given payload = bytes([48, 48, 48, 48]) # When msg = UDPMessage(payload=payload) # Then assert msg.payload == payload
def test_new_udp_message_created_with_correct_message_nb_when_type_bytes(): # Given message_nb = bytes(UDPMessage.MSG_NUMBER_LENGTH * [1]) # When msg = UDPMessage(subtopic=message_nb) # Then assert msg.message_nb == message_nb
def test_new_udp_message_throw_error_when_input_message_nb_too_big(): # Given msg_nb = bytes([1, 0, 0, 0, 0, 0]) # When # Then with pytest.raises(ValueError): UDPMessage(subtopic=msg_nb)
def test_new_udp_message_created_with_correct_msg_id_when_type_bytes(): # Given msg_id = bytes([48, 48, 48, 48]) # When msg = UDPMessage(code=msg_id) # Then assert msg.msg_id == msg_id
def test_new_udp_message_created_with_correct_topic_when_type_bytes(): # Given topic = bytes([1, 0, 0, 0]) # When msg = UDPMessage(topic=topic) # Then assert msg.topic == topic
def test_new_udp_message_throw_error_when_input_payload_too_big(): # Given payload = bytes([0] * (UDPMessage.PAYLOAD_MAX_SIZE + 1)) # When # Then with pytest.raises(ValueError): UDPMessage(payload=payload)
def test_new_udp_message_throw_error_when_input_topic_too_big(): # Given topic = bytes([1, 0, 0, 0, 0, 0]) # When # Then with pytest.raises(ValueError): UDPMessage(topic=topic)
def test_new_udp_message_throw_error_when_input_message_id_too_big(): # Given msg_id = bytes([1, 0, 0, 0, 0, 0]) # When # Then with pytest.raises(ValueError): UDPMessage(code=msg_id)
def test_new_udp_message_created_with_correct_message_nb_when_type_int(): # Given expected_message_nb = bytes([1] + (UDPMessage.MSG_NUMBER_LENGTH - 1) * [0]) message_nb = 1 # When msg = UDPMessage(subtopic=message_nb) # Then assert msg.message_nb == expected_message_nb
def test_check_topic_remove_old_opened_topic_when_a_topic_is_completed(get_msg_sample): # Given messages = get_msg_sample tm = TopicManager() tm.add_message(UDPMessage.from_bytes(messages[0])) time.sleep(.01) new_image = np.array(4 * [4 * 4 * [[0, 0, 0]]]) im = ImageManager(max_packet_size=64) im.refresh_image(new_image) new_messages = list(im.get_messages(2)) tm.add_message(UDPMessage.from_bytes(new_messages[0])) # When for i in new_messages[1:]: tm.add_message(UDPMessage.from_bytes(i)) # Then assert len(tm.open_topic.keys()) == 0