update to 1.0.2
This commit is contained in:
254
source/tests/test_register_data.py
Normal file
254
source/tests/test_register_data.py
Normal file
@@ -0,0 +1,254 @@
|
||||
"""
|
||||
Test scenarios:
|
||||
Report any of the following anomalies:
|
||||
1. Not valid dictionary syntax [done]
|
||||
2. Dictionary key duplicates [done]
|
||||
3. "address" duplicates
|
||||
4. The value field has different keys than: [done]
|
||||
* address
|
||||
* register_type
|
||||
* data_format
|
||||
* si_adj
|
||||
* signed
|
||||
* data_unit
|
||||
* data_length
|
||||
* description
|
||||
5. "dataformat" starts with 'uint' but "signed" is different than "False"
|
||||
6. "dataformat" starts with 'int' but "signed" is different than "True"
|
||||
7. If "data_format" contains "int" in the name,
|
||||
"data_length" != <the number inside "data_format"> / 16 or 1 if no number is found
|
||||
in "data_length"
|
||||
8. "data_unit" is not one of following:
|
||||
{%, A, C, Hx, KWh, N/A, V, VA, Var, W, Wh, hour, second}
|
||||
Note: values are case sensitive
|
||||
9. "register_type" is different than {input, holding}
|
||||
Note: no values are case sensitive
|
||||
10. the key name contains "volt" but the "data_unit" is different than "V"
|
||||
11. the key name contains "current" but the "data_unit" is different than "A"
|
||||
12. "address" overlapping
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import unittest
|
||||
from itertools import filterfalse
|
||||
from pathlib import Path
|
||||
|
||||
from src.solaxx3.solax_registers_info import SolaxRegistersInfo
|
||||
|
||||
import_path = Path().resolve() / "tests" / "mock_packages"
|
||||
if str(import_path) not in sys.path:
|
||||
sys.path.append(str(import_path))
|
||||
|
||||
REGISTER_FILE = "src/solaxx3/solax_registers_info.py"
|
||||
|
||||
|
||||
class RegisterTests(unittest.TestCase):
|
||||
"""Tests for SolaxRegistersInfo."""
|
||||
|
||||
registers = SolaxRegistersInfo._registers
|
||||
|
||||
def test_tc1_valid_dictionary(self):
|
||||
"""Prints an error if `registers` is a dictionary."""
|
||||
|
||||
self.assertIsInstance(self.registers, dict)
|
||||
|
||||
def test_tc2_duplicate_keys(self):
|
||||
"""Prints an error if 'registers' has any duplicate keys."""
|
||||
|
||||
PATTERN = r"""
|
||||
"(?P<key>.*?)" # The key of the dictionary
|
||||
\s*: # colon
|
||||
\s*{ # start of value
|
||||
"""
|
||||
regex = re.compile(PATTERN, re.VERBOSE)
|
||||
|
||||
with open(REGISTER_FILE, "r", encoding="utf-8") as dictionary:
|
||||
list_keys = []
|
||||
|
||||
for line_number, line in filterfalse(
|
||||
lambda x: x[1].lstrip().startswith("#"), enumerate(dictionary, start=1)
|
||||
):
|
||||
re_match = regex.search(line)
|
||||
|
||||
if re_match is not None:
|
||||
key = re_match.group("key")
|
||||
self.assertNotIn(key, list_keys, f"line={line_number}")
|
||||
list_keys.append(key)
|
||||
|
||||
def test_tc4_correct_value_field(self):
|
||||
"""Prints an error if the keys do not have the following keys:
|
||||
- address, register_type, data_format, si_adj, signed, data_unit, data_length,
|
||||
description"""
|
||||
|
||||
VALID_SUBKEYS = [
|
||||
"address",
|
||||
"data_format",
|
||||
"data_length",
|
||||
"data_unit",
|
||||
"description",
|
||||
"register_type",
|
||||
"si_adj",
|
||||
"signed",
|
||||
]
|
||||
|
||||
for register, register_value in self.registers.items():
|
||||
self.assertListEqual(
|
||||
sorted(list(register_value.keys())), VALID_SUBKEYS, f"dict={register!r}"
|
||||
)
|
||||
|
||||
def test_tc5_correctly_unsigned(self):
|
||||
"""Prints an error if subkey `data_format` starts with `uint` but `signed` is
|
||||
different than False."""
|
||||
|
||||
for register, register_value in self.registers.items():
|
||||
self.assertFalse(
|
||||
register_value["data_format"].startswith("uint")
|
||||
and register_value["signed"] is not False,
|
||||
f"not correctly signed: dict='{register}'",
|
||||
)
|
||||
|
||||
def test_tc6_correctly_signed(self):
|
||||
"""Prints an error if subkey `data_format` starts with 'int' but `signed`
|
||||
is different than True."""
|
||||
|
||||
for register, register_value in self.registers.items():
|
||||
self.assertFalse(
|
||||
register_value["data_format"].startswith("int")
|
||||
and register_value["signed"] is not True,
|
||||
f"not correctly signed: dict='{register}'",
|
||||
)
|
||||
|
||||
def test_tc7_correct_data_length(self):
|
||||
"""Prints an error if 'data_format' does not match 'data_length'."""
|
||||
|
||||
PATTERN = r"int(?P<bits>\d+)"
|
||||
regex = re.compile(PATTERN)
|
||||
|
||||
for register, register_value in self.registers.items():
|
||||
match = regex.search(register_value["data_format"])
|
||||
|
||||
if match:
|
||||
bits = int(match.group("bits"))
|
||||
self.assertEqual(
|
||||
bits / 16, register_value["data_length"], f"dict={register!r}"
|
||||
)
|
||||
|
||||
def test_tc8_correct_data_unit_value(self):
|
||||
"""Prints an error if `data_unit` is not %, C, Hz, KWh, N/A, V, VA, Var,
|
||||
W, Wh, hour, second."""
|
||||
|
||||
POSSIBLE_DATA_UNITS = (
|
||||
"min",
|
||||
"bps",
|
||||
"%",
|
||||
"A",
|
||||
"C",
|
||||
"Hz",
|
||||
"KWh",
|
||||
"N/A",
|
||||
"V",
|
||||
"VA",
|
||||
"Var",
|
||||
"W",
|
||||
"Wh",
|
||||
"hour",
|
||||
"second",
|
||||
)
|
||||
|
||||
for register, register_value in self.registers.items():
|
||||
self.assertGreater(
|
||||
len(register_value["data_unit"]),
|
||||
0,
|
||||
f"empty 'data_unit': dict={register!r}",
|
||||
)
|
||||
self.assertIn(
|
||||
register_value["data_unit"],
|
||||
POSSIBLE_DATA_UNITS,
|
||||
f"invalid data_unit: '{register_value['data_unit']}'; 'dict='{register}'",
|
||||
)
|
||||
|
||||
def test_tc9_correct_register_type(self):
|
||||
"""Prints an error if `register_type` is not 'input' or 'holding'."""
|
||||
|
||||
POSSIBLE_REGISTER_TYPES = ("input", "holding")
|
||||
|
||||
for register, register_value in self.registers.items():
|
||||
self.assertIn(
|
||||
register_value["register_type"],
|
||||
POSSIBLE_REGISTER_TYPES,
|
||||
f"dict='{register}'",
|
||||
)
|
||||
|
||||
def test_tc10_correct_data_unit_with_volt(self):
|
||||
"""Prints an error if the key contains 'volt' but `data_unit` is differrent
|
||||
than 'V'."""
|
||||
|
||||
for register, register_value in self.registers.items():
|
||||
self.assertFalse(
|
||||
"volt" in register
|
||||
and register_value["data_unit"] != "V"
|
||||
and "percent" not in register
|
||||
and "ratio" not in register,
|
||||
f"'data_unit' does not match name: dict={register!r}",
|
||||
)
|
||||
|
||||
def test_tc11_correct_data_unit_with_current(self):
|
||||
"""Prints an error if the key contains 'current' but `data_unit` is
|
||||
differrent than 'A'."""
|
||||
|
||||
for register, register_value in self.registers.items():
|
||||
self.assertFalse(
|
||||
"current" in register and register_value["data_unit"] != "A",
|
||||
f"'data_unit' does not match name: dict='{register}'",
|
||||
)
|
||||
|
||||
def test_tc12_correct_addresses(self):
|
||||
"""Prints an error if there is any address overlapping."""
|
||||
|
||||
holding_addresses: list = []
|
||||
input_addresses: list = []
|
||||
|
||||
for register, register_value in self.registers.items():
|
||||
addresses = (
|
||||
holding_addresses
|
||||
if register_value["register_type"] == "holding"
|
||||
else input_addresses
|
||||
)
|
||||
data_length = register_value["data_length"]
|
||||
|
||||
for x in range(data_length):
|
||||
address = register_value["address"] + x
|
||||
self.assertNotIn(address, addresses, f"dict={register!r}")
|
||||
addresses.append(address)
|
||||
|
||||
def test_tc13_subkey_duplicates(self):
|
||||
"""Prints an error if there are any subkey duplicates."""
|
||||
|
||||
PATTERN = r"""
|
||||
"(?P<subkey>.*?)" # dictionary subkey
|
||||
\s*: # colon
|
||||
\s*?[^[]+$ # value different than a dict
|
||||
"""
|
||||
|
||||
regex = re.compile(PATTERN, re.VERBOSE)
|
||||
|
||||
with open(REGISTER_FILE, "r", encoding="utf-8") as dictionary:
|
||||
subkeys = []
|
||||
|
||||
for line_number, line in filterfalse(
|
||||
lambda x: x[1].lstrip().startswith("#"), enumerate(dictionary, start=1)
|
||||
):
|
||||
match = regex.search(line)
|
||||
|
||||
if match:
|
||||
subkey = match.group("subkey")
|
||||
|
||||
self.assertNotIn(subkey, subkeys, f"line={line_number}")
|
||||
subkeys.append(subkey)
|
||||
else:
|
||||
subkeys.clear()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user