From 7dc2e36c2b103ef1fe5910ec056e5b0072dc7e11 Mon Sep 17 00:00:00 2001 From: Henrik Wahlqvist Date: Wed, 27 Aug 2025 10:04:52 +0200 Subject: [PATCH] Fixed bug in composition_yaml.py Introduced in c77107375fa0db4a2e6f510052e05170f2d72b4f Limited the xml string before extending it with longer xml character replacements Change-Id: I45a39fdf6454746c3df64a0c6a9410b24c0f94a1 --- .../zone_controller/composition_yaml.py | 41 ++++++++++--------- .../test_composition_yaml/prepare_for_xml.py | 38 +++++++++++++++++ .../zone_controller/test_composition_yaml.py | 20 +++++++++ 3 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 test_data/zone_controller/test_composition_yaml/prepare_for_xml.py diff --git a/powertrain_build/zone_controller/composition_yaml.py b/powertrain_build/zone_controller/composition_yaml.py index a2b8e3f..9d3f682 100644 --- a/powertrain_build/zone_controller/composition_yaml.py +++ b/powertrain_build/zone_controller/composition_yaml.py @@ -88,30 +88,33 @@ class CompositionYaml(ProblemLogger): return float(value_str[:-1]) return int(value_str) - @staticmethod - def _prepare_for_xml(string): + def _prepare_for_xml(self, signal_name, string): """Prepare a string for XML serialization. Args: + signal_name (str): The name of the signal. string (str): The string to prepare. Returns: xml_string (str): The prepared string. """ - limited_string = string[:255] - no_line_breaks_string = "".join(limited_string.splitlines()) - xml_string = scalarstring.DoubleQuotedScalarString( - no_line_breaks_string.replace( - "&", "&" - ).replace( - "<", "<" - ).replace( - ">", ">" - ).replace( - "'", "'" - ).replace( - '"', """ - ) - ) + illegal_xml_characters = { + "&": "&", # needs to be first in list + "<": "<", + ">": ">", + "'": "'", + '"': """ + } + xml_string_tmp = "".join(string.splitlines()) + for char, replacement in illegal_xml_characters.items(): + xml_string_tmp = xml_string_tmp.replace(char, replacement) + if len(xml_string_tmp) > 255: + self.warning(f"Converted description for {signal_name} exceeds 255 characters and will be truncated.") + for replacement in illegal_xml_characters.values(): + found = xml_string_tmp.find(replacement, 255-len(replacement), 255+len(replacement)) + if found < 255 and found + len(replacement) > 255: + xml_string_tmp = xml_string_tmp[:found] + xml_string_tmp = xml_string_tmp[:255] # Since "found" is always < 255 this is safe + xml_string = scalarstring.DoubleQuotedScalarString(xml_string_tmp) return xml_string def get_base_data_types(self): @@ -703,7 +706,7 @@ class CompositionYaml(ProblemLogger): "init": self.calibration_init_values.get(signal_name, max(min(0, upper), lower)), } if info["description"]: - class_info[signal_name]["longname"] = self._prepare_for_xml(info["description"]) + class_info[signal_name]["longname"] = self._prepare_for_xml(signal_name, info["description"]) return class_info, data_types if isinstance(lower, list) or isinstance(upper, list): @@ -812,6 +815,6 @@ class CompositionYaml(ProblemLogger): "init": init, } if info["description"]: - class_info[signal_name]["longname"] = self._prepare_for_xml(info["description"]) + class_info[signal_name]["longname"] = self._prepare_for_xml(signal_name, info["description"]) data_types = {**data_types, **new_data_type} return class_info, data_types diff --git a/test_data/zone_controller/test_composition_yaml/prepare_for_xml.py b/test_data/zone_controller/test_composition_yaml/prepare_for_xml.py new file mode 100644 index 0000000..4e5085f --- /dev/null +++ b/test_data/zone_controller/test_composition_yaml/prepare_for_xml.py @@ -0,0 +1,38 @@ +# Copyright 2024 Volvo Car Corporation +# Licensed under Apache 2.0. + +"""Unit test data for powertrain_build.zone_controller.composition_yaml.""" + +description = "This is a valid description." +illegal_description = "This is a description with 'single' and \"double\" quotes & some XML characters." +fixed_illegal_description = "This is a description with 'single' and "double" quotes & some <illegal> XML characters." +long_description = ( + "This is a long description that spans multiple lines. \n" + "It does not contain any single and double quotes or any other illegal XML characters. \n" + "Adding a lot of random text to make it longer than 255 characters... \n" + "dummy-text, dummy-text, dummy-text, dummy-text, dummy-text, dummy-text, dummy-text, dummy-text..." +) +long_illegal_description = ( + "This is a long description that spans multiple lines. \n" + "It can contain 'single' and \"double\" quotes, & XML characters. \n" + "Adding a lot of random text to make it longer than 255 characters... \n" + "dummy-text, dummy-text, dummy-text, dummy-text, dummy-text, dummy-text, dummy-text, dummy-text..." +) +fixed_long_illegal_description = ( + "This is a long description that spans multiple lines. " + "It can contain 'single' and "double" quotes, & <illegal> XML characters. " + "Adding a lot of random text to make it longer than 255 characters... " + "dummy-text, dummy-text, dummy" +) +long_illegal_description_truncated_tag = ( + "This is a long description that spans multiple lines. \n" + "It can contain 'single' and \"double\" quotes, & XML characters. \n" + "Adding a lot of random text to make it longer than 255 characters... \n" + "dummy-text & dummy-text & dummy-text & dummy-text & dummy-text & dummy-text & dummy-text & dummy-text..." +) +fixed_long_illegal_description_truncated_tag = ( + "This is a long description that spans multiple lines. " + "It can contain 'single' and "double" quotes, & <illegal> XML characters. " + "Adding a lot of random text to make it longer than 255 characters... " + "dummy-text & dummy-text " +) diff --git a/tests/zone_controller/test_composition_yaml.py b/tests/zone_controller/test_composition_yaml.py index 01daacc..fa6e408 100644 --- a/tests/zone_controller/test_composition_yaml.py +++ b/tests/zone_controller/test_composition_yaml.py @@ -24,6 +24,7 @@ from test_data.zone_controller.test_composition_yaml import ( composition_yaml_with_dtcs, composition_yaml_with_external_io, composition_yaml_with_nvm, + prepare_for_xml, ) SRC_DIR = Path(__file__).parent @@ -161,6 +162,25 @@ class TestCompositionYaml(unittest.TestCase): self.build_cfg, self.signal_interfaces, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}, {} ) + def test_prepare_for_xml(self): + """Checking that the XML preparation is done correctly.""" + # Normal + result = self.composition_yaml._prepare_for_xml("dummy", prepare_for_xml.description) + self.assertEqual(result, prepare_for_xml.description) + # Illegal + result = self.composition_yaml._prepare_for_xml("dummy", prepare_for_xml.illegal_description) + self.assertEqual(result, prepare_for_xml.fixed_illegal_description) + # Long normal + result = self.composition_yaml._prepare_for_xml("dummy", prepare_for_xml.long_description) + expected = "".join(prepare_for_xml.long_description.splitlines())[:255] + self.assertEqual(result, expected) + # Long illegal + result = self.composition_yaml._prepare_for_xml("dummy", prepare_for_xml.long_illegal_description) + self.assertEqual(result, prepare_for_xml.fixed_long_illegal_description) + # Long illegal description which is truncated in the middle of an XML tag + result = self.composition_yaml._prepare_for_xml("dummy", prepare_for_xml.long_illegal_description_truncated_tag) + self.assertEqual(result, prepare_for_xml.fixed_long_illegal_description_truncated_tag) + def test_composition_yaml(self): """Checking that the dict is generated correctly""" result = self.composition_yaml.gather_yaml_info()