351 lines
15 KiB
Python
351 lines
15 KiB
Python
# Copyright 2024 Volvo Car Corporation
|
|
# Licensed under Apache 2.0.
|
|
|
|
"""Unit test script for powertrain_build.zone_controller.generate_yaml."""
|
|
|
|
import copy
|
|
import os
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from powertrain_build.build_proj_config import BuildProjConfig
|
|
from powertrain_build.core import ZCCore
|
|
from powertrain_build.dids import ZCDIDs
|
|
from powertrain_build.unit_configs import UnitConfigs
|
|
from powertrain_build.zone_controller.composition_yaml import CompositionYaml
|
|
from test_data.zone_controller.test_composition_yaml import (
|
|
composition_yaml_setup,
|
|
composition_yaml,
|
|
composition_yaml_with_a2l_axis_data,
|
|
composition_yaml_with_calls_all_fields,
|
|
composition_yaml_with_calls_no_optional_fields,
|
|
composition_yaml_with_dids,
|
|
composition_yaml_with_dtcs,
|
|
composition_yaml_with_external_io,
|
|
composition_yaml_with_nvm,
|
|
)
|
|
|
|
SRC_DIR = Path(__file__).parent
|
|
|
|
|
|
class BuildProjConfigMock(BuildProjConfig):
|
|
"""Class mocking BuildProjConfig."""
|
|
name = ""
|
|
|
|
|
|
def mock_get_code_generation_config_default(item):
|
|
"""Function to mock BuildProjConfig.get_code_generation_config."""
|
|
return {
|
|
"generalAsilLevelDebug": "B",
|
|
"generalAsilLevelDependability": "B",
|
|
"generateCalibrationInterfaceFiles": False,
|
|
"useCalibrationRteMacroExpansion": False,
|
|
"generateCoreDummy": False,
|
|
"generateDummyVar": False,
|
|
"generateInterfaceHeaders": False,
|
|
"generateRteCheckpointIds": False,
|
|
"generateYamlInterfaceFile": False,
|
|
"includeAllEnums": False,
|
|
"mapToRteEnums": False,
|
|
"propagateTagName": False,
|
|
"useA2lSymbolLinks": False,
|
|
"useRteNvmStructs": False,
|
|
}[item]
|
|
|
|
|
|
def mock_get_code_generation_config_calibration_interface(item):
|
|
"""Function to mock BuildProjConfig.get_code_generation_config."""
|
|
return {
|
|
"generalAsilLevelDebug": "B",
|
|
"generalAsilLevelDependability": "B",
|
|
"generateCalibrationInterfaceFiles": True,
|
|
"useCalibrationRteMacroExpansion": False,
|
|
"generateCoreDummy": False,
|
|
"generateDummyVar": False,
|
|
"generateInterfaceHeaders": False,
|
|
"generateRteCheckpointIds": False,
|
|
"generateYamlInterfaceFile": False,
|
|
"includeAllEnums": False,
|
|
"mapToRteEnums": False,
|
|
"propagateTagName": False,
|
|
"useA2lSymbolLinks": False,
|
|
"useRteNvmStructs": False,
|
|
}[item]
|
|
|
|
|
|
def mock_get_composition_config_default(key):
|
|
"""Function to mock BuildProjConfig.get_composition_config."""
|
|
return {
|
|
"compositionArxml": "some_arxml.arxml",
|
|
"compositionName": "compositionName",
|
|
"compositionEnding": "yml",
|
|
"softwareComponentName": "testName_SC",
|
|
"softwareComponentTemplate": "ARTCSC",
|
|
"asil": "QM",
|
|
"secure": False,
|
|
"customYamlInitFunctionName": None,
|
|
"customYamlStepFunctionName": None,
|
|
"generateExternalImplementationType": True,
|
|
"includeStatic": True,
|
|
"includeShared": True,
|
|
"includeDiagnostics": True,
|
|
"includeNvm": True,
|
|
"scaleMapsAndCurves": True,
|
|
}[key]
|
|
|
|
|
|
def mock_get_composition_config_custom_names(key):
|
|
"""Function to mock BuildProjConfig.get_composition_config."""
|
|
return {
|
|
"compositionArxml": "some_arxml.arxml",
|
|
"compositionName": "compositionName",
|
|
"compositionEnding": "yml",
|
|
"softwareComponentName": "testName_SC",
|
|
"softwareComponentTemplate": "ARTCSC",
|
|
"asil": "QM",
|
|
"secure": False,
|
|
"customYamlInitFunctionName": "dummy_init",
|
|
"customYamlStepFunctionName": "dummy_step",
|
|
"generateExternalImplementationType": True,
|
|
"includeStatic": True,
|
|
"includeShared": True,
|
|
"includeDiagnostics": True,
|
|
"includeNvm": True,
|
|
"scaleMapsAndCurves": True,
|
|
}[key]
|
|
|
|
|
|
class TestCompositionYaml(unittest.TestCase):
|
|
"""Test case for testing composition_yaml."""
|
|
|
|
def setUp(self):
|
|
"""Set-up common data structures for all tests in the test case."""
|
|
self.build_cfg = MagicMock(spec_set=BuildProjConfigMock)
|
|
self.build_cfg.get_code_generation_config.side_effect = mock_get_code_generation_config_default
|
|
self.build_cfg.get_composition_config.side_effect = mock_get_composition_config_default
|
|
self.build_cfg.name = "XVC"
|
|
self.build_cfg.get_scheduler_prefix = MagicMock(return_value="prefix_")
|
|
self.build_cfg.get_src_code_dst_dir = MagicMock(
|
|
return_value=os.path.abspath("output")
|
|
)
|
|
self.build_cfg.get_units_raster_cfg = MagicMock(
|
|
return_value=({"SampleTimes": {"testRunnable": 10}})
|
|
)
|
|
|
|
self.unit_cfg = MagicMock(spec_set=UnitConfigs)
|
|
self.unit_cfg.get_per_cfg_unit_cfg.return_value = copy.deepcopy(
|
|
composition_yaml_setup.get_per_cfg_unit_cfg_return_value
|
|
)
|
|
|
|
with patch.object(ZCCore, "_get_project_dtcs", return_value=set()):
|
|
self.zc_core = ZCCore(self.build_cfg, self.unit_cfg)
|
|
|
|
with patch.object(ZCDIDs, "_get_project_dids", return_value={}):
|
|
self.zc_dids = ZCDIDs(self.build_cfg, self.unit_cfg)
|
|
|
|
self.signal_interfaces = MagicMock()
|
|
self.signal_interfaces.composition_spec = copy.deepcopy(composition_yaml_setup.composition_spec)
|
|
|
|
self.nvm_def = MagicMock()
|
|
self.nvm_def.struct_member_prefix = "e_"
|
|
|
|
self.calibration_definitions = copy.deepcopy(composition_yaml_setup.calibration_definitions)
|
|
|
|
with patch.object(
|
|
CompositionYaml,
|
|
"_get_all_calibration_definitions",
|
|
return_value=self.calibration_definitions
|
|
):
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
|
|
def test_composition_yaml(self):
|
|
"""Checking that the dict is generated correctly"""
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml.expected_result, result)
|
|
|
|
def test_composition_yaml_extra_runnable_keys(self):
|
|
"""Checking that the dict is generated correctly with extra runnable keys."""
|
|
self.signal_interfaces.composition_spec["mode_switch_points"] = ["DummyPort"]
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml.expected_extra_runnable_keys_result, result)
|
|
|
|
def test_composition_yaml_with_custom_names(self):
|
|
"""Checking that the dict is generated correctly with custom names."""
|
|
self.build_cfg.get_composition_config.side_effect = mock_get_composition_config_custom_names
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml.expected_custom_names_result, result)
|
|
|
|
def test_composition_yaml_with_calibration(self):
|
|
"""Checking that the dict is generated correctly including calibration data,
|
|
setting generateCalibrationInterfaceFiles to true."""
|
|
self.build_cfg.get_code_generation_config.side_effect = mock_get_code_generation_config_calibration_interface
|
|
with patch.object(
|
|
CompositionYaml,
|
|
"_get_all_calibration_definitions",
|
|
return_value=self.calibration_definitions
|
|
):
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml.expected_cal_result, result)
|
|
|
|
def test_composition_yaml_with_calibration_and_rte_macro(self):
|
|
"""Checking that the dict is generated correctly including calibration data,
|
|
setting both generateCalibrationInterfaceFiles and useCalibrationRteMacroExpansion to true (sort of)."""
|
|
self.build_cfg.get_code_generation_config = MagicMock(return_value=True)
|
|
with patch.object(
|
|
CompositionYaml,
|
|
"_get_all_calibration_definitions",
|
|
return_value=self.calibration_definitions
|
|
):
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml.expected_result, result)
|
|
|
|
def test_composition_yaml_with_a2l_axis_data(self):
|
|
"""Checking that the dict is generated correctly, including a2l axis data."""
|
|
self.unit_cfg.get_per_cfg_unit_cfg.return_value = \
|
|
composition_yaml_with_a2l_axis_data.get_per_cfg_unit_cfg_return_value
|
|
calibration_definitions = \
|
|
self.calibration_definitions + composition_yaml_with_a2l_axis_data.calibration_definitions
|
|
with patch.object(CompositionYaml, "_get_all_calibration_definitions", return_value=calibration_definitions):
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg,
|
|
self.signal_interfaces,
|
|
self.unit_cfg,
|
|
self.zc_core,
|
|
self.zc_dids,
|
|
self.nvm_def,
|
|
composition_yaml_with_a2l_axis_data.a2l_axis_data,
|
|
{}
|
|
)
|
|
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml_with_a2l_axis_data.expected_result, result)
|
|
|
|
def test_composition_yaml_with_calls_all_fields(self):
|
|
"""Checking that the dict is generated correctly, with calls including all fields."""
|
|
self.signal_interfaces.composition_spec["calls"] = {
|
|
"CallOne": {
|
|
"interface": "InterfaceOne",
|
|
"direction": "IN",
|
|
"operation": "OperationOne",
|
|
"timeout": 0.1,
|
|
}
|
|
}
|
|
with patch.object(
|
|
CompositionYaml,
|
|
"_get_all_calibration_definitions",
|
|
return_value=self.calibration_definitions
|
|
):
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml_with_calls_all_fields.expected_result, result)
|
|
|
|
def test_composition_yaml_with_calls_no_optional_fields(self):
|
|
"""Checking that the dict is generated correctly, with calls without optional fields."""
|
|
self.signal_interfaces.composition_spec["calls"] = {
|
|
"CallOne": {
|
|
"direction": "IN",
|
|
"operation": "OperationOne",
|
|
}
|
|
}
|
|
with patch.object(
|
|
CompositionYaml,
|
|
"_get_all_calibration_definitions",
|
|
return_value=self.calibration_definitions
|
|
):
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml_with_calls_no_optional_fields.expected_result, result)
|
|
|
|
def test_composition_yaml_with_dids(self):
|
|
"""Checking that the dict is generated correctly, with DIDs."""
|
|
self.zc_dids.project_dids = {"DID1": {"type": "UInt8"}}
|
|
self.signal_interfaces.composition_spec["diagnostics"] = composition_yaml_with_dids.diagnostics
|
|
with patch.object(
|
|
CompositionYaml,
|
|
"_get_all_calibration_definitions",
|
|
return_value=self.calibration_definitions
|
|
):
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml_with_dids.expected_result, result)
|
|
|
|
def test_composition_yaml_with_dtcs(self):
|
|
"""Checking that the dict is generated correctly, with DTCs."""
|
|
self.zc_core.project_dtcs = {"DTC1"}
|
|
self.signal_interfaces.composition_spec["diagnostics"] = composition_yaml_with_dtcs.diagnostics
|
|
with patch.object(
|
|
CompositionYaml,
|
|
"_get_all_calibration_definitions",
|
|
return_value=self.calibration_definitions
|
|
):
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml_with_dtcs.expected_result, result)
|
|
|
|
def test_composition_yaml_with_nvm(self):
|
|
"""Checking that the dict is generated correctly, with NVM."""
|
|
self.nvm_def.nvm_definitions = composition_yaml_with_nvm.project_nvm_definitions
|
|
self.nvm_def.project_nvm_definitions = {
|
|
f"prefix_{item['name']}": item for item in self.nvm_def.nvm_definitions
|
|
}
|
|
self.signal_interfaces.composition_spec["nv-needs"] = composition_yaml_with_nvm.yaml_nvm_definitions
|
|
with patch.object(
|
|
CompositionYaml,
|
|
"_get_all_calibration_definitions",
|
|
return_value=self.calibration_definitions
|
|
):
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml_with_nvm.expected_result, result)
|
|
|
|
def test_composition_yaml_with_external_io(self):
|
|
"""Checking that the dict is generated correctly, with external IO."""
|
|
self.signal_interfaces.get_external_io.return_value = composition_yaml_with_external_io.external_io
|
|
with patch.object(
|
|
CompositionYaml,
|
|
"_get_all_calibration_definitions",
|
|
return_value=self.calibration_definitions
|
|
):
|
|
self.composition_yaml = CompositionYaml(
|
|
self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {}
|
|
)
|
|
result = self.composition_yaml.gather_yaml_info()
|
|
self.assertDictEqual(composition_yaml_with_external_io.expected_result, result)
|
|
|
|
def test_get_init_values_expecting_failure(self):
|
|
"""Test CompositionYaml.get_init_values with a non-existing calibration definition."""
|
|
self.composition_yaml.clear_log()
|
|
json_variables = {"signal_name": "dummy"}
|
|
c_definitions = ["CVC_CAL Float32 signal_name_other = 1.F; "]
|
|
with patch.object(CompositionYaml, "_get_all_calibration_definitions", return_value=c_definitions):
|
|
init_values = self.composition_yaml.get_init_values(json_variables)
|
|
logged_problems = self.composition_yaml.get_problems()
|
|
self.assertEqual(init_values, {})
|
|
self.assertEqual(logged_problems["warning"], [])
|
|
self.assertEqual(logged_problems["critical"], ["Missing init values for calibration variables:\nsignal_name"])
|