Init Commit: Moved bproto to seperate repo

This commit is contained in:
AlexanderHD27
2025-04-14 14:43:03 +02:00
commit 45bfc724fc
125 changed files with 10822 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
import sys
import pytest
from typing import Any
sys.path.append('src/')
from protocol_components.dtypes import BprotoFieldBaseType
from parser.parser import create_ast_parser, bproto_ErrorListener
from parser.ast_visitor import BprotoASTVisitor
@pytest.mark.parametrize("source_text,expected_output", [
("uint64\n", (BprotoFieldBaseType.UINT64, 1, None)),
("float32\n", (BprotoFieldBaseType.FLOAT32, 1, None)),
("float32[8]\n", (BprotoFieldBaseType.FLOAT32, 8, None)),
])
def test_ast_field_dtype_defintion(
source_text: str,
expected_output: tuple[BprotoFieldBaseType, int, Any]):
err_listner = bproto_ErrorListener()
parser = create_ast_parser(source_text, err_listner)
ast = parser.dtype()
vinterp = BprotoASTVisitor()
assert len(err_listner.syntax_error_list) == 0
res = vinterp.visit(ast)
assert isinstance(res, tuple)
assert len(res) == 3
# Correct dtype
assert res[0] == expected_output[0].value
# Correct array size
assert res[1] == expected_output[1]
# Correct refrerence; Should normally be None be Non
assert res[2] == expected_output[2]

View File

@@ -0,0 +1,23 @@
import sys
sys.path.append('src/')
from parser.parser import create_ast_parser, bproto_ErrorListener
from parser.ast_visitor import BprotoASTVisitor
def test_ast_tag_line():
source = "protocol HCP version 25\n"
err_listner = bproto_ErrorListener()
parser = create_ast_parser(source, err_listner)
ast = parser.protocol_header()
vinterp = BprotoASTVisitor()
assert len(err_listner.syntax_error_list) == 0
res = vinterp.visit(ast)
assert isinstance(res, tuple)
assert len(res) == 2
name, version = res
assert name == "HCP"
assert version == 25

View File

@@ -0,0 +1,26 @@
import sys
from compiler import BprotoCompiler
sys.path.append('src/')
from errors import BprotoCompilerError
from backend.rendering.cBackend import CBackendRenderer
from backend.fsOutput import BackendFileSystemOutput
def test_compiler_c_smoke_test():
# Load Refernces
with open("test/test_protocol.bproto") as f:
source_text = f.read()
# Comile Stuff
compiler = BprotoCompiler([
CBackendRenderer(".")
], "template")
try:
output: BackendFileSystemOutput = compiler.compile(source_text, ".")
except BprotoCompilerError:
assert False, "This should run througth"
output.saveToDisk("test/backend_output/c")
output.toString()

View File

@@ -0,0 +1,59 @@
import sys
import pytest
from compiler import BprotoCompiler
sys.path.append('src/')
from errors import BprotoCompilerError
from backend.rendering.pythonBackend import PythonBackendRenderer
from backend.fsOutput import BackendFileSystemOutput
@pytest.mark.skip()
def test_compiler_python_against_ref():
# Load Refernces
with open("test/test_protocol.bproto") as f:
source_text = f.read()
# Comile Stuff
compiler = BprotoCompiler([
PythonBackendRenderer(".")
], "template")
try:
output: BackendFileSystemOutput = compiler.compile(source_text, ".")
except BprotoCompilerError:
assert False, "This should run througth"
output.saveToDisk("test/backend_output/python")
string_output = output.toString()
with open("test/reference/python/HCP_protocol_enum.py") as f:
assert f.read() == string_output.get("HCP_protocol_enum.py")
with open("test/reference/python/HCP_protocol_bitfield.py") as f:
assert f.read() == string_output.get("HCP_protocol_bitfield.py")
with open("test/reference/python/HCP_protocol_message_ids.py") as f:
assert f.read() == string_output.get("HCP_protocol_message_ids.py")
with open("test/reference/python/HCP_protocol_packets.py") as f:
assert f.read() == string_output.get("HCP_protocol_packets.py")
def test_compiler_python_smoke_test():
# Load Refernces
with open("test/test_protocol.bproto") as f:
source_text = f.read()
# Comile Stuff
compiler = BprotoCompiler([
PythonBackendRenderer(".")
], "template")
try:
output: BackendFileSystemOutput = compiler.compile(source_text, ".")
except BprotoCompilerError:
assert False, "This should run througth"
output.saveToDisk("test/backend_output/python")
output.toString()

View File

@@ -0,0 +1,32 @@
import sys
from compiler import BprotoCompiler
sys.path.append('src/')
from errors import BprotoCompilerError
from backend.rendering.txtBackend import TxtBackendRenderer
from backend.fsOutput import BackendFileSystemOutput
def test_compiler_txt_against_ref():
with open("test/test_protocol.bproto") as f:
source_text = f.read()
# Comile Stuff
compiler = BprotoCompiler([
TxtBackendRenderer(".")
], "template")
try:
output: BackendFileSystemOutput = compiler.compile(source_text, ".")
except BprotoCompilerError:
assert False, "This should run througth"
string_output = output.toString()
output.saveToDisk("test/backend_output/txt")
# Load Refernces
with open("test/reference/txt/protocolSummary.txt") as f:
expected_output = f.read()
assert isinstance(string_output.get("protocolSummary.txt"), str)
assert string_output.get("protocolSummary.txt") == expected_output

View File

@@ -0,0 +1,168 @@
import sys
import os
import pytest
import shutil
from jinja2 import Environment, FileSystemLoader, select_autoescape
sys.path.append('src/')
from backend.fsOutput import BackendFSOutFile, BackendFSOutStaticConent, BackendFSOutFolder, BackendFSOutputJinjaFile
TEST_FOLDER = "/tmp/protocolGenerator_test_data"
@pytest.fixture
def filesystem_folder():
yield TEST_FOLDER
shutil.rmtree(TEST_FOLDER)
print("Cleanup done")
def test_fsOutput_saveToDisk(filesystem_folder: str):
jinja_env = Environment(
loader=FileSystemLoader("test/data/templates/"),
autoescape=select_autoescape()
)
# Setup
root_folder = BackendFSOutFolder("root", [
BackendFSOutFile("file1", "some content"),
BackendFSOutFile("file2", "some other content"),
BackendFSOutFolder("subfolder", [
BackendFSOutFile("file3", "some more content #2"),
BackendFSOutFile("file4", "even more content"),
]),
BackendFSOutStaticConent("test/data/static_folder/static_content2.txt", "static_content2.txt"),
BackendFSOutStaticConent("test/data/static_content1.txt", "static_content1.txt"),
BackendFSOutputJinjaFile(jinja_env, "template.jinja2", "template.txt", {"name": "World"})
])
root_folder.saveToDisk(filesystem_folder)
# Test Structure
assert os.path.isdir(filesystem_folder)
assert os.path.isfile(filesystem_folder + "/root/file1")
assert os.path.isfile(filesystem_folder + "/root/file2")
assert os.path.isdir(filesystem_folder + "/root/subfolder")
assert os.path.isfile(filesystem_folder + "/root/subfolder/file3")
assert os.path.isfile(filesystem_folder + "/root/subfolder/file4")
assert os.path.isfile(filesystem_folder + "/root/static_content1.txt")
assert os.path.isfile(filesystem_folder + "/root/static_content2.txt")
assert os.path.isfile(filesystem_folder + "/root/template.txt")
# Test Content
with open(filesystem_folder + "/root/file1", "r") as f:
assert f.read() == "some content"
with open(filesystem_folder + "/root/file2", "r") as f:
assert f.read() == "some other content"
with open(filesystem_folder + "/root/subfolder/file3", "r") as f:
assert f.read() == "some more content #2"
with open(filesystem_folder + "/root/subfolder/file4", "r") as f:
assert f.read() == "even more content"
with open(filesystem_folder + "/root/static_content1.txt", "r") as f:
assert f.read() == "test1"
with open(filesystem_folder + "/root/static_content2.txt", "r") as f:
assert f.read() == "test2"
with open(filesystem_folder + "/root/template.txt", "r") as f:
assert f.read() == "Hello World!"
def test_fsOutput_treeString():
jinja_env = Environment(
loader=FileSystemLoader("test/data/templates/"),
autoescape=select_autoescape()
)
root_folder = BackendFSOutFolder("root", [
BackendFSOutFile("file1", "some content"),
BackendFSOutFile("file2", "some other content"),
BackendFSOutFolder("subfolder", [
BackendFSOutFile("file3", "some more content #2"),
BackendFSOutFile("file4", "even more content"),
BackendFSOutputJinjaFile(jinja_env, "template.jinja", "generate", {"name": "Jinja2"})
]),
BackendFSOutStaticConent("test/data/static_folder", "data"),
BackendFSOutputJinjaFile(jinja_env, "template.jinja", None, {"name": "World"})
])
string = root_folder.assemble_file_tree_string()
assert string == """└── root
├── [file1]
├── [file2]
├── subfolder
│ ├── [file3]
│ ├── [file4]
│ └── Template [generate]
├── Static [data]
└── Template [template]
"""
def test_fsOutput_toString():
jinja_env = Environment(
loader=FileSystemLoader("test/data/templates/"),
autoescape=select_autoescape()
)
root_folder = BackendFSOutFolder("root", [
BackendFSOutFile("file1", "some content"),
BackendFSOutFile("file2", "some other content"),
BackendFSOutFolder("subfolder", [
BackendFSOutFile("file3", "some more content #2"),
BackendFSOutFile("file4", "even more content"),
BackendFSOutputJinjaFile(jinja_env, "template.jinja2", "generate", {"name": "Jinja2"})
]),
BackendFSOutStaticConent("test/data/static_folder", "data/static_folder"),
BackendFSOutStaticConent("test/data/static_content1.txt", "data"),
BackendFSOutputJinjaFile(jinja_env, "template.jinja2", None, {"name": "World"})
])
res = root_folder.toString()
assert res == {
"root/file1": "some content",
"root/file2": "some other content",
"root/subfolder/file3": "some more content #2",
"root/subfolder/file4": "even more content",
"root/subfolder/generate": "Hello Jinja2!",
"root/data/static_content1.txt": "test1",
"root/data/static_folder/static_content2.txt": "test2",
"root/template": "Hello World!"
}
def test_fsOutput_pathnormalization(filesystem_folder: str):
root_folder = BackendFSOutFolder(".", [
BackendFSOutFolder(".", [
BackendFSOutFile("file2", "some other content"),
]),
BackendFSOutFile("file1", "some content"),
])
# Check if this works for toString
res = root_folder.toString()
assert res == {
"file2": "some other content",
"file1": "some content"
}
# Check if this works for saveToDisk
root_folder.saveToDisk(filesystem_folder)
assert os.path.isfile(filesystem_folder + "/file1")
assert os.path.isfile(filesystem_folder + "/file2")
with open(filesystem_folder + "/file1", "r") as f:
assert f.read() == "some content"
with open(filesystem_folder + "/file2", "r") as f:
assert f.read() == "some other content"

View File

@@ -0,0 +1,49 @@
import sys
sys.path.append('src/')
from nameHandling.base import ComponentName, NameStyleBproto
def test_component_name():
assert ComponentName(["test"]) != 1234
assert ComponentName(["test"]) == ComponentName(["test"])
assert ComponentName(["test"]) != ComponentName(["test", "test"])
assert hash(ComponentName(["test"])) == hash(ComponentName(["test"]))
assert hash(ComponentName(["test"])) != hash(ComponentName(["test", "test"]))
assert repr(ComponentName(["test"])) == "ComponentName(Test)"
assert repr(ComponentName(["test", "test"])) == "ComponentName(Test-Test)"
def test_ComponentNameStyleBproto():
assert NameStyleBproto.fromStr("test") == ComponentName(["test"])
assert NameStyleBproto.fromStr("ThisIsATest") == ComponentName(["this", "is", "a", "test"])
assert NameStyleBproto.fromStr("thisIsATest") == ComponentName(["this", "is", "a", "test"])
assert NameStyleBproto.fromStr("this-is-a-test") == ComponentName(["this", "is", "a", "test"])
assert NameStyleBproto.fromStr("this-Is-A-test") == ComponentName(["this", "is", "a", "test"])
assert NameStyleBproto.fromStr("this_is_a_test") == ComponentName(["this", "is", "a", "test"])
assert NameStyleBproto.fromStr("this_Is_a_Test") == ComponentName(["this", "is", "a", "test"])
assert NameStyleBproto.fromStr("this-Is_ATest") == ComponentName(["this", "is", "a", "test"])
assert NameStyleBproto.fromStr("ThisIs-A_test") == ComponentName(["this", "is", "a", "test"])
def test_ComponentNameStyleBproto_context_enum():
assert NameStyleBproto.fromStr("test", "enum") == ComponentName(["test"])
assert NameStyleBproto.fromStr("ThisIsATest", "enum") == ComponentName(["thisisatest"])
assert NameStyleBproto.fromStr("TESTTEST", "enum") == ComponentName(["testtest"])
assert NameStyleBproto.fromStr("TEST_TEST", "enum") == ComponentName(["test", "test"])
def test_ComponentNameStyleBproto_comparison():
c = NameStyleBproto.fromStr("ThisIsATest")
assert c == "this-is-a-test"
assert c == "this_is_a_test"
assert c == "thisIsATest"
assert c == "ThisIsATest"
assert c == "This_Is_A_Test"
def test_ComponentNameStyleBproto_from_Component():
c = ComponentName(["this", "is", "a", "test"])
c2 = NameStyleBproto.fromStr(c)
assert c == c2

View File

@@ -0,0 +1,40 @@
import sys
sys.path.append('src/')
from nameHandling.brand_applier import BrandApplier
from protocol_components.message import Message
from nameHandling.base import ComponentName
def test_name_applier():
protocolNameApplier = BrandApplier("brand")
name = ComponentName(["test"])
name = protocolNameApplier.apply(name)
assert name == ComponentName(["brand", "test"])
name = ComponentName(["test", "test"])
name = protocolNameApplier.apply(name)
assert name == ComponentName(["brand", "test", "test"])
msg = Message(ComponentName(["this", "is", "a", "test"]), 0)
msg: Message = protocolNameApplier.apply(msg)
assert msg.name == ComponentName(["brand", "this", "is", "a", "test"])
protocolNameApplier = BrandApplier(ComponentName(["happy", "brand"]))
msg = Message(ComponentName(["this", "is", "a", "test"]), 0)
msg: Message = protocolNameApplier.apply(msg)
assert msg.name == ComponentName(["happy", "brand", "this", "is", "a", "test"])
try:
protocolNameApplier.apply(None)
assert False
except Exception as e:
assert isinstance(e, TypeError)
try:
protocolNameApplier = BrandApplier(None)
assert False
except Exception as e:
assert isinstance(e, TypeError)

View File

@@ -0,0 +1,73 @@
import sys
sys.path.append('src/')
from nameHandling.base import ComponentName
from protocol_components.protocolFactory import FactoryProtocolDefition
from protocol_components.enumeration import FactoryEnumeration, Enumeration
from protocol_components.bitfields import FactoryBitfield, Bitfield
from protocol_components.dtypes import BprotoFieldBaseType
from protocol_components.field import FactoryField
from protocol_components.message import FactoryMessage, Message
from nameHandling.style.cNameStyle import NameStyleC
def test_style_c_fromStr():
try:
NameStyleC.fromStr("")
assert False
except Exception as e:
assert isinstance(e, NotImplementedError)
def test_style_c_toStr_enum():
assert NameStyleC.toStr(ComponentName(["my", "custom", "Values"]), "enum_name") == "MyCustomValues"
assert NameStyleC.toStr(ComponentName(["my", "custom", "enum", "value"]), "enum_item") == "MY_CUSTOM_ENUM_VALUE"
assert NameStyleC.toStr(ComponentName(["my", "custom", "class"]), "struct_name") == "MyCustomClass"
assert NameStyleC.toStr(ComponentName(["my", "custom", "class", "member"]), "struct_member") == "myCustomClassMember"
assert NameStyleC.toStr(ComponentName(["my", "custom", "class"]), "bitfield_name") == "MyCustomClass"
assert NameStyleC.toStr(ComponentName(["my", "custom", "class", "member"]), "bitfield_member") == "myCustomClassMember"
def test_style_c_preprocessor():
protocol_factory = FactoryProtocolDefition()
enum_factory = FactoryEnumeration()
enum_factory.add_value("first-Value", 1)
enum_factory.add_value("second-Important-Value", 2)
protocol_factory.add_enum(enum_factory.assemble("mode"))
bitfield_factory = FactoryBitfield()
bitfield_factory.add_bit("FirstBit", 0)
bitfield_factory.add_bit("SecondBit", 1)
protocol_factory.add_bitfield(bitfield_factory.assemble("bitfield1"))
message_factory = FactoryMessage()
message_factory.add_field(FactoryField().assemble("x", 1, BprotoFieldBaseType.FLOAT32, 2, None))
message_factory.add_field(FactoryField().assemble("y", 2, BprotoFieldBaseType.FLOAT32, 2, None))
protocol_factory.add_message(message_factory.assemble("cordUpdate", 1))
protocol = protocol_factory.assemble("ABCP", 5)
protocol = NameStyleC.preprocess(protocol)
enum: Enumeration = list(protocol.enums.values())[0]
bitfield: Bitfield = list(protocol.bitfields.values())[0]
message: Message = list(protocol.messages.values())[0]
assert isinstance(enum, Enumeration)
assert isinstance(bitfield, Bitfield)
assert isinstance(message, Message)
assert NameStyleC.toStr(enum.name, "enum_name") == "Abcp_enum_Mode"
assert NameStyleC.toStr(bitfield.name, "bitfield_name") == "Abcp_bitfield_Bitfield1"
assert NameStyleC.toStr(message.name, "struct_name") == "Abcp_message_CordUpdate"
assert enum.values == {
ComponentName(["abcp", "_enum_", "mode", "value", "first", "value"]): 1,
ComponentName(["abcp", "_enum_", "mode", "value", "second", "important", "value"]): 2
}
assert bitfield.bits == {
ComponentName(["first", "bit"]): 0,
ComponentName(["second", "bit"]): 1
}

View File

@@ -0,0 +1,59 @@
import sys
sys.path.append('src/')
from nameHandling.base import ComponentName
from protocol_components.protocolFactory import FactoryProtocolDefition
from protocol_components.enumeration import FactoryEnumeration, Enumeration
from protocol_components.bitfields import FactoryBitfield, Bitfield
from protocol_components.dtypes import BprotoFieldBaseType
from protocol_components.field import FactoryField
from protocol_components.message import FactoryMessage, Message
from nameHandling.style.pythonNameStyle import NameStylePython
def test_style_python_fromStr():
try:
NameStylePython.fromStr("")
assert False
except Exception as e:
assert isinstance(e, NotImplementedError)
def test_style_python_toStr_enum():
assert NameStylePython.toStr(ComponentName(["my", "custom", "enum"]), "enum_name") == "MyCustomEnum"
assert NameStylePython.toStr(ComponentName(["my", "custom", "enum", "value"]), "enum_item") == "MY_CUSTOM_ENUM_VALUE"
assert NameStylePython.toStr(ComponentName(["my", "custom", "class"]), "class_name") == "MyCustomClass"
assert NameStylePython.toStr(ComponentName(["my", "custom", "class", "member"]), "class_member") == "my_custom_class_member"
def test_style_python_preprocessor():
protocol_factory = FactoryProtocolDefition()
enum_factory = FactoryEnumeration()
enum_factory.add_value("first-Value", 1)
enum_factory.add_value("second-Important-Value", 2)
protocol_factory.add_enum(enum_factory.assemble("mode"))
bitfield_factory = FactoryBitfield()
bitfield_factory.add_bit("FirstBit", 0)
bitfield_factory.add_bit("SecondBit", 1)
protocol_factory.add_bitfield(bitfield_factory.assemble("bitfield1"))
message_factory = FactoryMessage()
message_factory.add_field(FactoryField().assemble("x", 1, BprotoFieldBaseType.FLOAT32, 2, None))
message_factory.add_field(FactoryField().assemble("y", 2, BprotoFieldBaseType.FLOAT32, 2, None))
protocol_factory.add_message(message_factory.assemble("cordUpdate", 1))
protocol = protocol_factory.assemble("HCPT", 5)
protocol = NameStylePython.preprocess(protocol)
enum: Enumeration = list(protocol.enums.values())[0]
bitfield: Bitfield = list(protocol.bitfields.values())[0]
message: Message = list(protocol.messages.values())[0]
assert isinstance(enum, Enumeration)
assert isinstance(bitfield, Bitfield)
assert NameStylePython.toStr(enum.name, "enum_name") == "HcptEnumMode"
assert NameStylePython.toStr(bitfield.name, "class_name") == "HcptBitfieldBitfield1"
assert NameStylePython.toStr(message.name, "class_name") == "HcptMessageCordUpdate"

View File

@@ -0,0 +1,80 @@
import sys
sys.path.append('src/')
from nameHandling.base import ComponentName
from protocol_components.bitfields import FactoryBitfield, Bitfield
from errors import BprotoDuplicateNameError, BprotoDuplicateBitfieldPositionError, BprotoCompilerError
from nameHandling.base import NameStyleBproto
def test_bitfield_factory():
fatory = FactoryBitfield()
fatory.add_bit("BIT #1", 0)
fatory.add_bit("BIT #4", None)
fatory.add_bit("BIT #2", 1)
fatory.add_bit("BIT #5", 4)
fatory.add_bit("BIT #6", None)
fatory.add_bit("BIT #3", 2)
fatory.add_bit("BIT #7", 6)
bitfield: Bitfield = fatory.assemble("TestBitfield")
assert bitfield.name == "TestBitfield"
assert isinstance(bitfield.name, ComponentName)
assert isinstance(bitfield.get_name(), ComponentName)
assert isinstance(bitfield.get_identifier(), ComponentName)
assert bitfield.bits == {
NameStyleBproto.fromStr("BIT #1"): 0,
NameStyleBproto.fromStr("BIT #2"): 1,
NameStyleBproto.fromStr("BIT #3"): 2,
NameStyleBproto.fromStr("BIT #4"): 3,
NameStyleBproto.fromStr("BIT #5"): 4,
NameStyleBproto.fromStr("BIT #6"): 5,
NameStyleBproto.fromStr("BIT #7"): 6,
}
assert isinstance(list(bitfield.bits.keys())[0], ComponentName)
def test_bitfield_factory_failure_cases_duplicate_name():
factory = FactoryBitfield()
factory.add_bit("BIT #1", 0)
factory.add_bit("BIT #1", 1)
factory.add_bit("BIT #2", 2)
try:
factory.assemble("TestBitfield")
assert False
except BprotoCompilerError as e:
assert isinstance(e, BprotoDuplicateNameError)
def test_bitfield_factory_failure_cases_duplicate_position():
factory = FactoryBitfield()
factory.add_bit("BIT #1", 0)
factory.add_bit("BIT #2", 1)
factory.add_bit("BIT #3", 1)
try:
factory.assemble("TestBitfield")
assert False
except BprotoCompilerError as e:
assert isinstance(e, BprotoDuplicateBitfieldPositionError)
def test_bitfield_size_calculation():
bitfield = Bitfield("TestBitfield")
bitfield.length = 7
assert bitfield.get_size_bytes() == 1
bitfield.length = 0
assert bitfield.get_size_bytes() == 1
bitfield.length = 8
assert bitfield.get_size_bytes() == 1
bitfield.length = 9
assert bitfield.get_size_bytes() == 2

View File

@@ -0,0 +1,73 @@
from nameHandling.base import ComponentName, NameStyleBproto
from nameHandling.style.pythonNameStyle import NameStylePython
from protocol_components.enumeration import FactoryEnumeration
from copy import deepcopy
def test_deepcopy_component_name():
name = ComponentName(["This", "Is", "Some", "text"], NameStylePython, source_string="ThisIsSomeText")
new_name = deepcopy(name)
name.name_parts = ["Some", "Other", "Stuff"]
name.source_string = "SomeOtherStuff"
name.source_style = NameStyleBproto
# Verify Values did not change
assert name.name_parts != new_name.name_parts
assert name.source_string != new_name.source_string
assert name.source_style != new_name.source_style
def test_deepcopy_enum():
old_enum_factory = FactoryEnumeration()
old_enum_factory.add_value("Value1", 1)
old_enum_factory.add_value("Value2", 2)
old_enum_factory.add_value("Value3", 3)
old_enum = old_enum_factory.assemble("OldEnum")
new_enum = deepcopy(old_enum)
# Check ids
assert id(new_enum) != id(old_enum)
assert id(new_enum.name) != id(old_enum.name)
assert id(old_enum.values) != id(new_enum.values)
old_keys = list(old_enum.values.keys())
new_keys = list(new_enum.values.keys())
assert id(old_keys[0]) != id(new_keys[0])
assert id(old_keys[1]) != id(new_keys[1])
assert id(old_keys[2]) != id(new_keys[2])
def test_deepcopy_component_names_memo():
name1 = NameStyleBproto.fromStr("Value1")
name2 = NameStyleBproto.fromStr("Value2")
assert id(name1) != id(name2)
name1_copy = deepcopy(name1)
assert isinstance(name1_copy, ComponentName)
assert name1_copy == name1
assert id(name1_copy) != id(name1)
name1_copy2 = deepcopy(name1, {
id(name1): name1_copy,
id(name1_copy): name1_copy
})
name1_copy3 = deepcopy(name1_copy, {
id(name1): name1_copy,
id(name1_copy): name1_copy
})
assert isinstance(name1_copy2, ComponentName)
assert isinstance(name1_copy3, ComponentName)
assert id(name1_copy) == id(name1_copy2)
assert id(name1_copy2) != id(name1)
assert id(name1_copy) == id(name1_copy3)
assert id(name1_copy3) != id(name1)

228
test/compiler/test_emums.py Normal file
View File

@@ -0,0 +1,228 @@
import sys
sys.path.append('src/')
from protocol_components.dtypes import BprotoFieldBaseType
from protocol_components.enumeration import FactoryEnumeration, Enumeration
from errors import BprotoDuplicateNameError, BprotoDuplicateEnumValueError, BprotoCompilerError, BprotoEnumBitsizeTooLargeError
from nameHandling.base import NameStyleBproto, ComponentName
def test_enum_factory():
factory = FactoryEnumeration()
factory.add_value("X", 0)
factory.add_value("A", 1)
factory.add_value("C", 3)
factory.add_value("B", 2)
factory.add_value("D", None)
factory.add_value("E", 5)
factory.add_value("F", None)
enum = factory.assemble("TestEnum")
assert enum.name == "TestEnum"
assert enum.values == {
NameStyleBproto.fromStr("X"): 0,
NameStyleBproto.fromStr("A"): 1,
NameStyleBproto.fromStr("B"): 2,
NameStyleBproto.fromStr("C"): 3,
NameStyleBproto.fromStr("D"): 4,
NameStyleBproto.fromStr("E"): 5,
NameStyleBproto.fromStr("F"): 6
}
assert isinstance(enum.name, ComponentName)
assert isinstance(enum.get_name(), ComponentName)
assert isinstance(enum.get_identifier(), ComponentName)
assert isinstance(list(enum.values.keys())[0], ComponentName)
def test_enum_factory_failure_cases_dubplicate_value():
factory = FactoryEnumeration()
factory.add_value("X", 0)
factory.add_value("Y", 0)
factory.add_value("Z", 1)
try:
factory.assemble("TestEnum")
assert False
except BprotoCompilerError as e:
assert isinstance(e, BprotoDuplicateEnumValueError)
def test_enum_factory_failure_cases_duplicate_name():
factory = FactoryEnumeration()
factory.add_value("X", 0)
factory.add_value("Y", 1)
factory.add_value("Y", 2)
try:
factory.assemble("TestEnum")
assert False
except BprotoCompilerError as e:
assert isinstance(e, BprotoDuplicateNameError)
def test_enum_factory_failure_cases_duplicate_name_and_value():
factory = FactoryEnumeration()
factory.add_value("X", 0)
factory.add_value("X", 0)
try:
factory.assemble("TestEnum")
assert False
except BprotoCompilerError as e:
assert isinstance(e, BprotoDuplicateNameError) or isinstance(e, BprotoDuplicateEnumValueError)
def test_enum_sorted():
factory = FactoryEnumeration()
factory.add_value("X", 0)
factory.add_value("D", None)
factory.add_value("C", 3)
factory.add_value("E", 5)
factory.add_value("F", None)
factory.add_value("B", 2)
factory.add_value("A", 1)
enum = factory.assemble("TestEnum")
assert list(enum.values.values()) == list(range(7))
def test_enum_size_calc():
assert FactoryEnumeration.calculate_bitsize(list(range(17))) == 5
assert FactoryEnumeration.calculate_bitsize(list(range(16))) == 4
assert FactoryEnumeration.calculate_bitsize(list(range(9))) == 4
assert FactoryEnumeration.calculate_bitsize(list(range(8))) == 3
assert FactoryEnumeration.calculate_bitsize(list(range(7))) == 3
assert FactoryEnumeration.calculate_bitsize([1, 2, 31]) == 5
assert FactoryEnumeration.calculate_bitsize([1, 32, 2]) == 6
assert FactoryEnumeration.calculate_bitsize([127, 0, 2]) == 7
assert FactoryEnumeration.calculate_bitsize([]) == 1
def test_enum_auxiliary_data_typemap():
e = Enumeration("TestEnum", 8)
assert e.map_auxiliary_datatypes() == BprotoFieldBaseType.UINT8
e = Enumeration("TestEnum", 16)
assert e.map_auxiliary_datatypes() == BprotoFieldBaseType.UINT16
e = Enumeration("TestEnum", 24)
assert e.map_auxiliary_datatypes() == BprotoFieldBaseType.UINT32
e = Enumeration("TestEnum", 32)
assert e.map_auxiliary_datatypes() == BprotoFieldBaseType.UINT32
e = Enumeration("TestEnum", 64)
assert e.map_auxiliary_datatypes() == BprotoFieldBaseType.UINT64
e = Enumeration("TestEnum", 65)
try:
e.map_auxiliary_datatypes()
assert False
except Exception as err:
assert isinstance(err, BprotoEnumBitsizeTooLargeError)
def test_enum_size_calculation():
# Uint8
factory = FactoryEnumeration()
for i in range(3):
factory.add_value(f"#{i}", i)
e = factory.assemble("TestEnum")
assert e.get_size_bits() == 8
assert e.get_size_bytes() == 1
factory = FactoryEnumeration()
for i in range(2**8):
factory.add_value(f"#{i}", i)
e = factory.assemble("TestEnum")
assert e.get_size_bits() == 8
assert e.get_size_bytes() == 1
# Uint16
factory = FactoryEnumeration()
for i in range(2**8 + 1):
factory.add_value(f"#{i}", i)
e = factory.assemble("TestEnum")
assert e.get_size_bits() == 16
assert e.get_size_bytes() == 2
factory = FactoryEnumeration()
for i in range(2**16):
factory.add_value(f"#{i}", i)
e = factory.assemble("TestEnum")
assert e.get_size_bits() == 16
assert e.get_size_bytes() == 2
# Uint32
factory = FactoryEnumeration()
for i in range(17):
factory.add_value(f"#{i}", i)
factory.add_value(f"#{2**16}", 2**16)
# We start couinting from 0 -> 2**16 does not fit in 16 bits
e = factory.assemble("TestEnum")
assert e.get_size_bits() == 32
assert e.get_size_bytes() == 4
factory = FactoryEnumeration()
for i in range(17):
factory.add_value(f"#{i}", i)
factory.add_value(f"#{2**32 - 1}", 2**32 - 1)
# We start couinting from 0 2**32 - 1 does barely fit in 32 bits
e = factory.assemble("TestEnum")
assert e.get_size_bits() == 32
assert e.get_size_bytes() == 4
# Uint64
factory = FactoryEnumeration()
for i in range(17):
factory.add_value(f"#{i}", i)
factory.add_value(f"#{2**32}", 2**32)
# We start couinting from 0 -> 2**32 does not fit in 32 bits
e = factory.assemble("TestEnum")
assert e.get_size_bits() == 64
assert e.get_size_bytes() == 8
factory = FactoryEnumeration()
for i in range(17):
factory.add_value(f"#{i}", i)
factory.add_value(f"#{2**64 - 1}", 2**64 - 1)
# We start couinting from 0 2**64 - 1 does barely fit in 64 bits
e = factory.assemble("TestEnum")
assert e.get_size_bits() == 64
assert e.get_size_bytes() == 8
# Too large
factory = FactoryEnumeration()
for i in range(17):
factory.add_value(f"#{i}", i)
factory.add_value(f"#{2**64}", 2**64)
# This can cause rounding errors
# log2(2**64) ~ log2(2**64 - 1) ~ 64
# This is btw not a problem for 2**32
# stupid floating point errors
# We start couinting from 0 -> 2**64 does not fit in 64 bits
try:
e = factory.assemble("TestEnum")
assert False
except Exception as err:
assert isinstance(err, BprotoEnumBitsizeTooLargeError)

View File

@@ -0,0 +1,98 @@
import sys
sys.path.append('src/')
from protocol_components.dtypes import BprotoFieldBaseType, BprotoArrayPolicyViolationError, BprotoUnknownDataTypeError
from protocol_components.field import Field, FactoryField, FieldBitfield, FieldBitfieldRef
from protocol_components.bitfields import Bitfield
from nameHandling.base import ComponentName
def test_field_object():
field = Field("field1", 3, BprotoFieldBaseType.UINT64, 4)
assert field.name == "field1"
assert field.pos == 3
assert field.type == BprotoFieldBaseType.UINT64
assert field.array_size == 4
assert field.get_identifier() == "field1"
assert field.get_name() == "field1"
assert field.get_type_name() == "field"
assert field.get_size_bytes() == 8 * 4
assert field.get_size_bits() == 64 * 4
def test_field_factory():
factory = FactoryField()
field: Field = factory.assemble("field1", 3, BprotoFieldBaseType.UINT64.value, 4, None)
assert isinstance(field, Field)
assert field.name == "field1"
assert field.pos == 3
assert field.type == BprotoFieldBaseType.UINT64
assert field.array_size == 4
assert isinstance(field.name, ComponentName)
assert isinstance(field.get_identifier(), ComponentName)
assert isinstance(field.get_name(), ComponentName)
assert isinstance(field.get_type_name(), str)
def test_field_factory_errors_invalid_datatype():
factory = FactoryField()
try:
factory.assemble(
"field1", 3, "junkDataType", 4, None
)
assert False
except BprotoUnknownDataTypeError:
assert True
except Exception as e:
assert False, f"Unexpected exception: {e}"
def test_field_factory_errors_invalid_array_policy():
factory = FactoryField()
try:
factory.assemble(
"field1", 1, BprotoFieldBaseType.BITFIELD, 4, None
)
assert False
except BprotoArrayPolicyViolationError:
assert True
except Exception as e:
assert False, f"Unexpected exception: {e}"
def test_field_factory_bitfield():
factory = FactoryField()
field: FieldBitfield = factory.assemble(
"field1", 3, BprotoFieldBaseType.BITFIELD, 1,
Bitfield("test")
)
assert isinstance(field, FieldBitfield)
assert field.name == "field1"
assert field.pos == 3
assert field.type == BprotoFieldBaseType.BITFIELD
assert field.array_size == 1
assert isinstance(field.bitfield, Bitfield)
assert field.bitfield.get_name() == "test"
def test_field_factory_bitfield_ref():
factory = FactoryField()
field: FieldBitfield = factory.assemble(
"field1", 3, BprotoFieldBaseType.BITFIELD, 1,
"test"
)
assert isinstance(field, FieldBitfieldRef)
assert field.name == "field1"
assert field.pos == 3
assert field.type == BprotoFieldBaseType.BITFIELD
assert field.array_size == 1
assert field.ref == "test"

View File

@@ -0,0 +1,71 @@
import sys
sys.path.append('src/')
from nameHandling.base import ComponentName, NameStyleBproto
from protocol_components.message import FactoryMessage, Message
from protocol_components.field import FactoryField
from errors import BprotoDuplicateNameError, BprotoMessageIDAlreadyUsed
def test_message_factory():
factory = FactoryMessage()
field1 = FactoryField().assemble("field1", 0, "uint64", 4, None)
field2 = FactoryField().assemble("field2", 1, "uint64", 4, None)
field3 = FactoryField().assemble("field3", 2, "uint64", 4, None)
field4 = FactoryField().assemble("field4", 3, "uint64", 4, None)
factory.add_field(field3)
factory.add_field(field2)
factory.add_field(field4)
factory.add_field(field1)
message: Message = factory.assemble("TestMessage", 42)
assert message.name == "TestMessage"
assert isinstance(message.name, ComponentName)
assert isinstance(message.get_name(), ComponentName)
assert message.message_index_number == 42
assert message.get_identifier() == 42
assert message.fields == {
NameStyleBproto.fromStr("field1"): field1,
NameStyleBproto.fromStr("field2"): field2,
NameStyleBproto.fromStr("field3"): field3,
NameStyleBproto.fromStr("field4"): field4
}
assert isinstance(list(message.fields.keys())[0], ComponentName)
assert message.get_size_bytes() == 8 * 4 * 4
assert message.get_size_bits() == 64 * 4 * 4
def test_message_factory_error_duplicate_name():
field1 = FactoryField().assemble("field1", 0, "uint64", 4, None)
field2 = FactoryField().assemble("field1", 1, "uint64", 4, None)
factory = FactoryMessage()
factory.add_field(field1)
factory.add_field(field2)
try:
factory.assemble("TestMessage", 42)
assert False
except Exception as e:
assert isinstance(e, BprotoDuplicateNameError)
def test_message_factory_error_duplicate_index():
field1 = FactoryField().assemble("field1", 0, "uint64", 4, None)
field2 = FactoryField().assemble("field2", 0, "uint64", 4, None)
factory = FactoryMessage()
factory.add_field(field1)
factory.add_field(field2)
try:
factory.assemble("TestMessage", 42)
assert False
except Exception as e:
assert isinstance(e, BprotoMessageIDAlreadyUsed)

View File

@@ -0,0 +1,181 @@
import sys
sys.path.append('src/')
from protocol_components.dtypes import BprotoFieldBaseType
from nameHandling.base import ComponentName, NameStyleBproto
from protocol_components.message import FactoryMessage
from protocol_components.enumeration import FactoryEnumeration, Enumeration
from protocol_components.bitfields import FactoryBitfield, Bitfield
from protocol_components.protocolFactory import FactoryProtocolDefition
from protocol_components.field import FactoryField, Field, FieldEnum, FieldBitfield, FieldEnumRef, FieldBitfieldRef
from errors import BprotoUnresolvedReferenceError, BprotoAlreadyDefinedError
from nameHandling.resolver import ProtocolReferenceResolver
def test_protocol_factory():
factory = FactoryProtocolDefition()
factory.add_message(FactoryMessage().assemble("Message1", 42))
factory.add_message(FactoryMessage().assemble("Message2", 43))
message_with_fields_factory = FactoryMessage()
message_with_fields_factory.add_field(FactoryField().assemble("field1", 0, "uint64", 1, None))
message_with_fields_factory.add_field(FactoryField().assemble("enum_ref", 1, BprotoFieldBaseType.ENUM, 1, "Enum1"))
message_with_fields_factory.add_field(FactoryField().assemble("bitfield_ref", 2, BprotoFieldBaseType.BITFIELD, 1, "Bitfield2"))
factory.add_message(
message_with_fields_factory.assemble("Message3", 44)
)
factory.add_enum(FactoryEnumeration().assemble("Enum1"))
factory.add_enum(FactoryEnumeration().assemble("Enum2"))
factory.add_enum(FactoryEnumeration().assemble("Enum3"))
factory.add_bitfield(FactoryBitfield().assemble("Bitfield1"))
factory.add_bitfield(FactoryBitfield().assemble("Bitfield2"))
factory.add_bitfield(FactoryBitfield().assemble("Bitfield3"))
protocol = factory.assemble("TESTPROTOCOL", 1)
assert protocol.name.name_parts == ["testprotocol"]
assert isinstance(protocol.name, ComponentName)
assert protocol.version == 1
assert protocol.messages == {
NameStyleBproto.fromStr("Message1"): factory.messages.get(NameStyleBproto.fromStr("Message1")),
NameStyleBproto.fromStr("Message2"): factory.messages.get(NameStyleBproto.fromStr("Message2")),
NameStyleBproto.fromStr("Message3"): factory.messages.get(NameStyleBproto.fromStr("Message3"))
}
assert protocol.enums == {
NameStyleBproto.fromStr("Enum1"): factory.enums.get(NameStyleBproto.fromStr("Enum1")),
NameStyleBproto.fromStr("Enum2"): factory.enums.get(NameStyleBproto.fromStr("Enum2")),
NameStyleBproto.fromStr("Enum3"): factory.enums.get(NameStyleBproto.fromStr("Enum3"))
}
assert protocol.bitfields == {
NameStyleBproto.fromStr("Bitfield1"): factory.bitfields.get(NameStyleBproto.fromStr("Bitfield1")),
NameStyleBproto.fromStr("Bitfield2"): factory.bitfields.get(NameStyleBproto.fromStr("Bitfield2")),
NameStyleBproto.fromStr("Bitfield3"): factory.bitfields.get(NameStyleBproto.fromStr("Bitfield3"))
}
# Check if references are resolved works
assert isinstance(protocol.messages.get(NameStyleBproto.fromStr("Message3")).fields.get(NameStyleBproto.fromStr("enum_ref")), FieldEnum)
assert isinstance(protocol.messages.get(NameStyleBproto.fromStr("Message3")).fields.get(NameStyleBproto.fromStr("bitfield_ref")), FieldBitfield)
def test_protocol_resolve_refernces():
enum1 = FactoryEnumeration().assemble("Enum1")
bitfield1 = FactoryBitfield().assemble("Bitfield1")
message1Factory = FactoryMessage()
message1Factory.add_field(FactoryField().assemble("field1", 0, "uint64", 1, None))
message1Factory.add_field(FactoryField().assemble("enum_field", 1, BprotoFieldBaseType.ENUM, 1, enum1))
message1Factory.add_field(FactoryField().assemble("enum_ref_field", 2, BprotoFieldBaseType.ENUM, 1, "Enum1"))
message1Factory.add_field(FactoryField().assemble("bitfield_field", 3, BprotoFieldBaseType.BITFIELD, 1, bitfield1))
message1Factory.add_field(FactoryField().assemble("bitfield_ref_field", 4, BprotoFieldBaseType.BITFIELD, 1, "Bitfield1"))
message1 = message1Factory.assemble("Message1", 42)
# Check if message is correctly assembled
assert isinstance(message1.fields.get(NameStyleBproto.fromStr("field1")), Field)
assert message1.fields.get(NameStyleBproto.fromStr("field1")).type == BprotoFieldBaseType.UINT64
assert message1.fields.get(NameStyleBproto.fromStr("field1")).array_size == 1
assert message1.fields.get(NameStyleBproto.fromStr("field1")).ref is None
assert isinstance(message1.fields.get(NameStyleBproto.fromStr("field1")).ref, type(None))
assert isinstance(message1.fields.get(NameStyleBproto.fromStr("enum_field")), FieldEnum)
assert message1.fields.get(NameStyleBproto.fromStr("enum_field")).type == BprotoFieldBaseType.ENUM
assert isinstance(message1.fields.get(NameStyleBproto.fromStr("enum_field")).enum, Enumeration)
assert message1.fields.get(NameStyleBproto.fromStr("enum_field")).enum == enum1
assert isinstance(message1.fields.get(NameStyleBproto.fromStr("enum_ref_field")), FieldEnumRef)
assert message1.fields.get(NameStyleBproto.fromStr("enum_ref_field")).type == BprotoFieldBaseType.ENUM
assert message1.fields.get(NameStyleBproto.fromStr("enum_ref_field")).ref == "Enum1"
assert isinstance(message1.fields.get(NameStyleBproto.fromStr("bitfield_field")), FieldBitfield)
assert message1.fields.get(NameStyleBproto.fromStr("bitfield_field")).type == BprotoFieldBaseType.BITFIELD
assert isinstance(message1.fields.get(NameStyleBproto.fromStr("bitfield_field")).bitfield, Bitfield)
assert message1.fields.get(NameStyleBproto.fromStr("bitfield_field")).bitfield == bitfield1
assert isinstance(message1.fields.get(NameStyleBproto.fromStr("bitfield_ref_field")), FieldBitfieldRef)
assert message1.fields.get(NameStyleBproto.fromStr("bitfield_ref_field")).type == BprotoFieldBaseType.BITFIELD
assert message1.fields.get(NameStyleBproto.fromStr("bitfield_ref_field")).ref == "Bitfield1"
factory = FactoryProtocolDefition()
factory.add_message(message1)
factory.add_enum(enum1)
factory.add_bitfield(bitfield1)
protocol = factory.assemble("TestProtocol", 1)
# Check if references are resolved works
resolver = ProtocolReferenceResolver(protocol)
resolver.resolve_refrence_protocol()
assert protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("enum_field")).type == BprotoFieldBaseType.ENUM
assert isinstance(protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("enum_field")), FieldEnum)
assert protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("enum_field")).enum == enum1
assert protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("enum_ref_field")).type == BprotoFieldBaseType.ENUM
assert isinstance(protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("enum_ref_field")), FieldEnum)
assert protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("enum_ref_field")).enum == enum1
assert protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("bitfield_field")).type == BprotoFieldBaseType.BITFIELD
assert isinstance(protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("bitfield_field")), FieldBitfield)
assert protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("bitfield_field")).bitfield == bitfield1
assert protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("bitfield_ref_field")).type == BprotoFieldBaseType.BITFIELD
assert isinstance(protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("bitfield_ref_field")), FieldBitfield)
assert protocol.messages.get(NameStyleBproto.fromStr("Message1")).fields.get(NameStyleBproto.fromStr("bitfield_ref_field")).bitfield == bitfield1
def test_protocol_resolve_refernces_unknown_refernces():
message1Factory = FactoryMessage()
message1Factory.add_field(FactoryField().assemble("field1", 0, "uint64", 1, None))
message1Factory.add_field(FactoryField().assemble("enum_ref_field", 1, BprotoFieldBaseType.ENUM, 1, "Enum1"))
message1Factory.add_field(FactoryField().assemble("bitfield_ref_field", 2, BprotoFieldBaseType.BITFIELD, 1, "Bitfield1",))
message1 = message1Factory.assemble("Message1", 42)
factory = FactoryProtocolDefition()
factory.add_message(message1)
try:
factory.assemble("TestProtocol", 1)
assert False
except Exception as e:
assert isinstance(e, BprotoUnresolvedReferenceError)
try:
factory.assemble("TestProtocol", 1)
assert False
except Exception as e:
assert isinstance(e, BprotoUnresolvedReferenceError)
def test_protocol_factory_already_define_error():
factory = FactoryProtocolDefition()
factory.add_message(FactoryMessage().assemble("Message1", 42))
try:
factory.add_message(FactoryMessage().assemble("Message1", 42))
factory.assemble("A", 1)
assert False
except Exception as e:
assert isinstance(e, BprotoAlreadyDefinedError)
factory = FactoryProtocolDefition()
factory.add_enum(FactoryEnumeration().assemble("Enum1"))
try:
factory.assemble("A", 1)
factory.add_enum(FactoryEnumeration().assemble("Enum1"))
assert False
except Exception as e:
assert isinstance(e, BprotoAlreadyDefinedError)
factory = FactoryProtocolDefition()
factory.add_bitfield(FactoryBitfield().assemble("Bitfield1"))
try:
factory.add_bitfield(FactoryBitfield().assemble("Bitfield1"))
factory.assemble("A", 1)
assert False
except Exception as e:
assert isinstance(e, BprotoAlreadyDefinedError)

View File

@@ -0,0 +1 @@
test1

View File

@@ -0,0 +1 @@
test2

View File

@@ -0,0 +1 @@
Hello {{ name }}!

View File

@@ -0,0 +1,48 @@
cmake_minimum_required(VERSION 3.25)
project(bproto_test)
enable_testing()
include("../../backend_output/c/HCP.cmake")
# include CTest support
include(CTest)
set(CMAKE_BUILD_TYPE "Debug")
# Adding Unity test framework
add_library(unity STATIC Unity/src/unity.c)
target_include_directories(unity PUBLIC Unity/src)
# Normal test target
add_executable(test_runner
src/main.c
src/test_messages.c
)
target_link_libraries(test_runner PUBLIC
bproto
unity
gcov
)
target_include_directories(test_runner PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src
)
target_compile_options(test_runner PRIVATE -fprofile-arcs -ftest-coverage -fPIC -O0 --coverage)
target_compile_options(bproto PRIVATE -fprofile-arcs -ftest-coverage -fPIC -O0 --coverage)
target_link_options(test_runner PRIVATE -fprofile-arcs -ftest-coverage -fPIC --coverage)
target_link_options(bproto PRIVATE -fprofile-arcs -ftest-coverage -fPIC --coverage)
# Python Integration test build
add_executable(test_integration_python
src/test_python_integration.c
)
target_link_libraries(test_integration_python PUBLIC
bproto
gcov
)
target_include_directories(test_integration_python PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src
)

17
test/output/c/src/main.c Normal file
View File

@@ -0,0 +1,17 @@
#include "unity.h"
#include "suit.h"
void setUp(void) {};
void tearDown(void) {};
int main() {
UNITY_BEGIN();
RUN_TEST(test_encdec_generic_Error);
RUN_TEST(test_encdec_generic_MotionUpdate);
RUN_TEST(test_encdec_generic_Error_invalid_id);
RUN_TEST(test_encdec_generic_Error_invalid_size);
RUN_TEST(test_encdec_generic_Error_large_size);
RUN_TEST(test_encdec_generic_Error_invalid_crc);
return UNITY_END();
}

9
test/output/c/src/suit.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
// Enc/Dec with generic data buffer
void test_encdec_generic_MotionUpdate();
void test_encdec_generic_Error();
void test_encdec_generic_Error_invalid_id();
void test_encdec_generic_Error_invalid_size();
void test_encdec_generic_Error_large_size();
void test_encdec_generic_Error_invalid_crc();

View File

@@ -0,0 +1,224 @@
#include "suit.h"
#include <stdint.h>
#include "unity.h"
#include "HCP_message.h"
void test_encdec_generic_Error() {
uint8_t buffer_msg_data[MAX_MESSAGE_DATA_SIZE];
Hcp_message_MotionUpdate msg = {
.speed = {3, 6, 9},
.stearing = 0.5,
.name = "This is a test",
.heading = 'B',
.enable = {true, false, true, false},
.enables = {
.a = false, .b = true, .c = false
},
.dangerLvl = HCP_ENUM_INLINE_MOTION_UPDATE_DANGER_LVL_VALUE_MELTDOWN
};
size_t length = toBytes_Hcp_message_MotionUpdate(&msg, buffer_msg_data);
TEST_ASSERT_EQUAL(length, 58);
TEST_ASSERT_EQUAL(buffer_msg_data[0], 0x01);
Hcp_message_MotionUpdate rx_msg;
size_t parsed_length = 0;
HCP_MSG_ID msg_id = 0xff;
HCP_PARSER_RESULT result = fromBytes(&rx_msg, buffer_msg_data, length, &parsed_length, &msg_id);
TEST_ASSERT_EQUAL(result, HCP_PARSER_RESULT_OK);
TEST_ASSERT_EQUAL(parsed_length, 58);
TEST_ASSERT_EQUAL(msg_id, HCP_MESSAGE_MOTION_UPDATE_ID);
TEST_ASSERT_EQUAL(msg.stearing, rx_msg.stearing);
TEST_ASSERT_EQUAL_STRING(msg.name, rx_msg.name);
TEST_ASSERT_EQUAL(msg.heading, rx_msg.heading);
TEST_ASSERT_EQUAL(msg.dangerLvl, rx_msg.dangerLvl);
TEST_ASSERT_EQUAL(msg.speed[0], rx_msg.speed[0]);
TEST_ASSERT_EQUAL(msg.speed[1], rx_msg.speed[1]);
TEST_ASSERT_EQUAL(msg.speed[2], rx_msg.speed[2]);
TEST_ASSERT_EQUAL(msg.enable[0], rx_msg.enable[0]);
TEST_ASSERT_EQUAL(msg.enable[1], rx_msg.enable[1]);
TEST_ASSERT_EQUAL(msg.enable[2], rx_msg.enable[2]);
TEST_ASSERT_EQUAL(msg.enable[3], rx_msg.enable[3]);
TEST_ASSERT_EQUAL(msg.enables.a, rx_msg.enables.a);
TEST_ASSERT_EQUAL(msg.enables.b, rx_msg.enables.b);
TEST_ASSERT_EQUAL(msg.enables.c, rx_msg.enables.c);
}
void test_encdec_generic_MotionUpdate() {
uint8_t buffer_msg_data[MAX_MESSAGE_DATA_SIZE];
Hcp_message_Error msg = {
.enables = {
.aa = true,
.asd = false,
.b = true,
.c = false,
.test = true
},
.recoveryStatus = HCP_ENUM_INLINE_ERROR_RECOVERY_STATUS_VALUE_NO
};
size_t length = toBytes_Hcp_message_Error(&msg, buffer_msg_data);
TEST_ASSERT_EQUAL(length, 5);
TEST_ASSERT_EQUAL(buffer_msg_data[0], 0x02);
Hcp_message_Error rx_msg;
size_t parsed_length = 0;
HCP_MSG_ID msg_id = 0xff;
HCP_PARSER_RESULT result = fromBytes(&rx_msg, buffer_msg_data, length, &parsed_length, &msg_id);
TEST_ASSERT_EQUAL(result, HCP_PARSER_RESULT_OK);
TEST_ASSERT_EQUAL(parsed_length, 5);
TEST_ASSERT_EQUAL(msg_id, HCP_MESSAGE_ERROR_ID);
TEST_ASSERT_EQUAL(rx_msg.enables.aa, msg.enables.aa);
TEST_ASSERT_EQUAL(rx_msg.enables.asd, msg.enables.asd);
TEST_ASSERT_EQUAL(rx_msg.enables.b, msg.enables.b);
TEST_ASSERT_EQUAL(rx_msg.enables.c, msg.enables.c);
TEST_ASSERT_EQUAL(rx_msg.enables.test, msg.enables.test);
TEST_ASSERT_EQUAL(rx_msg.recoveryStatus, msg.recoveryStatus);
}
void test_encdec_generic_Error_invalid_id() {
uint8_t buffer_msg_data[MAX_MESSAGE_DATA_SIZE];
Hcp_message_Error msg = {
.enables = {
.aa = true,
.asd = false,
.b = true,
.c = false,
.test = true
},
.recoveryStatus = HCP_ENUM_INLINE_ERROR_RECOVERY_STATUS_VALUE_NO
};
size_t length = toBytes_Hcp_message_Error(&msg, buffer_msg_data);
TEST_ASSERT_EQUAL(length, 5);
TEST_ASSERT_EQUAL(buffer_msg_data[0], 0x02);
// Mofiy data
buffer_msg_data[0] = 0xf2;
Hcp_message_Error rx_msg;
size_t parsed_length = 0;
HCP_MSG_ID msg_id = 0xff;
HCP_PARSER_RESULT result = fromBytes(&rx_msg, buffer_msg_data, length, &parsed_length, &msg_id);
TEST_ASSERT_EQUAL(parsed_length, 0);
TEST_ASSERT_EQUAL(result, HCP_PARSER_RESULT_ERROR_INVALID_ID);
TEST_ASSERT_EQUAL(msg_id, 0xff);
}
void test_encdec_generic_Error_invalid_size() {
uint8_t buffer_msg_data[MAX_MESSAGE_DATA_SIZE];
Hcp_message_Error msg = {
.enables = {
.aa = true,
.asd = false,
.b = true,
.c = false,
.test = true
},
.recoveryStatus = HCP_ENUM_INLINE_ERROR_RECOVERY_STATUS_VALUE_NO
};
size_t length = toBytes_Hcp_message_Error(&msg, buffer_msg_data);
TEST_ASSERT_EQUAL(length, 5);
TEST_ASSERT_EQUAL(buffer_msg_data[0], 0x02);
// Mofiy data
length = 2;
Hcp_message_Error rx_msg;
size_t parsed_length = 0;
HCP_MSG_ID msg_id = 0xff;
HCP_PARSER_RESULT result = fromBytes(&rx_msg, buffer_msg_data, length, &parsed_length, &msg_id);
TEST_ASSERT_EQUAL(parsed_length, 0);
TEST_ASSERT_EQUAL(result, HCP_PARSER_RESULT_ERROR_INVALID_SIZE);
TEST_ASSERT_EQUAL(msg_id, 0xff);
}
void test_encdec_generic_Error_large_size() {
uint8_t buffer_msg_data[MAX_MESSAGE_DATA_SIZE];
Hcp_message_Error msg = {
.enables = {
.aa = true,
.asd = false,
.b = true,
.c = false,
.test = true
},
.recoveryStatus = HCP_ENUM_INLINE_ERROR_RECOVERY_STATUS_VALUE_NO
};
size_t length = toBytes_Hcp_message_Error(&msg, buffer_msg_data);
TEST_ASSERT_EQUAL(length, 5);
TEST_ASSERT_EQUAL(buffer_msg_data[0], 0x02);
length += 5;
Hcp_message_Error rx_msg;
size_t parsed_length = 0;
HCP_MSG_ID msg_id = 0xff;
HCP_PARSER_RESULT result = fromBytes(&rx_msg, buffer_msg_data, length, &parsed_length, &msg_id);
TEST_ASSERT_EQUAL(result, HCP_PARSER_RESULT_OK);
TEST_ASSERT_EQUAL(parsed_length, 5);
TEST_ASSERT_EQUAL(msg_id, HCP_MESSAGE_ERROR_ID);
TEST_ASSERT_EQUAL(rx_msg.enables.aa, msg.enables.aa);
TEST_ASSERT_EQUAL(rx_msg.enables.asd, msg.enables.asd);
TEST_ASSERT_EQUAL(rx_msg.enables.b, msg.enables.b);
TEST_ASSERT_EQUAL(rx_msg.enables.c, msg.enables.c);
TEST_ASSERT_EQUAL(rx_msg.enables.test, msg.enables.test);
TEST_ASSERT_EQUAL(rx_msg.recoveryStatus, msg.recoveryStatus);
}
void test_encdec_generic_Error_invalid_crc() {
uint8_t buffer_msg_data[MAX_MESSAGE_DATA_SIZE];
Hcp_message_Error msg = {
.enables = {
.aa = true,
.asd = false,
.b = true,
.c = false,
.test = true
},
.recoveryStatus = HCP_ENUM_INLINE_ERROR_RECOVERY_STATUS_VALUE_NO
};
size_t length = toBytes_Hcp_message_Error(&msg, buffer_msg_data);
TEST_ASSERT_EQUAL(length, 5);
TEST_ASSERT_EQUAL(buffer_msg_data[0], 0x02);
// Mofiy data
buffer_msg_data[3] = 0xff;
Hcp_message_Error rx_msg;
size_t parsed_length = 0;
HCP_MSG_ID msg_id = 0xff;
HCP_PARSER_RESULT result = fromBytes(&rx_msg, buffer_msg_data, length, &parsed_length, &msg_id);
TEST_ASSERT_EQUAL(parsed_length, 0);
TEST_ASSERT_EQUAL(result, HCP_PARSER_RESULT_ERROR_INVALID_CRC);
TEST_ASSERT_EQUAL(msg_id, 0xff);
}

View File

@@ -0,0 +1,84 @@
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include "HCP_message.h"
#include "HCP_enum.h"
int main() {
char hex_string[1024];
uint8_t data[sizeof(hex_string)/2];
size_t bytesRead = read(STDIN_FILENO, hex_string, sizeof(hex_string));
for(int i=0; i<(bytesRead/2); i+=1) {
sscanf(&hex_string[i*2], "%02hhx", &data[i]);
}
// Decode the package
char package_buffer[MAX_MESSAGE_STRUCT_SIZE];
size_t parsed_size = 0;
HCP_MSG_ID msg_id;
HCP_PARSER_RESULT result = fromBytes(
(void *)(package_buffer), data, bytesRead/2, &parsed_size, &msg_id
);
// Early exit if the decode failed
if(result != HCP_PARSER_RESULT_OK) {
if(result == HCP_PARSER_RESULT_ERROR_INVALID_ID) {
printf("ID\n");
return -1;
} else if(result == HCP_PARSER_RESULT_ERROR_INVALID_CRC) {
printf("CRC\n");
return -1;
} else if(result == HCP_PARSER_RESULT_ERROR_INVALID_SIZE) {
printf("SIZE\n");
return -1;
} else {
printf("???\n");
return -1;
}
}
// Modify and encode the package
char send_buffer[MAX_MESSAGE_DATA_SIZE];
size_t send_size;
switch (msg_id) {
case HCP_MESSAGE_MOTION_UPDATE_ID: {
Hcp_message_MotionUpdate *msg = (struct Hcp_message_MotionUpdate *)package_buffer;
msg->speed[0] += 1;
msg->speed[1] += 2;
msg->speed[2] += 3;
msg->stearing *= 0.5;
msg->name[0] = 'A';
msg->enables.a = !msg->enables.a;
msg->enables.b = !msg->enables.b;
msg->enables.c = !msg->enables.c;
msg->dangerLvl = HCP_ENUM_INLINE_MOTION_UPDATE_DANGER_LVL_VALUE_MELTDOWN;
send_size = toBytes_Hcp_message_MotionUpdate(msg, send_buffer);
break;
}
case HCP_MESSAGE_ERROR_ID: {
Hcp_message_Error *msg = (struct Hcp_message_Error *)package_buffer;
msg->enables.b = !msg->enables.b;
msg->enables.c = !msg->enables.c;
send_size = toBytes_Hcp_message_Error(msg, send_buffer);
break;
}
default:
break;
}
// Print the encoded package
printf("OK\n");
for(int i=0; i<send_size; i++) {
uint8_t byte = send_buffer[i];
printf("%02x", byte);
}
printf("\n");
return 0;
}

View File

@@ -0,0 +1,162 @@
import sys
import subprocess
sys.path.append('src/')
sys.path.append('test/')
sys.path.append('test/backend_output/python')
def send_c_binary(data: bytes) -> tuple[int, str, bytes]:
proc = subprocess.Popen(
["build/test_integration_python"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE
)
stdout, _ = proc.communicate(data.hex().encode() + b"\n")
return_code = proc.wait()
lines = stdout.splitlines()
if return_code != 0:
return (return_code, lines[0].decode("ascii"), b"")
else:
return (
return_code,
lines[0].decode("ascii"),
bytes.fromhex(lines[1].decode())
)
def test_integration_c_encdec_motion_update():
from backend_output.python.HCP_protocol_packets import HcpMessageMotionUpdate
from backend_output.python.HCP_protocol_bitfield import HcpBitfieldEnables
from backend_output.python.HCP_protocol_enum import HcpEnumInlineMotionUpdateDangerLvl
pkg = HcpMessageMotionUpdate(
speed=[1, 2, 3],
stearing=-0.1,
name="This is a test",
enable=[True, False, True, False],
heading="S",
enables=HcpBitfieldEnables(
a=True, b=False, c=True
),
danger_lvl=HcpEnumInlineMotionUpdateDangerLvl.WARNING
)
# Send to Integration Test Binary
ret_code, _, data_incoming = send_c_binary(pkg.to_bytes())
assert ret_code == 0
# Decode the incoming data.
pkg_incoming = HcpMessageMotionUpdate().from_bytes(data_incoming)
# Check that the incoming data is correctly modified.
assert pkg.speed[0] + 1 == pkg_incoming.speed[0]
assert pkg.speed[1] + 2 == pkg_incoming.speed[1]
assert pkg.speed[2] + 3 == pkg_incoming.speed[2]
assert round(pkg.stearing * 0.5 * 1000) == round(pkg_incoming.stearing * 1000)
assert pkg_incoming.name[0] == "A"
assert pkg.name[1:] == pkg_incoming.name[1:]
assert pkg.enables.a != pkg_incoming.enables.a
assert pkg.enables.b != pkg_incoming.enables.b
assert pkg.enables.c != pkg_incoming.enables.c
assert pkg.enable == pkg_incoming.enable
assert pkg_incoming.danger_lvl.value == HcpEnumInlineMotionUpdateDangerLvl.MELTDOWN.value
def test_integration_c_encdec_error():
from backend_output.python.HCP_protocol_packets import HcpMessageError
from backend_output.python.HCP_protocol_bitfield import HcpBitfieldInlineErrorEnables
from backend_output.python.HCP_protocol_enum import HcpEnumInlineErrorRecoveryStatus
pkg = HcpMessageError(
recovery_status=HcpEnumInlineErrorRecoveryStatus.NO,
enables=HcpBitfieldInlineErrorEnables(
test=True, asd=False, b=False, c=True, aa=True
),
)
# Send to Integration Test Binary
ret_code, _, data_incoming = send_c_binary(pkg.to_bytes())
assert ret_code == 0
# Decode the incoming data.
pkg_incoming = HcpMessageError().from_bytes(data_incoming)
# Check that the incoming data is correctly modified.
assert pkg_incoming.recovery_status.value == pkg.recovery_status.value
assert pkg_incoming.enables.test == pkg.enables.test
assert pkg_incoming.enables.asd == pkg.enables.asd
assert pkg_incoming.enables.b != pkg.enables.b
assert pkg_incoming.enables.c != pkg.enables.c
assert pkg_incoming.enables.aa == pkg.enables.aa
def test_integration_c_encdec_failure_size():
from backend_output.python.HCP_protocol_packets import HcpMessageError
from backend_output.python.HCP_protocol_bitfield import HcpBitfieldInlineErrorEnables
from backend_output.python.HCP_protocol_enum import HcpEnumInlineErrorRecoveryStatus
pkg = HcpMessageError(
recovery_status=HcpEnumInlineErrorRecoveryStatus.NO,
enables=HcpBitfieldInlineErrorEnables(
test=True, asd=False, b=False, c=True, aa=True
),
)
# Send to Integration Test Binary
data = pkg.to_bytes()
ret_code, msg, _ = send_c_binary(data[:4])
assert ret_code != 0
assert msg == "SIZE"
def test_integration_c_encdec_failure_crc():
from backend_output.python.HCP_protocol_packets import HcpMessageError
from backend_output.python.HCP_protocol_bitfield import HcpBitfieldInlineErrorEnables
from backend_output.python.HCP_protocol_enum import HcpEnumInlineErrorRecoveryStatus
pkg = HcpMessageError(
recovery_status=HcpEnumInlineErrorRecoveryStatus.NO,
enables=HcpBitfieldInlineErrorEnables(
test=True, asd=False, b=False, c=True, aa=True
),
)
# Send to Integration Test Binary
data = pkg.to_bytes()
data = data[:2] + b"\xaa" + data[3:]
ret_code, msg, _ = send_c_binary(data)
assert ret_code != 0
assert msg == "CRC"
def test_integration_c_encdec_failure_id():
from backend_output.python.HCP_protocol_packets import HcpMessageError
from backend_output.python.HCP_protocol_bitfield import HcpBitfieldInlineErrorEnables
from backend_output.python.HCP_protocol_enum import HcpEnumInlineErrorRecoveryStatus
pkg = HcpMessageError(
recovery_status=HcpEnumInlineErrorRecoveryStatus.NO,
enables=HcpBitfieldInlineErrorEnables(
test=True, asd=False, b=False, c=True, aa=True
),
)
# Send to Integration Test Binary
data = pkg.to_bytes()
data = b"\xff" + data[1:]
ret_code, msg, _ = send_c_binary(data)
assert ret_code != 0
assert msg == "ID"

63
test/output/test_crc.py Normal file
View File

@@ -0,0 +1,63 @@
def crc16(data: bytearray, offset: int, length: int):
if data is None or offset < 0 or offset > len(data) - 1 and offset + length > len(data):
return 0
crc = 0xFFFF
for i in range(0, length):
crc ^= data[offset + i] << 8
for j in range(0, 8):
if (crc & 0x8000) > 0:
crc = (crc << 1) ^ 0x1021
else:
crc = crc << 1
return crc & 0xFFFF
def test_crc_sum():
# ignore: F401
from backend_output.python.bproto_base import crc16_calc_inital, calc_crc_sum
protocol_hash = bytes.fromhex("4a07789a41ef64c38a7edb78574497d4")
protocol_data = bytes.fromhex("c9850ff561ea6d27f3171e1c680a4607ea8fa5278ee96de5c8e0ade101bdeb4332d07cb2269a47c910ca2cfe95de9ceda0bb9ee05b0909dcaf68f6a605df7dd4")
inital = crc16_calc_inital(protocol_hash)
checksum = calc_crc_sum(protocol_data, inital)
ref_data = protocol_hash + protocol_data
assert crc16(ref_data, 0, len(ref_data)) == checksum
protocol_hash = bytes.fromhex("4a07789a41ef64c38a7edb78574497d4")
protocol_data = bytes.fromhex("8898ebbd28e82663b71a7bbf8bc5ec72872ca057a77e31c1c11deff5004e8985767acb75da9b2d1dea643b08ed0458477e4a")
inital = crc16_calc_inital(protocol_hash)
checksum = calc_crc_sum(protocol_data, inital)
ref_data = protocol_hash + protocol_data
assert crc16(ref_data, 0, len(ref_data)) == checksum
protocol_hash = bytes.fromhex("4a07789a41ef64c38a7edb78574497d4")
protocol_data = bytes.fromhex("ca321a923e202614482cb392d858175ffe9f117d38")
inital = crc16_calc_inital(protocol_hash)
checksum = calc_crc_sum(protocol_data, inital)
ref_data = protocol_hash + protocol_data
assert crc16(ref_data, 0, len(ref_data)) == checksum
def test_crc_sum_change():
# ignore: F401
from backend_output.python.bproto_base import crc16_calc_inital, calc_crc_sum
protocol_hash = bytes.fromhex("4a07789a41ef64c38a7edb78574497d4")
protocol_data = bytes.fromhex("ca321a923e202614482cb392d858175ffe9f117d38")
inital = crc16_calc_inital(protocol_hash)
checksum = calc_crc_sum(protocol_data + b"\xaa", inital)
ref_data = protocol_hash + protocol_data
assert crc16(ref_data, 0, len(ref_data)) != checksum
protocol_hash = bytes.fromhex("4a07789a41ef64c38a7edb78574497d4")
protocol_data = bytes.fromhex("ca321a923e202614482cb392d858175ffe9f117d38")
inital = crc16_calc_inital(protocol_hash[-1:] + b"\xaa")
checksum = calc_crc_sum(protocol_data, inital)
ref_data = protocol_hash + protocol_data
assert crc16(ref_data, 0, len(ref_data)) != checksum

197
test/output/test_encdec.py Normal file
View File

@@ -0,0 +1,197 @@
import sys
sys.path.append('src/')
sys.path.append('test/')
sys.path.append('test/backend_output/python')
def test_HCP_encdec_MessageMotionUpdate():
# ignore: F401
from backend_output.python.HCP_protocol_packets import HcpMessageMotionUpdate
from backend_output.python.HCP_protocol_bitfield import HcpBitfieldEnables
from backend_output.python.HCP_protocol_enum import HcpEnumInlineMotionUpdateDangerLvl
src_msg = HcpMessageMotionUpdate(
speed=[2, 3, 4],
stearing=0.3,
name="Hello HCP!",
enable=[True, False, True, False],
heading="N",
enables=HcpBitfieldEnables(
a=True, b=False, c=True
),
danger_lvl=HcpEnumInlineMotionUpdateDangerLvl.ERROR
)
data = src_msg.to_bytes()
isinstance(data, bytes)
dest_msg = HcpMessageMotionUpdate().from_bytes(data)
assert isinstance(dest_msg.speed, list)
assert isinstance(dest_msg.speed[0], int)
assert src_msg.speed == dest_msg.speed
assert isinstance(dest_msg.stearing, float)
assert round(src_msg.stearing * 1000) == round(dest_msg.stearing * 1000)
assert isinstance(dest_msg.name, str)
assert src_msg.name == dest_msg.name
assert isinstance(dest_msg.enable, list)
assert isinstance(dest_msg.enable[0], bool)
assert src_msg.enable == dest_msg.enable
assert isinstance(dest_msg.heading, str)
assert src_msg.heading == dest_msg.heading
assert isinstance(dest_msg.enables.a, bool)
assert isinstance(dest_msg.enables.b, bool)
assert isinstance(dest_msg.enables.c, bool)
assert src_msg.enables.a == dest_msg.enables.a
assert src_msg.enables.b == dest_msg.enables.b
assert src_msg.enables.c == dest_msg.enables.c
assert isinstance(src_msg.danger_lvl, HcpEnumInlineMotionUpdateDangerLvl)
assert src_msg.danger_lvl.value == dest_msg.danger_lvl.value
def test_HCP_encdec_MessageMotionUpdate_failure():
# ignore: F401
from backend_output.python.HCP_protocol_packets import HcpMessageMotionUpdate
from backend_output.python.HCP_protocol_bitfield import HcpBitfieldEnables
from backend_output.python.HCP_protocol_enum import HcpEnumInlineMotionUpdateDangerLvl
from backend_output.python.bproto_error import bproto_PackageErrorInvalidMessageID, bproto_PackageErrorInvalidSize
src_msg = HcpMessageMotionUpdate(
speed=[2, 3, 4],
stearing=0.3,
name="Hello HCP!",
enable=[True, False, True, False],
heading="N",
enables=HcpBitfieldEnables(
a=True, b=False, c=True
),
danger_lvl=HcpEnumInlineMotionUpdateDangerLvl.ERROR
)
data = src_msg.to_bytes()
try:
HcpMessageMotionUpdate().from_bytes(b"\xaa" + data[1:])
except Exception as e:
isinstance(e, bproto_PackageErrorInvalidMessageID)
data = src_msg.to_bytes()
try:
HcpMessageMotionUpdate().from_bytes(data[:10])
except Exception as e:
isinstance(e, bproto_PackageErrorInvalidSize)
def test_HCP_encdec_HcpMessageError():
# ignore: F401
from backend_output.python.HCP_protocol_packets import HcpMessageError
from backend_output.python.HCP_protocol_bitfield import HcpBitfieldInlineErrorEnables
from backend_output.python.HCP_protocol_enum import HcpEnumInlineErrorRecoveryStatus
src_msg = HcpMessageError(
recovery_status=HcpEnumInlineErrorRecoveryStatus.YES,
enables=HcpBitfieldInlineErrorEnables(
test=True, asd=False, b=True, c=False, aa=True
)
)
data = src_msg.to_bytes()
isinstance(data, bytes)
dest_msg = HcpMessageError().from_bytes(data)
assert src_msg.recovery_status.value == dest_msg.recovery_status.value
assert src_msg.enables.test == dest_msg.enables.test
assert src_msg.enables.asd == dest_msg.enables.asd
assert src_msg.enables.b == dest_msg.enables.b
assert src_msg.enables.c == dest_msg.enables.c
assert src_msg.enables.aa == dest_msg.enables.aa
def test_HCP_encdec_HcpMessageError_failure():
# ignore: F401
from backend_output.python.HCP_protocol_packets import HcpMessageError
from backend_output.python.HCP_protocol_bitfield import HcpBitfieldInlineErrorEnables
from backend_output.python.HCP_protocol_enum import HcpEnumInlineErrorRecoveryStatus
from backend_output.python.bproto_error import bproto_PackageErrorInvalidMessageID, bproto_PackageErrorInvalidSize
src_msg = HcpMessageError(
recovery_status=HcpEnumInlineErrorRecoveryStatus.YES,
enables=HcpBitfieldInlineErrorEnables(
test=True, asd=False, b=True, c=False, aa=True
)
)
data = src_msg.to_bytes()
try:
HcpMessageError().from_bytes(b"\xaa" + data[1:])
except Exception as e:
isinstance(e, bproto_PackageErrorInvalidMessageID)
data = src_msg.to_bytes()
try:
HcpMessageError().from_bytes(data[:3])
except Exception as e:
isinstance(e, bproto_PackageErrorInvalidSize)
def test_generic_parse_package():
# ignore: F401
from backend_output.python.HCP_protocol_packets import HcpMessageError, HcpMessageMotionUpdate, parse_package
from backend_output.python.HCP_protocol_bitfield import HcpBitfieldInlineErrorEnables, HcpBitfieldEnables
from backend_output.python.HCP_protocol_enum import HcpEnumInlineErrorRecoveryStatus, HcpEnumInlineMotionUpdateDangerLvl
from backend_output.python.bproto_error import bproto_PackageErrorInvalidMessageID, bproto_PackageErrorInvalidSize
src_msg1 = HcpMessageError(
recovery_status=HcpEnumInlineErrorRecoveryStatus.YES,
enables=HcpBitfieldInlineErrorEnables(
test=True, asd=False, b=True, c=False, aa=True
)
)
src_msg2 = HcpMessageMotionUpdate(
speed=[2, 3, 4],
stearing=0.3,
name="Hello HCP!",
enable=[True, False, True, False],
heading="N",
enables=HcpBitfieldEnables(
a=True, b=False, c=True
),
danger_lvl=HcpEnumInlineMotionUpdateDangerLvl.ERROR
)
dest_msg1, parsed_size = parse_package(src_msg1.to_bytes())
assert parsed_size == 5
assert isinstance(dest_msg1, HcpMessageError)
dest_msg2, parsed_size = parse_package(src_msg2.to_bytes())
assert parsed_size == 58
assert isinstance(dest_msg2, HcpMessageMotionUpdate)
try:
parse_package(b"\xaa" + src_msg2.to_bytes()[1:])
except Exception as e:
isinstance(e, bproto_PackageErrorInvalidMessageID)
try:
parse_package(src_msg1.to_bytes()[:2])
except Exception as e:
isinstance(e, bproto_PackageErrorInvalidSize)
try:
parse_package(src_msg2.to_bytes()[:2])
except Exception as e:
isinstance(e, bproto_PackageErrorInvalidSize)

View File

@@ -0,0 +1,82 @@
from bproto_base import bproto_Bitfield
class HcpBitfieldEnables(bproto_Bitfield):
BYTE_SIZE = 1
def __init__(self,
a=False,
b=False,
c=False,
):
self.a = a
self.b = b
self.c = c
def to_bytes(self) -> bytes:
res = 0
res |= (1 << 0) if self.a else 0
res |= (1 << 1) if self.b else 0
res |= (1 << 3) if self.c else 0
return int.to_bytes(res, self.BYTE_SIZE, 'little')
def from_bytes(self, data: bytes):
bits = int.from_bytes(data[:self.BYTE_SIZE], 'little')
self.a = (bits & (1 << 0)) != 0
self.b = (bits & (1 << 1)) != 0
self.c = (bits & (1 << 3)) != 0
return self
def __repr__(self) -> str:
value_str = ", ".join([
"a=" + str(self.a),
"b=" + str(self.b),
"c=" + str(self.c),
])
return "HcpBitfieldEnables(" + value_str + ")"
class HcpBitfieldInlineErrorEnables(bproto_Bitfield):
BYTE_SIZE = 1
def __init__(self,
test=False,
asd=False,
b=False,
c=False,
aa=False,
):
self.test = test
self.asd = asd
self.b = b
self.c = c
self.aa = aa
def to_bytes(self) -> bytes:
res = 0
res |= (1 << 0) if self.test else 0
res |= (1 << 1) if self.asd else 0
res |= (1 << 2) if self.b else 0
res |= (1 << 3) if self.c else 0
res |= (1 << 7) if self.aa else 0
return int.to_bytes(res, self.BYTE_SIZE, 'little')
def from_bytes(self, data: bytes):
bits = int.from_bytes(data[:self.BYTE_SIZE], 'little')
self.test = (bits & (1 << 0)) != 0
self.asd = (bits & (1 << 1)) != 0
self.b = (bits & (1 << 2)) != 0
self.c = (bits & (1 << 3)) != 0
self.aa = (bits & (1 << 7)) != 0
return self
def __repr__(self) -> str:
value_str = ", ".join([
"test=" + str(self.test),
"asd=" + str(self.asd),
"b=" + str(self.b),
"c=" + str(self.c),
"aa=" + str(self.aa),
])
return "HcpBitfieldInlineErrorEnables(" + value_str + ")"

View File

@@ -0,0 +1,19 @@
import enum
class HcpEnumErrorCodes(enum.Enum):
MOTOR1_DISCONNECTED = 1
MOTOR2_DISCONNECTED = 2
MOTORS_DISCONNECTED = 3
INPUT_BUFFER_OVERFLOW = 4
class HcpEnumInlineMotionUpdateDangerLvl(enum.Enum):
WARNING = 0
ERROR = 1
MELTDOWN = 2
class HcpEnumInlineErrorRecoveryStatus(enum.Enum):
YES = 0
NO = 1

View File

@@ -0,0 +1,12 @@
import enum
class MessageIds_HCP(enum.Enum):
MSG_ID_HCP_MESSAGE_MOTION_UPDATE = 1
MSG_ID_HCP_MESSAGE_ERROR = 2
MESSAGE_SIZE_MAP_HCP = {
MessageIds_HCP.MSG_ID_HCP_MESSAGE_MOTION_UPDATE: 58,
MessageIds_HCP.MSG_ID_HCP_MESSAGE_ERROR: 5,
}

View File

@@ -0,0 +1,134 @@
from bproto_base import bproto_Package, calc_crc_sum
from bproto_error import bproto_Error, bproto_PackageErrorInvalidSize, bproto_PackageErrorInvalidMessageID, bproto_PackageErrorInvalidCRC
from HCP_protocol_enum import HcpEnumErrorCodes, HcpEnumInlineMotionUpdateDangerLvl, HcpEnumInlineErrorRecoveryStatus
from HCP_protocol_bitfield import HcpBitfieldEnables, HcpBitfieldInlineErrorEnables
from HCP_protocol_message_ids import MessageIds_HCP, MESSAGE_SIZE_MAP_HCP
from typing import Annotated, List
import struct
class HcpMessageMotionUpdate(bproto_Package):
ID = MessageIds_HCP.MSG_ID_HCP_MESSAGE_MOTION_UPDATE
SIZE = 58
speed: Annotated[List[int], 3]
stearing: float
name: str
enable: Annotated[List[bool], 4]
heading: str
enables: HcpBitfieldEnables
danger_lvl: HcpEnumInlineMotionUpdateDangerLvl
def __init__(self,
speed: Annotated[List[int], 3] = 0,
stearing: float = 0.0,
name: str = '',
enable: Annotated[List[bool], 4] = False,
heading: str = '',
enables: HcpBitfieldEnables = HcpBitfieldEnables(),
danger_lvl: HcpEnumInlineMotionUpdateDangerLvl = HcpEnumInlineMotionUpdateDangerLvl.WARNING,
):
self.speed = speed
self.stearing = stearing
self.name = name
self.enable = enable
self.heading = heading
self.enables = enables
self.danger_lvl = danger_lvl
def to_bytes(self) -> bytes:
res = struct.pack(">B", self.ID.value)
res += struct.pack('>iii', self.speed[0], self.speed[1], self.speed[2])
res += struct.pack('>f', self.stearing)
res += self.name.encode('ascii')[:32].ljust(32, b'\x00')
res += struct.pack('>BBBB', self.enable[0], self.enable[1], self.enable[2], self.enable[3])
res += self.heading.encode('ascii')[:1].ljust(1, b'\x00')
res += self.enables.to_bytes()
res += struct.pack('>B', self.danger_lvl.value)
res += calc_crc_sum(res)
return res
def from_bytes(self, data: bytes):
# msg_id = struct.unpack('>B', data[:1])
data = data[1:]
self.speed = list(struct.unpack('>iii', data[:12]))
data = data[12:]
self.stearing = struct.unpack('>f', data[:4])[0]
data = data[4:]
self.name = data[:32].decode('ascii').strip('\x00')
data = data[32:]
self.enable = [bool(i) for i in struct.unpack('>BBBB', data[:4])]
data = data[4:]
self.heading = data[:1].decode('ascii').strip('\x00')
data = data[1:]
self.enables = HcpBitfieldEnables().from_bytes(data[:1])
data = data[1:]
self.danger_lvl = HcpEnumInlineMotionUpdateDangerLvl(struct.unpack('>B', data[:1])[0])
data = data[1:]
# crc = data[:2]
return self
class HcpMessageError(bproto_Package):
ID = MessageIds_HCP.MSG_ID_HCP_MESSAGE_ERROR
SIZE = 5
recovery_status: HcpEnumInlineErrorRecoveryStatus
enables: HcpBitfieldInlineErrorEnables
def __init__(self,
recovery_status: HcpEnumInlineErrorRecoveryStatus = HcpEnumInlineErrorRecoveryStatus.YES,
enables: HcpBitfieldInlineErrorEnables = HcpBitfieldInlineErrorEnables(),
):
self.recovery_status = recovery_status
self.enables = enables
def to_bytes(self) -> bytes:
res = struct.pack(">B", self.ID.value)
res += struct.pack('>B', self.recovery_status.value)
res += self.enables.to_bytes()
res += calc_crc_sum(res)
return res
def from_bytes(self, data: bytes):
# msg_id = struct.unpack('>B', data[:1])
data = data[1:]
self.recovery_status = HcpEnumInlineErrorRecoveryStatus(struct.unpack('>B', data[:1])[0])
data = data[1:]
self.enables = HcpBitfieldInlineErrorEnables().from_bytes(data[:1])
data = data[1:]
# crc = data[:2]
return self
def parse_package(data: bytes) -> tuple[bproto_Package, int]:
if len(data) < 1:
raise bproto_PackageErrorInvalidSize("Package has no data")
msg_id = struct.unpack('>B', data[:1])
if msg_id not in MessageIds_HCP:
raise bproto_PackageErrorInvalidMessageID(f"Message ID '{msg_id}' is invaild")
msg_id = MessageIds_HCP(msg_id)
expected_size = MESSAGE_SIZE_MAP_HCP.get(msg_id) or 0
if expected_size > len(data):
raise bproto_PackageErrorInvalidSize(f"Package is to short for '{msg_id}' ({expected_size} > {len(data)})")
pkg_data = data[:expected_size]
crc_data = pkg_data[-2:]
if calc_crc_sum(pkg_data[:-2]) != crc_data:
raise bproto_PackageErrorInvalidCRC(f"CRC {crc_data.hex()} did not match calculated")
match(msg_id):
case MessageIds_HCP.MSG_ID_HCP_MESSAGE_MOTION_UPDATE:
return HcpMessageMotionUpdate().from_bytes(pkg_data), expected_size
case MessageIds_HCP.MSG_ID_HCP_MESSAGE_ERROR:
return HcpMessageError().from_bytes(pkg_data), expected_size
case _:
raise bproto_Error("Message ID could not be interpreted; this should not have happen; its a developer error! Create a issue")

View File

@@ -0,0 +1,49 @@
Name: Hcp
Version: 0
Enums:
ErrorCodes:
- MOTOR1_DISCONNECTED = 1
- MOTOR2_DISCONNECTED = 2
- MOTORS_DISCONNECTED = 3
- INPUT_BUFFER_OVERFLOW = 4
InlineMotionUpdateDangerLvl:
- WARNING = 0
- ERROR = 1
- MELTDOWN = 2
InlineErrorRecoveryStatus:
- YES = 0
- NO = 1
Bitfield:
Enables:
- a: 0
- b: 1
- c: 3
InlineErrorEnables:
- test: 0
- asd: 1
- b: 2
- c: 3
- aa: 7
Messages:
[1] MotionUpdate:
Size: 55 bytes
Fields:
- (12) speed: int32[3]
- (4) stearing: float32[1]
- (32) name: string[32]
- (4) enable: bool[4]
- (1) heading: char[1]
- (1) enables: bitfield[1] -> Enables
- (1) dangerLvl: enum[1] -> InlineMotionUpdateDangerLvl
[2] Error:
Size: 2 bytes
Fields:
- (1) recoveryStatus: enum[1] -> InlineErrorRecoveryStatus
- (1) enables: bitfield[1] -> InlineErrorEnables

4
test/runner.py Normal file
View File

@@ -0,0 +1,4 @@
import pytest
if __name__ == '__main__':
pytest.main()

39
test/test_protocol.bproto Normal file
View File

@@ -0,0 +1,39 @@
protocol HCP version 0
enum error_codes {
MOTOR1_disconnected = 1,
MOTOR2_disconnected = 2,
MOTORS_disconnected = 3,
input_buffer_overflow = 4,
}
bits enables {
a, b, c: 3
}
message [1] MotionUpdate {
[0] speed : int32[3],
[1] stearing : float32,
[3] enable : bool[4],
[4] heading : char,
[2] name : string[32],
[5] enables : bits enables,
[6] dangerLvl: enum {
WARNING,
ERROR, MELTDOWN
}
}
message [2] Error {
[0] recoveryStatus: enum {
YES, NO
},
[1] enables: bits {
test : 0,
asd : 1,
b,
c,
aa: 7
}
}