Skip to content

Add server/client network stub, to allow test of network packets. #1963

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pymodbus/transport/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
"CommParams",
"CommType",
"ModbusProtocol",
"ModbusProtocolStub",
"NULLMODEM_HOST",
]

from pymodbus.transport.stub import ModbusProtocolStub
from pymodbus.transport.transport import (
NULLMODEM_HOST,
CommParams,
Expand Down
29 changes: 29 additions & 0 deletions pymodbus/transport/stub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""ModbusProtocol network stub."""
from __future__ import annotations

from pymodbus.transport.transport import ModbusProtocol


class ModbusProtocolStub(ModbusProtocol):
"""Protocol layer including transport."""

async def start_run(self):
"""Call need functions to start server/client."""
if self.is_server:
return await self.transport_listen()
return await self.transport_connect()

def callback_data(self, data: bytes, addr: tuple | None = None) -> int:
"""Handle received data."""
if (response := self.stub_handle_data(data)):
self.transport_send(response)
return len(data)

# ---------------- #
# external methods #
# ---------------- #
def stub_handle_data(self, data: bytes) -> bytes | None:
"""Handle received data."""
if len(data) > 5:
return data
return None
1 change: 1 addition & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def pytest_configure():
"TestReconnectModbusProtocol": 8000,
"TestClientServerSyncExamples": 8100,
"TestClientServerAsyncExamples": 8200,
"TestNetwork": 8300,
}


Expand Down
2 changes: 2 additions & 0 deletions test/sub_transport/test_reconnect.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ async def test_no_reconnect_call(self, client):

async def test_reconnect_call(self, client):
"""Test connection_lost()."""
client.comm_params.on_reconnect_callback = mock.MagicMock()
client.loop = asyncio.get_running_loop()
client.call_create = mock.AsyncMock(return_value=(None, None))
await client.transport_connect()
Expand All @@ -38,6 +39,7 @@ async def test_reconnect_call(self, client):
assert client.reconnect_task
assert client.call_create.call_count == 2
assert client.reconnect_delay_current == client.comm_params.reconnect_delay * 2
assert client.comm_params.on_reconnect_callback.called
client.transport_close()

async def test_multi_reconnect_call(self, client):
Expand Down
28 changes: 28 additions & 0 deletions test/test_network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Test transport."""

import pytest

from pymodbus.client import AsyncModbusTcpClient
from pymodbus.logging import Log
from pymodbus.transport import NULLMODEM_HOST, ModbusProtocolStub


class TestNetwork:
"""Test network problems."""

@staticmethod
@pytest.fixture(name="use_port")
def get_port_in_class(base_ports):
"""Return next port."""
base_ports[__class__.__name__] += 1
return base_ports[__class__.__name__]

async def test_stub(self, use_port, use_cls):
"""Test double packet on network."""
Log.debug("test_double_packet {}", use_port)
client = AsyncModbusTcpClient(NULLMODEM_HOST, port=use_port)
stub = ModbusProtocolStub(use_cls, True)
assert await stub.start_run()
assert await client.connect()
client.transport_close()
stub.transport_close()