Files
powertrain-build/tests/powertrain_build/test_build.py
Henrik Wahlqvist 05cd4a2b81 Don't generate VcDebug files if there are no variables
Change-Id: I57c0d04ecaa08164e4654409ed40cfd01b63faef
2025-07-09 10:21:59 +02:00

348 lines
15 KiB
Python

# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
"""Unit test script for powertrain_build.build module."""
import os
import logging
import unittest
from unittest.mock import MagicMock, patch, PropertyMock
from pathlib import Path
from powertrain_build import build_defs
from powertrain_build.lib import helper_functions
from powertrain_build.problem_logger import ProblemLogger
from powertrain_build.build_proj_config import BuildProjConfig
from powertrain_build.unit_configs import UnitConfigs
from powertrain_build.signal_interfaces import CsvSignalInterfaces
from powertrain_build.core import Core
from powertrain_build import build
SRC_DIR = Path(__file__).parent
def remove(*files):
"""Try to remove file."""
for file_ in files:
try:
os.remove(file_)
except FileNotFoundError:
pass
def exists(*files):
"""Check if file exists."""
for file_ in files:
if not os.path.isfile(file_):
raise AssertionError(f'File {file_} does not exist.')
return True
class TestBuild(unittest.TestCase):
"""Test case for testing the build module."""
def setUp(self):
"""Set-up common data structures for all tests in the test case."""
output_folder = Path(SRC_DIR, 'output')
cnfg_files_folder = Path(SRC_DIR, 'cnfg_files')
common_src_dir = str(Path(SRC_DIR, 'common_src_files'))
prj_cnf_dir = str(cnfg_files_folder)
prj_out_dir = str(output_folder)
prj_src_dir = str(Path(SRC_DIR, 'src_files'))
projdir = cnfg_files_folder.resolve()
helper_functions.create_dir(Path(SRC_DIR, 'output'))
helper_functions.create_dir(Path(cnfg_files_folder, 'output'))
self.build_cfg = MagicMock(spec_set=BuildProjConfig(Path(cnfg_files_folder, 'ProjectCfg.json')))
self.build_cfg.get_prj_cfg_dir = MagicMock(return_value=prj_cnf_dir)
self.build_cfg.get_prj_config = MagicMock(return_value='CFG1')
self.build_cfg.get_reports_dst_dir = MagicMock(return_value=prj_out_dir)
self.build_cfg.get_src_code_dst_dir = MagicMock(return_value=prj_out_dir)
self.build_cfg.get_unit_cfg_deliv_dir = MagicMock(return_value=prj_out_dir)
self.build_cfg.get_did_cfg_file_name = MagicMock(return_value='DIDIds_FullRange')
self.build_cfg.get_car_com_dst = MagicMock(return_value=os.path.join(prj_out_dir, 'CarCom_DIDDefs.csv'))
self.build_cfg.get_core_dummy_name = MagicMock(return_value=prj_out_dir + '/VcCoreDummy')
self.build_cfg.get_nvm_defs = MagicMock(return_value={
'fileName': 'vcc_nvm_struct',
"baseNvmStructs": "nvm_structs_ref_empty.json"
})
self.build_cfg.get_common_src_dir = MagicMock(return_value=common_src_dir)
self.build_cfg.get_unit_src_dirs = MagicMock(return_value={'mocked': prj_src_dir})
self.build_cfg.get_unit_cfg_dirs = MagicMock(return_value={'mocked': prj_cnf_dir})
self.build_cfg.get_a2l_name = MagicMock(return_value='merged.a2l')
self.build_cfg.get_root_dir = MagicMock(return_value=projdir)
self.build_cfg.get_ecu_info = MagicMock(return_value=('Denso', 'G2'))
self.unit_cfg = MagicMock(spec_set=UnitConfigs)
type(self.unit_cfg).base_types_headers = '#include tl_basetypes.h\n'
type(self.unit_cfg).code_generators = 'target_link'
@staticmethod
def test_setup_logging():
"""Check that init_logger is called."""
log = logging.getLogger()
log_dst_dir = str(Path(SRC_DIR, 'output'))
problem_logger = MagicMock(spec_set=ProblemLogger)
build.setup_logging(log_dst_dir, problem_logger, debug=True, quiet=True)
problem_logger.init_logger.assert_called_once_with(log)
def test_check_interfaces(self):
"""Check that interface check is run."""
signal_if = MagicMock(spec_set=CsvSignalInterfaces)
signal_if.check_config = MagicMock(return_value={'sigs': {'ext': {}, 'int': {}}})
build.check_interfaces(self.build_cfg, signal_if)
self.build_cfg.get_reports_dst_dir.assert_called_once()
signal_if.check_config.called_once()
def test_interface_report(self):
"""Check that interface report is generated."""
signal_if = MagicMock(spec_set=CsvSignalInterfaces)
signal_if.check_config = MagicMock(return_value={'sigs': {'ext': {}, 'int': {}}})
filename = str(Path(SRC_DIR, 'output', 'SigIf.html'))
remove(filename)
build.interface_report(self.build_cfg, self.unit_cfg, signal_if)
self.build_cfg.get_reports_dst_dir.assert_called_once()
exists(filename)
def test_generate_did_files(self):
"""Check that interface report is generated."""
output_folder = Path(SRC_DIR, 'output')
filename = str(Path(output_folder, 'VcDIDDefinition'))
cfile = filename + '.c'
hfile = filename + '.h'
csvfile = str(Path(output_folder, 'CarCom_DIDDefs.csv'))
remove(cfile, hfile, csvfile)
build.generate_did_files(self.build_cfg, self.unit_cfg)
self.build_cfg.get_src_code_dst_dir.assert_called_once()
self.build_cfg.get_did_cfg_file_name.assert_called()
self.build_cfg.get_prj_cfg_dir.assert_called()
exists(cfile, hfile, csvfile)
def test_generate_core_dummy_denso(self):
"""Check that core dummy files are generated."""
self.build_cfg.get_ecu_info = MagicMock(return_value=('Denso', 'G2'))
core = MagicMock(spec_set=Core)
core.get_current_core_config = MagicMock()
filename = str(Path(SRC_DIR, 'output', 'VcCoreDummy'))
cfile = filename + '.c'
hfile = filename + '.h'
remove(cfile, hfile)
build.generate_core_dummy(self.build_cfg, core, self.unit_cfg)
core.get_current_core_config.assert_called_once()
self.build_cfg.get_core_dummy_name.assert_called_once()
self.build_cfg.get_ecu_info.assert_called_once()
exists(cfile, hfile)
def test_generate_core_dummy_rb(self):
"""Check that core dummy files are generated."""
self.build_cfg.get_ecu_info = MagicMock(return_value=('RB', 'xx'))
core = MagicMock(spec_set=Core)
core.get_current_core_config = MagicMock()
filename = str(Path(SRC_DIR, 'output', 'VcCoreDummy'))
cfile = filename + '.c'
hfile = filename + '.h'
remove(cfile, hfile)
build.generate_core_dummy(self.build_cfg, core, self.unit_cfg)
core.get_current_core_config.assert_called_once()
self.build_cfg.get_core_dummy_name.assert_called_once()
self.build_cfg.get_ecu_info.assert_called_once()
exists(cfile, hfile)
def test_generate_core_dummy_other(self):
"""Check that core dummy files are generated."""
self.build_cfg.get_ecu_info = MagicMock(return_value=('Other', 'yy'))
core = MagicMock(spec_set=Core)
core.get_current_core_config = MagicMock()
self.assertRaises(ValueError, build.generate_core_dummy, self.build_cfg, core, self.unit_cfg)
def test_generate_external_var(self):
"""Check that ExtVar files are generated."""
self.build_cfg.has_yaml_interface = False
nrm_dict = {
'EMS-Output': {},
'EMS-Input': {
'DummyIn': {
'type': 'Float32',
'min': 0,
'max': 100,
'unit': 'Nm',
'description': 'Dummy',
'init': 0,
'element_index': 5,
'IOType': 'x'
}
},
'LIN-Output': {},
'LIN-Input': {},
'CAN-Output': {},
'CAN-Input': {},
'Private CAN-Output': {},
'Private CAN-Input': {}
}
dep_dict = {
'EMS-Output': {},
'EMS-Input': {
'DummyInSafe': {
'type': 'Float32',
'min': 0,
'max': 100,
'unit': 'Nm',
'description': 'Safe dummy',
'init': 0,
'element_index': 5,
'IOType': 's'
}
},
'LIN-Output': {},
'LIN-Input': {},
'CAN-Output': {},
'CAN-Input': {},
'Private CAN-Output': {},
'Private CAN-Input': {}
}
sec_dict = {}
filepath = str(Path(SRC_DIR, 'output'))
files = [
filepath + '/VcExtVar.c',
filepath + '/VcExtVar.a2l',
filepath + '/VcExtVarSafe.c',
filepath + '/VcExtVarSafe.a2l'
]
remove(*files)
with patch('powertrain_build.user_defined_types.UserDefinedTypes') as udt_mock:
udt_mock.return_value.common_header_files = PropertyMock(return_value=[])
build.generate_external_var(
self.build_cfg, self.unit_cfg, udt_mock, build_defs.ASIL_B, nrm_dict, dep_dict, sec_dict
)
self.build_cfg.get_src_code_dst_dir.assert_called()
exists(*files)
self.assertFalse(os.path.isfile(f"{filepath}/VcExtVarSecure.c"))
def test_generate_nvm_def(self):
"""Check that NVM files are generated."""
unit_cfg = MagicMock(spec_set=UnitConfigs)
type(unit_cfg).base_types_headers = ''
no_nvm_a2l = False
filepath = str(Path(SRC_DIR, 'output'))
files = [
filepath + '/vcc_nvm_struct.c',
filepath + '/vcc_nvm_struct.h',
filepath + '/vcc_nvm_struct.a2l']
remove(*files)
build.generate_nvm_def(self.build_cfg, unit_cfg, no_nvm_a2l)
self.build_cfg.get_src_code_dst_dir.assert_called()
unit_cfg.get_per_cfg_unit_cfg.assert_called()
exists(*files)
@unittest.mock.patch('shutil.copy2')
def test_copy_unit_src_to_src_out(self, mock_copy):
"""Check that source files are copied."""
build.copy_unit_src_to_src_out(self.build_cfg)
self.build_cfg.get_unit_src_dirs.assert_called()
self.build_cfg.get_src_code_dst_dir.assert_called()
mock_copy.assert_called()
@unittest.mock.patch('shutil.copy2')
def test_copy_unit_cfgs_to_output(self, mock_copy):
"""Check that unit-configs are copied."""
build.copy_unit_cfgs_to_output(self.build_cfg)
self.build_cfg.get_unit_cfg_deliv_dir.assert_called()
self.build_cfg.get_unit_cfg_dirs.assert_called()
mock_copy.assert_called()
def test_merge_a2l_files(self):
"""Check that a2l-files are merged."""
unit_cfg = MagicMock(spec_set=UnitConfigs)
filepath = str(Path(SRC_DIR, 'output'))
remove(filepath + '/merged.a2l')
build.merge_a2l_files(self.build_cfg, unit_cfg)
self.build_cfg.get_unit_src_dirs.assert_called()
self.build_cfg.get_src_code_dst_dir.assert_called()
self.build_cfg.get_a2l_name.assert_called()
unit_cfg.get_per_unit_cfg.assert_called()
exists(filepath + '/merged.a2l')
def test_build_no_cfg(self):
"""Check that main entrypoint raises exception for missing config."""
prj_root = str(Path(SRC_DIR, 'cnfg_files', 'NonexistentProjectCfg.json'))
with unittest.mock.patch('powertrain_build.build.find_all_project_configs') as mock:
mock.return_value = [prj_root]
self.assertRaises(FileNotFoundError, build.build, prj_root, quiet=True)
@staticmethod
@unittest.mock.patch('powertrain_build.unit_configs.UnitConfigs.get_per_unit_cfg')
@unittest.mock.patch('powertrain_build.build.find_all_project_configs')
def test_build(mock_find_all_project_configs, mock_get_per_unit_cfg):
"""Check that main entrypoint can be run without exceptions."""
prj_root = str(Path(SRC_DIR, 'cnfg_files', 'ProjectCfg.json'))
output = str(Path(SRC_DIR, 'output', 'logs', 'build.log'))
remove(output)
mock_find_all_project_configs.return_value = [prj_root]
mock_get_per_unit_cfg.return_value = {'VcScBCoord': {}, 'VcScCVehMtn': {}, 'VcScFeh': {}}
build.build(prj_root,
interface=True,
core_dummy=True,
no_abort=False,
debug=True,
quiet=True)
exists(output)
@staticmethod
@unittest.mock.patch('powertrain_build.unit_configs.UnitConfigs.get_per_unit_cfg')
@unittest.mock.patch('powertrain_build.build.find_all_project_configs')
def test_build_ec(mock_find_all_project_configs, mock_get_per_unit_cfg):
"""Check that main entrypoint can be run without exceptions."""
prj_root = str(Path(SRC_DIR, 'cnfg_files', 'ProjectCfg.json'))
output = str(Path(SRC_DIR, 'output', 'logs', 'build.log'))
remove(output)
mock_find_all_project_configs.return_value = [prj_root]
mock_get_per_unit_cfg.return_value = {
'VcScBCoord': {'code_generator': 'embedded_coder'},
'VcScCVehMtn': {'code_generator': 'embedded_coder'},
'VcScFeh': {'code_generator': 'embedded_coder'}}
build.build(prj_root,
interface=True,
core_dummy=True,
no_abort=False,
debug=True,
quiet=True)
exists(output)
@staticmethod
@unittest.mock.patch('powertrain_build.unit_configs.UnitConfigs.get_per_unit_cfg')
@unittest.mock.patch('powertrain_build.build.find_all_project_configs')
def test_build_mixed_ec_tl(mock_find_all_project_configs, mock_get_per_unit_cfg):
"""Check that main entrypoint can be run without exceptions."""
prj_root = str(Path(SRC_DIR, 'cnfg_files', 'ProjectCfg.json'))
output = str(Path(SRC_DIR, 'output', 'logs', 'build.log'))
remove(output)
mock_find_all_project_configs.return_value = [prj_root]
mock_get_per_unit_cfg.return_value = {
'VcScBCoord': {'code_generator': 'embedded_coder'},
'VcScCVehMtn': {'code_generator': 'target_link'},
'VcScFeh': {}}
build.build(prj_root,
interface=True,
core_dummy=True,
no_abort=False,
debug=True,
quiet=True)
exists(output)
@staticmethod
@unittest.mock.patch('powertrain_build.unit_configs.UnitConfigs.get_per_unit_cfg')
@unittest.mock.patch('powertrain_build.build.find_all_project_configs')
def test_build_no_abort(mock_find_all_project_configs, mock_get_per_unit_cfg):
"""Check that main entrypoint can be run without exceptions with no abort set to True."""
prj_root = str(Path(SRC_DIR, 'cnfg_files', 'ProjectCfg.json'))
output = str(Path(SRC_DIR, 'output', 'logs', 'build.log'))
remove(output)
mock_find_all_project_configs.return_value = [prj_root]
mock_get_per_unit_cfg.return_value = {'VcScBCoord': {}, 'VcScCVehMtn': {}, 'VcScFeh': {}}
build.build(prj_root,
interface=True,
core_dummy=True,
no_abort=True,
debug=True,
quiet=True)
exists(output)