powertrain-build/tests/zone_controller/test_composition_yaml.py
Henrik Wahlqvist fc03669666 Add option for supplying custom step function name
Note that this only applies to the runnable in the generated yaml file.

Change-Id: I11eea48b41801d3a0dfc479b6cc489108659bb55
2024-10-22 11:24:06 +00:00

254 lines
11 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,
)
SRC_DIR = Path(__file__).parent
class BuildProjConfigMock(BuildProjConfig):
"""Class mocking BuildProjConfig."""
name = ""
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",
"softwareComponentBase": "QM",
"customYamlInitFunctionName": None,
"customYamlStepFunctionName": None,
"generateExternalImplementationType": True,
'includeStatic': True,
'includeShared': True,
'includeDiagnostics': 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",
"softwareComponentBase": "QM",
"customYamlInitFunctionName": "dummy_init",
"customYamlStepFunctionName": "dummy_step",
"generateExternalImplementationType": True,
'includeStatic': True,
'includeShared': True,
'includeDiagnostics': 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_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.build_cfg.get_code_generation_config = MagicMock(return_value=False)
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.zc_spec = copy.deepcopy(composition_yaml_setup.zc_spec)
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.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
def test_check_unsupported_fields(self):
"""Test CompositionYaml.check_unsupported_fields."""
self.composition_yaml.warning = MagicMock()
test_data = {
"name": "sVcGpaDemo_D_BrkCtrlr",
"type": "UInt8",
"class": "CVC_DISP",
"lsb": 1,
"offset": "-",
"width": 1,
}
self.composition_yaml.check_unsupported_fields("dummy", test_data)
self.composition_yaml.warning.assert_not_called()
test_data["lsb"] = 10
self.composition_yaml.check_unsupported_fields("dummy", test_data)
self.composition_yaml.warning.assert_called_once()
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_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.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
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 (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.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml.expected_cal_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
a2l_axis_data = composition_yaml_with_a2l_axis_data.a2l_axis_data
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.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, 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.zc_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.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
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.zc_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.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
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.zc_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.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
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.zc_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.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_dtcs.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"])