Skip to content

Commit d552c1b

Browse files
authored
Add server/client network stub, to allow test of network packets. (#1963)
1 parent dedcb8b commit d552c1b

File tree

5 files changed

+62
-0
lines changed

5 files changed

+62
-0
lines changed

pymodbus/transport/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
"CommParams",
44
"CommType",
55
"ModbusProtocol",
6+
"ModbusProtocolStub",
67
"NULLMODEM_HOST",
78
]
89

10+
from pymodbus.transport.stub import ModbusProtocolStub
911
from pymodbus.transport.transport import (
1012
NULLMODEM_HOST,
1113
CommParams,

pymodbus/transport/stub.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""ModbusProtocol network stub."""
2+
from __future__ import annotations
3+
4+
from pymodbus.transport.transport import ModbusProtocol
5+
6+
7+
class ModbusProtocolStub(ModbusProtocol):
8+
"""Protocol layer including transport."""
9+
10+
async def start_run(self):
11+
"""Call need functions to start server/client."""
12+
if self.is_server:
13+
return await self.transport_listen()
14+
return await self.transport_connect()
15+
16+
def callback_data(self, data: bytes, addr: tuple | None = None) -> int:
17+
"""Handle received data."""
18+
if (response := self.stub_handle_data(data)):
19+
self.transport_send(response)
20+
return len(data)
21+
22+
# ---------------- #
23+
# external methods #
24+
# ---------------- #
25+
def stub_handle_data(self, data: bytes) -> bytes | None:
26+
"""Handle received data."""
27+
if len(data) > 5:
28+
return data
29+
return None

test/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def pytest_configure():
4646
"TestReconnectModbusProtocol": 8000,
4747
"TestClientServerSyncExamples": 8100,
4848
"TestClientServerAsyncExamples": 8200,
49+
"TestNetwork": 8300,
4950
}
5051

5152

test/sub_transport/test_reconnect.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ async def test_no_reconnect_call(self, client):
2828

2929
async def test_reconnect_call(self, client):
3030
"""Test connection_lost()."""
31+
client.comm_params.on_reconnect_callback = mock.MagicMock()
3132
client.loop = asyncio.get_running_loop()
3233
client.call_create = mock.AsyncMock(return_value=(None, None))
3334
await client.transport_connect()
@@ -38,6 +39,7 @@ async def test_reconnect_call(self, client):
3839
assert client.reconnect_task
3940
assert client.call_create.call_count == 2
4041
assert client.reconnect_delay_current == client.comm_params.reconnect_delay * 2
42+
assert client.comm_params.on_reconnect_callback.called
4143
client.transport_close()
4244

4345
async def test_multi_reconnect_call(self, client):

test/test_network.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Test transport."""
2+
3+
import pytest
4+
5+
from pymodbus.client import AsyncModbusTcpClient
6+
from pymodbus.logging import Log
7+
from pymodbus.transport import NULLMODEM_HOST, ModbusProtocolStub
8+
9+
10+
class TestNetwork:
11+
"""Test network problems."""
12+
13+
@staticmethod
14+
@pytest.fixture(name="use_port")
15+
def get_port_in_class(base_ports):
16+
"""Return next port."""
17+
base_ports[__class__.__name__] += 1
18+
return base_ports[__class__.__name__]
19+
20+
async def test_stub(self, use_port, use_cls):
21+
"""Test double packet on network."""
22+
Log.debug("test_double_packet {}", use_port)
23+
client = AsyncModbusTcpClient(NULLMODEM_HOST, port=use_port)
24+
stub = ModbusProtocolStub(use_cls, True)
25+
assert await stub.start_run()
26+
assert await client.connect()
27+
client.transport_close()
28+
stub.transport_close()

0 commit comments

Comments
 (0)