Dev 0.11 (#175)
* use random IP adresses for unit tests * Docker: The description ist missing (#171) Fixes #167 * S allius/issue167 (#172) * cleanup * Sonar qube 6 (#174) * test class ModbusConn
This commit is contained in:
@@ -325,7 +325,11 @@ def test_build_ha_conf1(contr_data_seq):
|
||||
|
||||
assert tests==4
|
||||
|
||||
def test_build_ha_conf2(contr_data_seq):
|
||||
i = InfosG3()
|
||||
i.static_init() # initialize counter
|
||||
|
||||
tests = 0
|
||||
for d_json, comp, node_id, id in i.ha_proxy_confs(ha_prfx="tsun/", node_id = 'proxy/', snr = '456'):
|
||||
|
||||
if id == 'out_power_123':
|
||||
@@ -344,9 +348,9 @@ def test_build_ha_conf1(contr_data_seq):
|
||||
assert d_json == json.dumps({"name": "Active Inverter Connections", "stat_t": "tsun/proxy/proxy", "dev_cla": None, "stat_cla": None, "uniq_id": "inv_count_456", "val_tpl": "{{value_json['Inverter_Cnt'] | int}}", "ic": "mdi:counter", "dev": {"name": "Proxy", "sa": "Proxy", "mdl": "proxy", "mf": "Stefan Allius", "sw": "unknown", "ids": ["proxy"]}, "o": {"name": "proxy", "sw": "unknown"}})
|
||||
tests +=1
|
||||
|
||||
assert tests==5
|
||||
assert tests==1
|
||||
|
||||
def test_build_ha_conf2(contr_data_seq, inv_data_seq, inv_data_seq2):
|
||||
def test_build_ha_conf3(contr_data_seq, inv_data_seq, inv_data_seq2):
|
||||
i = InfosG3()
|
||||
for key, result in i.parse (contr_data_seq):
|
||||
pass # side effect in calling i.parse()
|
||||
@@ -397,12 +401,9 @@ def test_must_incr_total(inv_data_seq2, inv_data_seq2_zero):
|
||||
assert json.dumps(i.db['env']) == json.dumps({"Inverter_Temp": 23})
|
||||
tests = 0
|
||||
for key, update in i.parse (inv_data_seq2):
|
||||
if key == 'total':
|
||||
if key == 'total' or key == 'env':
|
||||
assert update == False
|
||||
tests +=1
|
||||
elif key == 'env':
|
||||
assert update == False
|
||||
tests +=1
|
||||
|
||||
assert tests==3
|
||||
assert json.dumps(i.db['total']) == json.dumps({'Daily_Generation': 1.7, 'Total_Generation': 17.36})
|
||||
@@ -442,12 +443,9 @@ def test_must_incr_total2(inv_data_seq2, inv_data_seq2_zero):
|
||||
|
||||
tests = 0
|
||||
for key, update in i.parse (inv_data_seq2_zero):
|
||||
if key == 'total':
|
||||
if key == 'total' or key == 'env':
|
||||
assert update == False
|
||||
tests +=1
|
||||
elif key == 'env':
|
||||
assert update == False
|
||||
tests +=1
|
||||
|
||||
assert tests==3
|
||||
assert json.dumps(i.db['total']) == json.dumps({})
|
||||
@@ -456,12 +454,9 @@ def test_must_incr_total2(inv_data_seq2, inv_data_seq2_zero):
|
||||
|
||||
tests = 0
|
||||
for key, update in i.parse (inv_data_seq2):
|
||||
if key == 'total':
|
||||
if key == 'total' or key == 'env':
|
||||
assert update == True
|
||||
tests +=1
|
||||
elif key == 'env':
|
||||
assert update == True
|
||||
tests +=1
|
||||
|
||||
assert tests==3
|
||||
assert json.dumps(i.db['total']) == json.dumps({'Daily_Generation': 1.7, 'Total_Generation': 17.36})
|
||||
|
||||
@@ -1,18 +1,33 @@
|
||||
|
||||
# test_with_pytest.py
|
||||
import pytest, json, math
|
||||
import pytest, json, math, random
|
||||
from app.src.infos import Register
|
||||
from app.src.gen3plus.infos_g3p import InfosG3P
|
||||
from app.src.gen3plus.infos_g3p import RegisterMap
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def str_test_ip():
|
||||
ip = ".".join(str(random.randint(1, 254)) for _ in range(4))
|
||||
print(f'random_ip: {ip}')
|
||||
return ip
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def bytes_test_ip(str_test_ip):
|
||||
ip = bytes(str.encode(str_test_ip))
|
||||
l = len(ip)
|
||||
if l < 16:
|
||||
ip = ip + bytearray(16-l)
|
||||
print(f'random_ip: {ip}')
|
||||
return ip
|
||||
|
||||
@pytest.fixture
|
||||
def device_data(): # 0x4110 ftype: 0x02
|
||||
def device_data(bytes_test_ip): # 0x4110 ftype: 0x02
|
||||
msg = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xba\xd2\x00\x00'
|
||||
msg += b'\x19\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x64\x01\x4c\x53'
|
||||
msg += b'\x57\x35\x42\x4c\x45\x5f\x31\x37\x5f\x30\x32\x42\x30\x5f\x31\x2e'
|
||||
msg += b'\x30\x35\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
msg += b'\x00\x00\x00\x00\x00\x00\x40\x2a\x8f\x4f\x51\x54\x31\x39\x32\x2e'
|
||||
msg += b'\x31\x36\x38\x2e\x38\x30\x2e\x34\x39\x00\x00\x00\x0f\x00\x01\xb0'
|
||||
msg += b'\x00\x00\x00\x00\x00\x00\x40\x2a\x8f\x4f\x51\x54' + bytes_test_ip
|
||||
msg += b'\x0f\x00\x01\xb0'
|
||||
msg += b'\x02\x0f\x00\xff\x56\x31\x2e\x31\x2e\x30\x30\x2e\x30\x42\x00\x00'
|
||||
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\x00\x00'
|
||||
@@ -63,14 +78,14 @@ def test_default_db():
|
||||
"collector": {"Chip_Type": "IGEN TECH"},
|
||||
})
|
||||
|
||||
def test_parse_4110(device_data: bytes):
|
||||
def test_parse_4110(str_test_ip, device_data: bytes):
|
||||
i = InfosG3P(client_mode=False)
|
||||
i.db.clear()
|
||||
for key, update in i.parse (device_data, 0x41, 2):
|
||||
pass # side effect is calling generator i.parse()
|
||||
|
||||
assert json.dumps(i.db) == json.dumps({
|
||||
'controller': {"Data_Up_Interval": 300, "Collect_Interval": 1, "Heartbeat_Interval": 120, "Signal_Strength": 100, "IP_Address": "192.168.80.49", "Sensor_List": "02b0"},
|
||||
'controller': {"Data_Up_Interval": 300, "Collect_Interval": 1, "Heartbeat_Interval": 120, "Signal_Strength": 100, "IP_Address": str_test_ip, "Sensor_List": "02b0"},
|
||||
'collector': {"Chip_Model": "LSW5BLE_17_02B0_1.05", "Collector_Fw_Version": "V1.1.00.0B"},
|
||||
})
|
||||
|
||||
@@ -139,7 +154,11 @@ def test_build_ha_conf1():
|
||||
|
||||
assert tests==7
|
||||
|
||||
def test_build_ha_conf2():
|
||||
i = InfosG3P(client_mode=False)
|
||||
i.static_init() # initialize counter
|
||||
|
||||
tests = 0
|
||||
for d_json, comp, node_id, id in i.ha_proxy_confs(ha_prfx="tsun/", node_id = 'proxy/', snr = '456'):
|
||||
|
||||
if id == 'out_power_123':
|
||||
@@ -161,9 +180,9 @@ def test_build_ha_conf1():
|
||||
assert d_json == json.dumps({"name": "Active Inverter Connections", "stat_t": "tsun/proxy/proxy", "dev_cla": None, "stat_cla": None, "uniq_id": "inv_count_456", "val_tpl": "{{value_json['Inverter_Cnt'] | int}}", "ic": "mdi:counter", "dev": {"name": "Proxy", "sa": "Proxy", "mdl": "proxy", "mf": "Stefan Allius", "sw": "unknown", "ids": ["proxy"]}, "o": {"name": "proxy", "sw": "unknown"}})
|
||||
tests +=1
|
||||
|
||||
assert tests==8
|
||||
assert tests==1
|
||||
|
||||
def test_build_ha_conf2():
|
||||
def test_build_ha_conf3():
|
||||
i = InfosG3P(client_mode=True)
|
||||
i.static_init() # initialize counter
|
||||
|
||||
@@ -209,7 +228,11 @@ def test_build_ha_conf2():
|
||||
|
||||
assert tests==7
|
||||
|
||||
def test_build_ha_conf4():
|
||||
i = InfosG3P(client_mode=True)
|
||||
i.static_init() # initialize counter
|
||||
|
||||
tests = 0
|
||||
for d_json, comp, node_id, id in i.ha_proxy_confs(ha_prfx="tsun/", node_id = 'proxy/', snr = '456'):
|
||||
|
||||
if id == 'out_power_123':
|
||||
@@ -231,7 +254,7 @@ def test_build_ha_conf2():
|
||||
assert d_json == json.dumps({"name": "Active Inverter Connections", "stat_t": "tsun/proxy/proxy", "dev_cla": None, "stat_cla": None, "uniq_id": "inv_count_456", "val_tpl": "{{value_json['Inverter_Cnt'] | int}}", "ic": "mdi:counter", "dev": {"name": "Proxy", "sa": "Proxy", "mdl": "proxy", "mf": "Stefan Allius", "sw": "unknown", "ids": ["proxy"]}, "o": {"name": "proxy", "sw": "unknown"}})
|
||||
tests +=1
|
||||
|
||||
assert tests==8
|
||||
assert tests==1
|
||||
|
||||
def test_exception_and_eval(inverter_data: bytes):
|
||||
|
||||
|
||||
90
app/tests/test_inverter.py
Normal file
90
app/tests/test_inverter.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# test_with_pytest.py
|
||||
import pytest
|
||||
import asyncio
|
||||
import aiomqtt
|
||||
import logging
|
||||
|
||||
from mock import patch, Mock
|
||||
from app.src.singleton import Singleton
|
||||
from app.src.inverter import Inverter
|
||||
from app.src.mqtt import Mqtt
|
||||
from app.src.gen3plus.solarman_v5 import SolarmanV5
|
||||
from app.src.config import Config
|
||||
|
||||
|
||||
pytest_plugins = ('pytest_asyncio',)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def module_init():
|
||||
def new_init(cls, cb_mqtt_is_up):
|
||||
cb_mqtt_is_up()
|
||||
|
||||
Singleton._instances.clear()
|
||||
with patch.object(Mqtt, '__init__', new_init):
|
||||
yield
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def test_port():
|
||||
return 1883
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def test_hostname():
|
||||
# if getenv("GITHUB_ACTIONS") == "true":
|
||||
# return 'mqtt'
|
||||
# else:
|
||||
return 'test.mosquitto.org'
|
||||
|
||||
@pytest.fixture
|
||||
def config_conn(test_hostname, test_port):
|
||||
Config.act_config = {
|
||||
'mqtt':{
|
||||
'host': test_hostname,
|
||||
'port': test_port,
|
||||
'user': '',
|
||||
'passwd': ''
|
||||
},
|
||||
'ha':{
|
||||
'auto_conf_prefix': 'homeassistant',
|
||||
'discovery_prefix': 'homeassistant',
|
||||
'entity_prefix': 'tsun',
|
||||
'proxy_node_id': 'test_1',
|
||||
'proxy_unique_id': ''
|
||||
},
|
||||
'inverters': {
|
||||
'allow_all': True,
|
||||
"R170000000000001":{
|
||||
'node_id': 'inv_1'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_inverter_cb(config_conn):
|
||||
_ = config_conn
|
||||
|
||||
with patch.object(Inverter, '_cb_mqtt_is_up', wraps=Inverter._cb_mqtt_is_up) as spy:
|
||||
print('call Inverter.class_init')
|
||||
Inverter.class_init()
|
||||
assert 'homeassistant/' == Inverter.discovery_prfx
|
||||
assert 'tsun/' == Inverter.entity_prfx
|
||||
assert 'test_1/' == Inverter.proxy_node_id
|
||||
spy.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mqtt_is_up(config_conn):
|
||||
_ = config_conn
|
||||
|
||||
with patch.object(Mqtt, 'publish') as spy:
|
||||
Inverter.class_init()
|
||||
await Inverter._cb_mqtt_is_up()
|
||||
spy.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mqtt_proxy_statt_invalid(config_conn):
|
||||
_ = config_conn
|
||||
|
||||
with patch.object(Mqtt, 'publish') as spy:
|
||||
Inverter.class_init()
|
||||
await Inverter._async_publ_mqtt_proxy_stat('InValId_kEy')
|
||||
spy.assert_not_called()
|
||||
78
app/tests/test_modbus_tcp.py
Normal file
78
app/tests/test_modbus_tcp.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# test_with_pytest.py
|
||||
import pytest
|
||||
import asyncio
|
||||
|
||||
from mock import patch
|
||||
from app.src.singleton import Singleton
|
||||
from app.src.config import Config
|
||||
from app.src.infos import Infos
|
||||
from app.src.modbus_tcp import ModbusConn
|
||||
|
||||
|
||||
pytest_plugins = ('pytest_asyncio',)
|
||||
|
||||
# initialize the proxy statistics
|
||||
Infos.static_init()
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def module_init():
|
||||
Singleton._instances.clear()
|
||||
yield
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def test_port():
|
||||
return 1883
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def test_hostname():
|
||||
# if getenv("GITHUB_ACTIONS") == "true":
|
||||
# return 'mqtt'
|
||||
# else:
|
||||
return 'test.mosquitto.org'
|
||||
|
||||
@pytest.fixture
|
||||
def config_mqtt_conn(test_hostname, test_port):
|
||||
Config.act_config = {'mqtt':{'host': test_hostname, 'port': test_port, 'user': '', 'passwd': ''},
|
||||
'ha':{'auto_conf_prefix': 'homeassistant','discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun'}
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def config_no_conn(test_port):
|
||||
Config.act_config = {'mqtt':{'host': "", 'port': test_port, 'user': '', 'passwd': ''},
|
||||
'ha':{'auto_conf_prefix': 'homeassistant','discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun'}
|
||||
}
|
||||
|
||||
class FakeReader():
|
||||
pass
|
||||
|
||||
|
||||
class FakeWriter():
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patch_open():
|
||||
async def new_conn(conn):
|
||||
await asyncio.sleep(0.01)
|
||||
return FakeReader(), FakeWriter()
|
||||
|
||||
def new_open(host: str, port: int):
|
||||
return new_conn(None)
|
||||
|
||||
with patch.object(asyncio, 'open_connection', new_open) as conn:
|
||||
yield conn
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_modbus_conn(patch_open):
|
||||
_ = patch_open
|
||||
assert Infos.stat['proxy']['Inverter_Cnt'] == 0
|
||||
|
||||
async with ModbusConn('test.local', 1234) as stream:
|
||||
assert stream.node_id == 'G3P'
|
||||
assert stream.addr == ('test.local', 1234)
|
||||
assert type(stream.reader) is FakeReader
|
||||
assert type(stream.writer) is FakeWriter
|
||||
assert Infos.stat['proxy']['Inverter_Cnt'] == 1
|
||||
|
||||
assert Infos.stat['proxy']['Inverter_Cnt'] == 0
|
||||
@@ -5,6 +5,7 @@ import aiomqtt
|
||||
import logging
|
||||
|
||||
from mock import patch, Mock
|
||||
from app.src.singleton import Singleton
|
||||
from app.src.mqtt import Mqtt
|
||||
from app.src.modbus import Modbus
|
||||
from app.src.gen3plus.solarman_v5 import SolarmanV5
|
||||
@@ -13,7 +14,10 @@ from app.src.config import Config
|
||||
|
||||
pytest_plugins = ('pytest_asyncio',)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def module_init():
|
||||
Singleton._instances.clear()
|
||||
yield
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def test_port():
|
||||
|
||||
@@ -7,6 +7,7 @@ class Test(metaclass=Singleton):
|
||||
pass # is a dummy test class
|
||||
|
||||
def test_singleton_metaclass():
|
||||
Singleton._instances.clear()
|
||||
a = Test()
|
||||
assert 1 == len(Singleton._instances)
|
||||
b = Test()
|
||||
|
||||
@@ -3,6 +3,7 @@ import struct
|
||||
import time
|
||||
import asyncio
|
||||
import logging
|
||||
import random
|
||||
from math import isclose
|
||||
from app.src.gen3plus.solarman_v5 import SolarmanV5
|
||||
from app.src.config import Config
|
||||
@@ -148,6 +149,12 @@ def incorrect_checksum(buf):
|
||||
checksum = (sum(buf[1:])+1) & 0xff
|
||||
return checksum.to_bytes(length=1)
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def str_test_ip():
|
||||
ip = ".".join(str(random.randint(1, 254)) for _ in range(4))
|
||||
print(f'random_ip: {ip}')
|
||||
return ip
|
||||
|
||||
@pytest.fixture
|
||||
def device_ind_msg(): # 0x4110
|
||||
msg = b'\xa5\xd4\x00\x10\x41\x00\x01' +get_sn() +b'\x02\xba\xd2\x00\x00'
|
||||
@@ -1692,7 +1699,7 @@ async def test_modbus_polling(config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp
|
||||
m.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_client_mode(config_tsun_inv1):
|
||||
async def test_start_client_mode(config_tsun_inv1, str_test_ip):
|
||||
_ = config_tsun_inv1
|
||||
assert asyncio.get_running_loop()
|
||||
m = MemoryStream(b'')
|
||||
@@ -1700,9 +1707,9 @@ async def test_start_client_mode(config_tsun_inv1):
|
||||
assert m.no_forwarding == False
|
||||
assert m.mb_timer.tim == None
|
||||
assert asyncio.get_running_loop() == m.mb_timer.loop
|
||||
await m.send_start_cmd(get_sn_int(), '192.168.1.1', m.mb_first_timeout)
|
||||
await m.send_start_cmd(get_sn_int(), str_test_ip, m.mb_first_timeout)
|
||||
assert m.writer.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x01\x00!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x030\x00\x000J\xde\xf1\x15')
|
||||
assert m.db.get_db_value(Register.IP_ADDRESS) == '192.168.1.1'
|
||||
assert m.db.get_db_value(Register.IP_ADDRESS) == str_test_ip
|
||||
assert isclose(m.db.get_db_value(Register.POLLING_INTERVAL), 0.5)
|
||||
assert m.db.get_db_value(Register.HEARTBEAT_INTERVAL) == 120
|
||||
|
||||
|
||||
@@ -1150,7 +1150,7 @@ def test_msg_ota_invalid(config_tsun_inv1, msg_ota_invalid):
|
||||
m.close()
|
||||
|
||||
def test_msg_unknown(config_tsun_inv1, msg_unknown):
|
||||
config_tsun_inv1
|
||||
_ = config_tsun_inv1
|
||||
m = MemoryStream(msg_unknown, (0,), False)
|
||||
m.db.stat['proxy']['Unknown_Msg'] = 0
|
||||
m.read() # read complete msg, and dispatch msg
|
||||
|
||||
Reference in New Issue
Block a user