powertrain-build/tests/interface/test_interface_device_proxy.py
Henrik Wahlqvist fda739bc13 Refactor usage of ecu_supplier
Removed most of the decisions based on ecu_supplier and made it more
granular using individual configuration.
Current project types will keep their config by adding templates in the
BaseConfig.json file.
Some usecases were kept for legacy reasons.

Change-Id: I3d6199713006489baff0bf73751596770fd1f968
2024-09-17 08:25:46 +00:00

690 lines
24 KiB
Python

# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
import unittest
from unittest import mock
from powertrain_build.interface.base import Signal
from powertrain_build.interface.device_proxy import DPAL, BadYamlFormat
class TestDPAL(unittest.TestCase):
def setUp(self):
self.interface_spec = {
"default": 3,
"domain": "test_domain",
"factor": 2,
"length": 4,
"offset": 1,
"property": "test_property",
"property_type": "Float32",
"variable": "test_signal",
"variable_type": "float32",
"range": {"min": 0, "max": 5},
"init": 0,
"description": "Dummy description",
"unit": "Nm",
"group": None,
"strategy": "Always",
'debug': False,
'dependability': False,
'port_name': None
}
self.signal_definition = {
"insignal": "test_signal",
"property": "test_property",
"type": "float32",
"offset": 1,
"factor": 2,
"default": 3,
"length": 4,
"min": 0,
"max": 5,
"enum": None,
"init": 0,
"description": "Dummy description",
"unit": "Nm",
"group": None,
"strategy": "Always",
'dependability': False,
'debug': False
}
self.dep_signal_definition = {
"insignal": "test_e2e_signal",
"property": "test_property",
"type": "float32",
"offset": 1,
"factor": 2,
"default": 3,
"length": 4,
"min": 0,
"max": 5,
"enum": None,
"init": 0,
"description": "Dummy description",
"unit": "Nm",
"group": "test_group",
"strategy": "Always",
'dependability': True,
'debug': False
}
self.e2e_sts_signal_definition = {
"insignal": "sVctest_domain_D_test_groupE2eSts",
"property": "test_property",
"type": "float32",
"offset": 1,
"factor": 2,
"default": 3,
"length": 4,
"min": 0,
"max": 5,
"enum": None,
"init": 255,
"description": "Dummy description",
"unit": "Nm",
"group": "test_group",
"strategy": "Always",
'dependability': True,
'debug': False
}
self.signal_spec = (
"enum_0",
"test_domain",
"test_property",
"float32",
"Float32",
"Float32",
1,
2,
3,
4,
0,
5,
None,
0,
"Dummy description",
"Nm",
None,
"Always",
False,
False,
None
)
self.dep_signal_spec = (
"enum_0",
"test_domain",
"test_property",
"float32",
"Float32",
"Float32",
1,
2,
3,
4,
0,
5,
None,
0,
"Dummy description",
"Nm",
"test_group",
"Always",
False,
True,
None
)
self.e2e_sts_signal_spec = (
"enum_0",
"test_domain",
"test_groupE2eSts",
"UInt8",
"UInt8",
"UInt8",
0,
1,
None,
None,
0,
255,
None,
255,
"E2E status code for E2E protected signal (group) test_e2e_signal.",
None,
"test_group",
"Always",
False,
True,
None
)
self.signal_yaml = {
"default": 3,
"length": 4,
"name": "test_property",
"type": "Float32",
"range": {"min": 0, "max": 5},
"description": "Dummy description",
"unit": "Nm"
}
self.outsignals = [
{
"outsignal": "outsignal",
"property": "test_property",
"type": "Float32",
"offset": 1,
"factor": 2,
"default": 3,
"length": 4,
"min": 0,
"max": 5,
"enum": None,
"init": 0,
"description": "Dummy description",
"unit": "Nm",
"group": None,
"strategy": "Always",
"debug": False,
"dependability": False
},
{
"outsignal": "outsignal_bad_strategy",
"property": "test_property",
"type": "Float32",
"offset": 1,
"factor": 2,
"default": 3,
"length": 4,
"min": 0,
"max": 5,
"enum": None,
"init": 0,
"description": "Dummy description",
"unit": "Nm",
"group": None,
"strategy": "OnChanged",
"debug": False,
"dependability": False
},
{
"outsignal": "outsignal_nonexistent_strategy",
"property": "test_property",
"type": "Float32",
"offset": 1,
"factor": 2,
"default": 3,
"length": 4,
"min": 0,
"max": 5,
"enum": None,
"init": 0,
"description": "Dummy description",
"unit": "Nm",
"group": None,
"strategy": "NonExistent",
"debug": False,
"dependability": False
}
]
app = mock.MagicMock()
app.get_domain_mapping.return_value = {"test": "test_domain"}
app.pybuild['build_cfg'].get_code_generation_config.return_value = True
self.dummy_app = app
self.maxDiff = None
def test_add_signals(self):
dpal = DPAL(self.dummy_app)
insignal1 = Signal("test_name_in1", None)
insignal2 = Signal("test_name_in2", None)
outsignal = Signal("test_name_out", None)
dpal.dp_translations = {"test_name_in1": {self.signal_spec},
"test_name_in2": {self.signal_spec},
"test_name_in_e2e": {self.dep_signal_spec},
"test_name_out": {self.signal_spec}, }
dpal.e2e_sts_signals.add("test_name_in_e2e")
dpal.add_signals([insignal1], "insignals")
expected = {
"other": {"insignals": {"test_name_in1", "test_name_in_e2e"}, "outsignals": set()},
"dp": {"insignals": set(), "outsignals": {"test_property"}},
}
expected_signal_primitives_list = ["test_domain.test_property.insignals"]
self.assertDictEqual(expected, dpal.signal_names)
self.assertListEqual(expected_signal_primitives_list, dpal.signal_primitives_list)
dpal.add_signals([outsignal], "outsignals")
expected = {
"other": {
"insignals": {"test_name_in1", "test_name_in_e2e"},
"outsignals": {"test_name_out"},
},
"dp": {"insignals": {"test_property"},
"outsignals": {"test_property"}},
}
expected_signal_primitives_list = ["test_domain.test_property.insignals",
"test_domain.test_property.outsignals"]
self.assertDictEqual(expected, dpal.signal_names)
self.assertListEqual(expected_signal_primitives_list, dpal.signal_primitives_list)
exception_msg = "You can't write test_domain.test_property.insignals"\
" as insignals since this primitive has been used."\
" Run model_yaml_verification to identify exact models."
with self.assertRaises(Exception) as context:
dpal.add_signals([insignal2], "insignals")
self.assertEqual(exception_msg, str(context.exception))
def test_check_group_mixed_signals(self):
dpal = DPAL(self.dummy_app)
test_signal = list(self.signal_spec)
test_signal[16] = "signal_group_a"
test_signal_1 = tuple(test_signal)
test_signal[16] = "signal_group_a"
test_signal_2 = tuple(test_signal)
dpal.dp_translations = {"test_signal_1": {test_signal_1},
"test_signal_2": {test_signal_2}
}
dpal.signal_names["other"]["insignals"] = {
"test_signal_1",
}
dpal.signal_names["other"]["outsignals"] = {
"test_signal_2",
}
expected_message = "Signal group signal_group_a for test_domain contains both consumed and produced signals"
with self.assertRaises(AssertionError) as context:
dpal.check_groups()
self.assertEqual(expected_message, str(context.exception))
def test_check_group_mixed_signals_mixed_signal_groups(self):
dpal = DPAL(self.dummy_app)
test_signal = list(self.signal_spec)
test_signal[16] = "signal_group_a"
test_signal_1 = tuple(test_signal)
test_signal[16] = "signal_group_b"
test_signal_2 = tuple(test_signal)
test_signal[16] = "signal_group_b"
test_signal_1 = tuple(test_signal)
dpal.dp_translations = {"test_signal_1": {test_signal_1},
"test_signal_2": {test_signal_2}
}
dpal.signal_names["other"]["insignals"] = {
"test_signal_1",
}
dpal.signal_names["other"]["outsignals"] = {
"test_signal_2",
}
expected_message = "Signal group signal_group_b for test_domain contains both consumed and produced signals"
with self.assertRaises(AssertionError) as context:
dpal.check_groups()
self.assertEqual(expected_message, str(context.exception))
def test_clear_signal_names(self):
dpal = DPAL(self.dummy_app)
insignal1 = Signal("test_name_in1", None)
insignal2 = Signal("test_name_in2", None)
outsignal = Signal("test_name_out", None)
dpal.add_signals([insignal1, insignal2], "insignals")
expected = {
"other": {"insignals": {"test_name_in1"}, "outsignals": set()},
"dp": {"insignals": set(), "outsignals": set()},
}
dpal.add_signals([outsignal], "outsignals")
dpal.clear_signal_names()
expected = {
"other": {"insignals": set(), "outsignals": set()},
"dp": {"insignals": set(), "outsignals": set()},
}
self.assertDictEqual(expected, dpal.signal_names)
def test_parse_signal_definitions(self):
signal_definitions = {"test": [self.signal_definition]}
app = self.dummy_app
app.insignals = [Signal("test_signal", None)]
app.get_signal_properties.return_value = {"type": "Float32"}
dpal = DPAL(app)
dpal.parse_signal_definitions(signal_definitions)
expected = {"test_signal": {self.signal_spec}}
self.assertDictEqual(dpal.dp_translations, expected)
def test_parse_e2e_group_signal_definitions(self):
signal_definitions = {"test": [self.dep_signal_definition]}
app = self.dummy_app
app.insignals = [Signal("test_e2e_signal", None)]
app.get_signal_properties.return_value = {"type": "Float32"}
dpal = DPAL(app)
dpal.parse_signal_definitions(signal_definitions, 'test_group')
expected = {
"test_e2e_signal": {self.dep_signal_spec},
"sVctest_domain_D_test_groupE2eSts": {self.e2e_sts_signal_spec}
}
self.assertDictEqual(dpal.dp_translations, expected)
def test_parse_bad_signal_definition(self):
"""E2E status signals cannot be in any yaml interface file."""
signal_definitions = {"test": [self.e2e_sts_signal_definition]}
app = self.dummy_app
app.insignals = [Signal("sVctest_domain_D_test_groupE2eSts", None)]
app.get_signal_properties.return_value = {"type": "Float32"}
dpal = DPAL(app)
with self.assertRaises(BadYamlFormat):
dpal.parse_signal_definitions(signal_definitions, 'test_group')
def test_parse_signal_definitions_output_strategies(self):
signal_definitions = {"test": self.outsignals}
app = self.dummy_app
outsignal1 = Signal("outsignal", None)
outsignal2 = Signal("outsignal_bad_strategy", None)
outsignal3 = Signal("outsignal_nonexistent_strategy", None)
app.outsignals = [outsignal1, outsignal2, outsignal3]
app.get_signal_properties.return_value = {"type": "Float32"}
dpal = DPAL(app)
dpal.parse_signal_definitions(signal_definitions)
expected = {
"outsignal": {(
"enum_0",
"test_domain",
"test_property",
"Float32",
"Float32",
"Float32",
1,
2,
3,
4,
0,
5,
None,
0,
"Dummy description",
"Nm",
None,
"Always",
False,
False,
None
)},
"outsignal_bad_strategy": {(
"enum_0",
"test_domain",
"test_property",
"Float32",
"Float32",
"Float32",
1,
2,
3,
4,
0,
5,
None,
0,
"Dummy description",
"Nm",
None,
"Always",
False,
False,
None
)},
"outsignal_nonexistent_strategy": {(
"enum_0",
"test_domain",
"test_property",
"Float32",
"Float32",
"Float32",
1,
2,
3,
4,
0,
5,
None,
0,
"Dummy description",
"Nm",
None,
"Always",
False,
False,
None
)}
}
self.assertDictEqual(dpal.dp_translations, expected)
def test_dp_spec_to_dict(self):
result = DPAL(self.dummy_app).dp_spec_to_dict(self.signal_spec, "test_signal")
self.assertDictEqual(result, self.interface_spec)
def test_insignals_dp_manifest_signals(self):
dpal = DPAL(self.dummy_app)
suffix = "unittest"
dpal.dp_translations = {"test_signal": {self.signal_spec}}
dpal.signal_names["other"]["insignals"] = {
"test_signal",
}
result = dpal.insignals_dp_manifest(suffix)
expected = [{"name": "unittest", "signal_groups": [{"name": self.signal_yaml["name"]}]}]
self.assertListEqual(result, expected)
def test_insignals_dp_manifest_signal_groups(self):
dpal = DPAL(self.dummy_app)
suffix = "unittest"
test_signal = list(self.signal_spec)
test_signal[16] = "signal_group_a"
test_signal_1 = tuple(test_signal)
dpal.dp_translations = {"test_signal_1": {test_signal_1},
}
dpal.signal_names["other"]["insignals"] = {
"test_signal_1",
}
result = dpal.insignals_dp_manifest(suffix)
expected = [{"name": "unittest", "signal_groups": [{"name": "signal_group_a"}]}]
self.assertListEqual(result, expected)
def test_outsignal_dp_manifest_signals(self):
dpal = DPAL(self.dummy_app)
suffix = "unittest"
dpal.dp_translations = {"test_signal": {self.signal_spec}}
dpal.signal_names["other"]["outsignals"] = {
"test_signal",
}
result = dpal.outsignals_dp_manifest(suffix)
expected = [{"name": "unittest", "signals": [self.signal_yaml], "signal_groups": []}]
self.assertListEqual(result, expected)
def test_outsignal_dp_manifest_signal_groups(self):
dpal = DPAL(self.dummy_app)
suffix = "unittest"
test_signal = list(self.signal_spec)
test_signal[16] = "signal_group_a"
test_signal_1 = tuple(test_signal)
dpal.dp_translations = {"test_signal_1": {test_signal_1},
}
dpal.signal_names["other"]["outsignals"] = {
"test_signal_1",
}
result = dpal.outsignals_dp_manifest(suffix)
expected = [{
"name": "unittest",
"signals": [],
"signal_groups": [{"name": "signal_group_a", "signals": [self.signal_yaml]}]}]
self.assertListEqual(result, expected)
def test_cleanup_dp_manifest_signal_group(self):
dpal = DPAL(self.dummy_app)
dpal.dp_translations = {"test_signal": {self.signal_spec}}
dpal.signal_names["other"]["outsignals"] = {
"test_signal",
}
client = {
"name": "unittest",
"consumes": [{"name": "unittest", "signal_groups": []}],
"produces": [{"name": "unittest", "signals": [self.signal_yaml], "signal_groups": []}]
}
dpal.cleanup_dp_manifest(client)
expected = {"name": "unittest", "produces": [{"name": "unittest", "signals": [self.signal_yaml]}]}
self.assertDictEqual(client, expected)
def test_cleanup_dp_manifest_signals(self):
dpal = DPAL(self.dummy_app)
test_signal = list(self.signal_spec)
test_signal[16] = "signal_group_a"
test_signal_1 = tuple(test_signal)
test_signal[16] = "signal_group_b"
test_signal_2 = tuple(test_signal)
dpal.dp_translations = {"test_signal_1": {test_signal_1}, "test_signal_2": {test_signal_2}}
dpal.signal_names["other"]["insignals"] = {
"test_signal_1",
}
dpal.signal_names["other"]["outsignals"] = {
"test_signal_2",
}
client = {
"name": "unittest",
"consumes": [{"name": "unittest", "signal_groups": [{"name": "signal_group_a"}]}],
"produces": [{
"name": "unittest", "signals": [],
"signal_groups": [{"name": "signal_group_b", "signals": [self.signal_yaml]}]}]
}
dpal.cleanup_dp_manifest(client)
expected = {"name": "unittest",
"consumes": [{"name": "unittest", "signal_groups": [{"name": "signal_group_a"}]}],
"produces": [{"name": "unittest", "signal_groups": [{"name": "signal_group_b",
"signals": [self.signal_yaml]}]}]
}
self.assertDictEqual(client, expected)
def test_to_manifest_signal_groups(self):
dpal = DPAL(self.dummy_app)
client_name = "unittest"
test_signal = list(self.signal_spec)
test_signal[16] = "signal_group_a"
test_signal_1 = tuple(test_signal)
test_signal[16] = "signal_group_b"
test_signal_2 = tuple(test_signal)
dpal.dp_translations = {"test_signal_1": {test_signal_1}, "test_signal_2": {test_signal_2}}
dpal.signal_names["other"]["insignals"] = {
"test_signal_1",
}
dpal.signal_names["other"]["outsignals"] = {
"test_signal_2",
}
result = dpal.to_manifest(client_name)
expected = {
"signal_info": {
"clients": [
{
"name": "unittest",
"produces": [{
"name": "unittest",
"signal_groups": [
{
"name": "signal_group_b",
"signals": [
{
"default": 3,
"length": 4,
"name": "test_property",
"type": "Float32",
"range": {"min": 0, "max": 5},
"description": "Dummy description",
"unit": "Nm"
}, ]
}],
}],
"consumes": [{"name": "unittest", "signal_groups": [{"name": "signal_group_a"}, ]}]
}
],
"version": 0.2,
}
}
self.assertDictEqual(result, expected)
def test_to_manifest_signals(self):
dpal = DPAL(self.dummy_app)
client_name = "unittest"
dpal.dp_translations = {"test_signal": {self.signal_spec}}
dpal.signal_names["other"]["outsignals"] = {
"test_signal",
}
result = dpal.to_manifest(client_name)
expected = {
"signal_info": {
"clients": [
{
"name": "unittest",
"produces": [{
"name": "unittest",
"signals": [
{
"default": 3,
"length": 4,
"name": "test_property",
"type": "Float32",
"range": {"min": 0, "max": 5},
"description": "Dummy description",
"unit": "Nm"
}
],
}],
}
],
"version": 0.2,
}
}
self.assertDictEqual(result, expected)
def test_domain_filter(self):
dpal = DPAL(self.dummy_app)
dpal.domain_filter = ["test_domain"] # Matches self.signal_spec
client_name = "unittest"
# Modify signal_spec to not match filter
domain_position = dpal.dp_position.domain.value
property_position = dpal.dp_position.property.value
signal_spec2 = self.signal_spec[:domain_position] + \
("another_domain",) + \
self.signal_spec[domain_position+1:property_position] + \
("another_property",) + \
self.signal_spec[property_position+1:]
dpal.dp_translations = {"test_signal1": {self.signal_spec},
"test_signal2": {signal_spec2}}
dpal.signal_names["other"]["outsignals"] = {
"test_signal1",
"test_signal2",
}
result = dpal.to_manifest(client_name)
expected = {
"signal_info": {
"clients": [
{
"name": "unittest",
"produces": [{
"name": "unittest",
"signals": [self.signal_yaml]
}],
}
],
"version": 0.2,
}
}
self.assertDictEqual(result, expected)
def test_empty_manifest(self):
# If we have no matching signals, don't create a manifest
dpal = DPAL(self.dummy_app)
client_name = "unittest"
dpal.device_filter = ["no_match"]
self.assertIsNone(dpal.to_manifest(client_name))