mirror of
https://github.com/OMGeeky/flucto-heisskleber.git
synced 2026-01-23 03:38:57 +01:00
* #129 AsyncTcpSource enhancements - retry connection on startup (behavior is configurable) - reconnect if data receiving fails (EOF received) - add Python logging - add unit tests * remove syncronous implementations. * WIP: Refactor packer/unpacker * Refactor type hints and topic handling in console sink. * Remove comma from tcp config enum definitions * Remove references to deleted synchronous classes. * Hopefully stable interface for Packer and Unpacker. * WIP: Working with protocols and generics * Finalized Sink, Source definition. * Rename mqtt source and sink files * Rename mqtt publisher and subscriber. * Fix start function to async. * Update documentation. * Remove recursion from udp source. * rename unpack to unpacker, stay consistent. * Renaming in tests. * Make MqttSource generic. * Configure pyproject.toml to move to uv * Add nox support. * Update documentation with myst-parser and sphinx. * Mess with autogeneration of __call__ signatures. * Add dynamic versioning to hatch * Asyncio wrapper for pyserial. * Add docstrings for serial sink and source. * Refactor config handling (#171) * Removes deprecated "verbose" and "print_std" parameters * Adds class methods for config generation from dictionary or file (yaml or json at this point) * Run-time type checking via __post_init__() function * Add serial dependency. * WIP * Move broker to bin/ * Update docs. * WIP: Need to update docstrings to make ruff happy. * Move source files to src/ * Fix tests for TcpSource. * WIP: Remove old tests. * Fix docstrings in mqtt classes. * Make default tcp unpacker json_unpacker. * No failed tests if there are no tests * Update test pipeline * Update ruff pre-commit * Updated ruff formatting * Format bin/ * Fix type hints * No type checking * Make stop() async * Only test on ubuntu for now * Don't be so strict about sphinx warnings. * Rename TestConf for pytest naming compability. * Install package in editable mode for ci tests. * Update dependencies for docs generation. * Add keepalive and will to mqtt, fixes #112. * Update readme to reflect changes in usage. * Requested fixes for console adapters. * Raise correct errors in unpacker and packer. * Correct logger name for mqtt sink. * Add config options for stopbits and parity to Serial. * Remove exception logging call from yaml parser. * Add comments to clear up very implicit test. * Rename Sink -> Sender, Source -> Receiver. * Rename sink and source in tests. * Fix tests. --------- Co-authored-by: Adrian Weiler <a.weiler@aldea.de>
120 lines
3.2 KiB
Python
120 lines
3.2 KiB
Python
import asyncio
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from heisskleber.udp import UdpConf, UdpReceiver, UdpSender
|
|
|
|
|
|
class MockUdpReceiver:
|
|
"""Helper class to receive UDP messages for testing."""
|
|
|
|
transport: asyncio.DatagramTransport
|
|
protocol: asyncio.DatagramProtocol
|
|
|
|
def __init__(self):
|
|
self.received_data = []
|
|
|
|
class ReceiverProtocol(asyncio.DatagramProtocol):
|
|
def __init__(self, received_data):
|
|
self.received_data = received_data
|
|
|
|
def connection_made(self, transport):
|
|
pass
|
|
|
|
def datagram_received(self, data, addr):
|
|
self.received_data.append(data)
|
|
|
|
async def start(self, host: str, port: int):
|
|
"""Start the UDP receiver."""
|
|
loop = asyncio.get_running_loop()
|
|
self.transport, self.protocol = await loop.create_datagram_endpoint(
|
|
lambda: self.ReceiverProtocol(self.received_data),
|
|
local_addr=(host, port),
|
|
)
|
|
|
|
async def stop(self):
|
|
"""Stop the UDP receiver."""
|
|
if hasattr(self, "transport"):
|
|
self.transport.close()
|
|
|
|
|
|
class MockUdpSender:
|
|
"""Helper class to send UDP messages for testing."""
|
|
|
|
transport: asyncio.DatagramTransport
|
|
protocol: asyncio.DatagramProtocol
|
|
|
|
def __init__(self):
|
|
self.received_data = []
|
|
|
|
class SenderProtocol(asyncio.DatagramProtocol):
|
|
def connection_made(self, transport):
|
|
pass
|
|
|
|
async def start(self, host: str, port: int):
|
|
"""Start the UDP receiver."""
|
|
loop = asyncio.get_running_loop()
|
|
self.transport, self.protocol = await loop.create_datagram_endpoint(
|
|
lambda: self.SenderProtocol(),
|
|
remote_addr=(host, port),
|
|
)
|
|
|
|
async def stop(self):
|
|
"""Stop the UDP receiver."""
|
|
if hasattr(self, "transport"):
|
|
self.transport.close()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_udp_source() -> None:
|
|
receiver_host = "127.0.0.1"
|
|
receiver_port = 35699
|
|
receiver = UdpReceiver(UdpConf(host=receiver_host, port=receiver_port))
|
|
|
|
try:
|
|
await receiver.start()
|
|
|
|
sink = MockUdpSender()
|
|
try:
|
|
await sink.start(receiver_host, receiver_port)
|
|
sink.transport.sendto(data=json.dumps({"message": "hi there!"}).encode())
|
|
|
|
data, extra = await receiver.receive()
|
|
assert data == {"message": "hi there!"}
|
|
finally:
|
|
await sink.stop()
|
|
finally:
|
|
await receiver.stop()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_actual_udp_transport():
|
|
"""Test actual UDP communication between sender and receiver."""
|
|
mock_receiver = MockUdpReceiver()
|
|
receiver_host = "127.0.0.1"
|
|
receiver_port = 45678
|
|
|
|
try:
|
|
await mock_receiver.start(receiver_host, receiver_port)
|
|
|
|
config = UdpConf(host=receiver_host, port=receiver_port)
|
|
sink = UdpSender(config)
|
|
|
|
try:
|
|
await sink.start()
|
|
|
|
test_data = {"message": "Hello, UDP!"}
|
|
await sink.send(test_data)
|
|
await asyncio.sleep(0.1)
|
|
|
|
assert len(mock_receiver.received_data) == 1
|
|
received_bytes = mock_receiver.received_data[0]
|
|
assert b'"message": "Hello, UDP!"' in received_bytes
|
|
|
|
finally:
|
|
await sink.stop()
|
|
|
|
finally:
|
|
await mock_receiver.stop()
|