add unit tests for AsyncStream class

This commit is contained in:
Stefan Allius
2024-10-06 17:43:40 +02:00
parent 9852f44dfa
commit e074a39f5a
8 changed files with 407 additions and 221 deletions

View File

@@ -0,0 +1,207 @@
# test_with_pytest.py
import pytest
import asyncio
import gc
from app.src.infos import Infos
from app.src.inverter_base import InverterBase
from app.src.async_stream import AsyncStreamServer, AsyncStreamClient
from app.tests.test_modbus_tcp import FakeReader, FakeWriter
from app.tests.test_inverter_base import config_conn, patch_open_connection
pytest_plugins = ('pytest_asyncio',)
# initialize the proxy statistics
Infos.static_init()
def test_timeout_cb():
reader = FakeReader()
writer = FakeWriter()
def timeout():
return 13
ifc = AsyncStreamClient(reader, writer, None, None)
assert 360 == ifc._AsyncStream__timeout()
ifc.prot_set_timeout_cb(timeout)
assert 13 == ifc._AsyncStream__timeout()
ifc.prot_set_timeout_cb(None)
assert 360 == ifc._AsyncStream__timeout()
# call healthy outside the contexter manager (__exit__() was called)
assert ifc.healthy()
del ifc
cnt = 0
for inv in InverterBase:
print(f'InverterBase refs:{gc.get_referrers(inv)}')
cnt += 1
assert cnt == 0
@pytest.mark.asyncio
async def test_close_cb():
assert asyncio.get_running_loop()
reader = FakeReader()
writer = FakeWriter()
cnt = 0
def timeout():
return 0.1
def closed():
nonlocal cnt
nonlocal ifc
ifc.close() # clears the closed callback
cnt += 1
cnt = 0
ifc = AsyncStreamClient(reader, writer, None, closed)
ifc.prot_set_timeout_cb(timeout)
await ifc.client_loop('')
assert cnt == 1
ifc.prot_set_timeout_cb(timeout)
await ifc.client_loop('')
assert cnt == 1 # check that the closed method would not be called
del ifc
cnt = 0
ifc = AsyncStreamClient(reader, writer, None, None)
ifc.prot_set_timeout_cb(timeout)
await ifc.client_loop('')
assert cnt == 0
del ifc
cnt = 0
for inv in InverterBase:
print(f'InverterBase refs:{gc.get_referrers(inv)}')
cnt += 1
assert cnt == 0
@pytest.mark.asyncio
async def test_read():
global test
assert asyncio.get_running_loop()
reader = FakeReader()
reader.test = FakeReader.RD_TEST_13_BYTES
reader.on_recv.set()
writer = FakeWriter()
cnt = 0
def timeout():
return 1
def closed():
nonlocal cnt
nonlocal ifc
ifc.close() # clears the closed callback
cnt += 1
def app_read():
return 0.01 # async wait of 0.01
cnt = 0
ifc = AsyncStreamClient(reader, writer, None, closed)
ifc.prot_set_timeout_cb(timeout)
ifc.rx_set_cb(app_read)
await ifc.client_loop('')
print('End loop')
assert 13 == ifc.rx_len()
assert cnt == 1
del ifc
cnt = 0
for inv in InverterBase:
print(f'InverterBase refs:{gc.get_referrers(inv)}')
cnt += 1
assert cnt == 0
@pytest.mark.asyncio
async def test_write():
global test
assert asyncio.get_running_loop()
reader = FakeReader()
reader.test = FakeReader.RD_TEST_13_BYTES
reader.on_recv.set()
writer = FakeWriter()
cnt = 0
def timeout():
return 1
def closed():
nonlocal cnt
nonlocal ifc
ifc.close() # clears the closed callback
cnt += 1
cnt = 0
ifc = AsyncStreamClient(reader, writer, None, closed)
ifc.prot_set_timeout_cb(timeout)
ifc.tx_add(b'test-data-resp')
assert 14 == ifc.tx_len()
await ifc.client_loop('')
print('End loop')
assert 13 == ifc.rx_len()
assert 0 == ifc.tx_len()
assert cnt == 1
del ifc
cnt = 0
for inv in InverterBase:
print(f'InverterBase refs:{gc.get_referrers(inv)}')
cnt += 1
assert cnt == 0
@pytest.mark.asyncio
async def test_publ_mqtt_cb():
assert asyncio.get_running_loop()
reader = FakeReader()
reader.test = FakeReader.RD_TEST_13_BYTES
reader.on_recv.set()
writer = FakeWriter()
cnt = 0
def timeout():
return 0.1
async def publ_mqtt():
nonlocal cnt
nonlocal ifc
cnt += 1
cnt = 0
ifc = AsyncStreamServer(reader, writer, publ_mqtt, None, None)
assert ifc.async_publ_mqtt
ifc.prot_set_timeout_cb(timeout)
await ifc.server_loop()
assert cnt == 3 # 2 calls in server_loop() and 1 in loop()
assert ifc.async_publ_mqtt
ifc.close() # clears the closed callback
assert not ifc.async_publ_mqtt
del ifc
cnt = 0
for inv in InverterBase:
print(f'InverterBase refs:{gc.get_referrers(inv)}')
cnt += 1
assert cnt == 0
@pytest.mark.asyncio
async def test_create_remote_cb():
assert asyncio.get_running_loop()
reader = FakeReader()
writer = FakeWriter()
cnt = 0
def timeout():
return 0.1
async def create_remote():
nonlocal cnt
nonlocal ifc
ifc.close() # clears the closed callback
cnt += 1
cnt = 0
ifc = AsyncStreamServer(reader, writer, None, create_remote, None)
assert ifc.create_remote
await ifc.create_remote()
assert cnt == 1
ifc.prot_set_timeout_cb(timeout)
await ifc.server_loop()
assert not ifc.create_remote
del ifc
cnt = 0
for inv in InverterBase:
print(f'InverterBase refs:{gc.get_referrers(inv)}')
cnt += 1
assert cnt == 0

View File

@@ -1,7 +1,7 @@
# test_with_pytest.py
import pytest
import asyncio
import sys,gc
import gc
from mock import patch
from enum import Enum

View File

@@ -71,22 +71,25 @@ def config_conn(test_hostname, test_port):
}
class TestType(Enum):
class FakeReader():
RD_TEST_0_BYTES = 1
RD_TEST_TIMEOUT = 2
RD_TEST_13_BYTES = 3
test = TestType.RD_TEST_0_BYTES
class FakeReader():
def __init__(self):
self.on_recv = asyncio.Event()
self.test = self.RD_TEST_0_BYTES
async def read(self, max_len: int):
print(f'fakeReader test: {self.test}')
await self.on_recv.wait()
if test == TestType.RD_TEST_0_BYTES:
if self.test == self.RD_TEST_0_BYTES:
return b''
elif test == TestType.RD_TEST_TIMEOUT:
elif self.test == self.RD_TEST_13_BYTES:
print('fakeReader return 13 bytes')
self.test = self.RD_TEST_0_BYTES
return b'test-data-req'
elif self.test == self.RD_TEST_TIMEOUT:
raise TimeoutError
def feed_eof(self):
return
@@ -95,8 +98,11 @@ class FakeReader():
class FakeWriter():
def __init__(self, conn='remote.intern'):
self.conn = conn
self.closing = False
def write(self, buf: bytes):
return
async def drain(self):
await asyncio.sleep(0)
def get_extra_info(self, sel: str):
if sel == 'peername':
return self.conn
@@ -104,11 +110,11 @@ class FakeWriter():
return 'sock:1234'
assert False
def is_closing(self):
return False
return self.closing
def close(self):
return
self.closing = True
async def wait_closed(self):
return
await asyncio.sleep(0)
@pytest.fixture
@@ -118,14 +124,19 @@ def patch_open():
return FakeReader(), FakeWriter(conn)
def new_open(host: str, port: int):
global test
if test == TestType.RD_TEST_TIMEOUT:
raise TimeoutError
return new_conn(f'{host}:{port}')
with patch.object(asyncio, 'open_connection', new_open) as conn:
yield conn
@pytest.fixture
def patch_open_timeout():
def new_open(host: str, port: int):
raise TimeoutError
with patch.object(asyncio, 'open_connection', new_open) as conn:
yield conn
@pytest.fixture
def patch_no_mqtt():
with patch.object(Mqtt, 'publish') as conn:
@@ -170,13 +181,11 @@ async def test_modbus_no_cnf():
assert Infos.stat['proxy']['Inverter_Cnt'] == 0
@pytest.mark.asyncio
async def test_modbus_cnf1(config_conn, patch_open):
async def test_modbus_cnf1(config_conn, patch_open_timeout):
_ = config_conn
_ = patch_open
global test
_ = patch_open_timeout
assert asyncio.get_running_loop()
Proxy.class_init()
test = TestType.RD_TEST_TIMEOUT
assert Infos.stat['proxy']['Inverter_Cnt'] == 0
loop = asyncio.get_event_loop()
@@ -194,10 +203,8 @@ async def test_modbus_cnf2(config_conn, patch_no_mqtt, patch_open):
_ = config_conn
_ = patch_open
_ = patch_no_mqtt
global test
assert asyncio.get_running_loop()
Proxy.class_init()
test = TestType.RD_TEST_0_BYTES
assert Infos.stat['proxy']['Inverter_Cnt'] == 0
ModbusTcp(asyncio.get_event_loop())
@@ -220,10 +227,8 @@ async def test_modbus_cnf3(config_conn, patch_no_mqtt, patch_open):
_ = config_conn
_ = patch_open
_ = patch_no_mqtt
global test
assert asyncio.get_running_loop()
Proxy.class_init()
test = TestType.RD_TEST_0_BYTES
assert Infos.stat['proxy']['Inverter_Cnt'] == 0
ModbusTcp(asyncio.get_event_loop(), tim_restart= 0)
@@ -253,10 +258,8 @@ async def test_mqtt_err(config_conn, patch_mqtt_err, patch_open):
_ = config_conn
_ = patch_open
_ = patch_mqtt_err
global test
assert asyncio.get_running_loop()
Proxy.class_init()
test = TestType.RD_TEST_0_BYTES
assert Infos.stat['proxy']['Inverter_Cnt'] == 0
ModbusTcp(asyncio.get_event_loop(), tim_restart= 0)
@@ -286,10 +289,8 @@ async def test_mqtt_except(config_conn, patch_mqtt_except, patch_open):
_ = config_conn
_ = patch_open
_ = patch_mqtt_except
global test
assert asyncio.get_running_loop()
Proxy.class_init()
test = TestType.RD_TEST_0_BYTES
assert Infos.stat['proxy']['Inverter_Cnt'] == 0
ModbusTcp(asyncio.get_event_loop(), tim_restart= 0)