From c96b52faf29ac63610e1b5efaf4a95d52e8f3a31 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Fri, 26 Sep 2025 11:27:17 +0200 Subject: [PATCH 1/4] Add the option to enforce test case prefix from command line We need to provide option to customize the test case prefix which was now hardcoded to SWUTEST_filename, but we want to have that set externally. --- mlx/unity2junit/unity2junit.py | 11 ++++++++--- tests/unity_parsing_test.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/mlx/unity2junit/unity2junit.py b/mlx/unity2junit/unity2junit.py index 87dccd5..47e27a7 100755 --- a/mlx/unity2junit/unity2junit.py +++ b/mlx/unity2junit/unity2junit.py @@ -14,7 +14,7 @@ class Unity2Junit: """Converts a Unity test output log to a JUnit XML report.""" - def __init__(self, log_file, output_file): + def __init__(self, log_file, output_file, tc_prefix=""): self.log_file = log_file self.output_file = output_file self.test_cases = [] @@ -22,6 +22,7 @@ def __init__(self, log_file, output_file): self.total_tests = 0 self.failures = 0 self.skipped = 0 + self.test_case_prefix = tc_prefix def parse_unity_output(self): """Parses the Unity log file and populates test case data.""" @@ -36,8 +37,11 @@ def parse_unity_output(self): filename = os.path.basename(file_path).replace("utest_", "").split('.')[0].upper() self.default_suite_name = filename # Set the default testsuite name + if not self.test_case_prefix: + self.test_case_prefix = f"SWUTEST_{filename}-" + # Modify the test name: replace the underscore between SWUTEST_ and the next part with a hyphen - formatted_test_name = f"SWUTEST_{filename}-{test_name.upper()}" + formatted_test_name = f"{self.test_case_prefix}{test_name.upper()}" test_case = { "name": formatted_test_name, @@ -97,9 +101,10 @@ def main(): parser.add_argument("log_file", help="Path to the Unity test output log file.") parser.add_argument("output_file", help="Path to the output JUnit XML file.") parser.add_argument("--version", "-v", action="version", version=f"%(prog)s {__version__}") + parser.add_argument("--tc-prefix", help="Prefix to add to each test case name.", default="") args = parser.parse_args() - converter = Unity2Junit(args.log_file, args.output_file) + converter = Unity2Junit(args.log_file, args.output_file, tc_prefix=args.tc_prefix) converter.convert() diff --git a/tests/unity_parsing_test.py b/tests/unity_parsing_test.py index 70ff21b..45708ca 100644 --- a/tests/unity_parsing_test.py +++ b/tests/unity_parsing_test.py @@ -142,6 +142,36 @@ def test_init_runner_output(self): generated_xml = tmp_output_file.readlines() self.assertListEqual(generated_xml, expected_xml) + def test_force_test_case_prefix(self): + ''' Verify that when a prefix is forced, it is used as the testsuite name.''' + with tempfile.NamedTemporaryFile(mode='w', delete=True) as tmp_output_file: + converter = Unity2Junit(TEST_IN_DIR / 'utest_Init_Runner.log', tmp_output_file.name, + tc_prefix="FORCED_PREFIX-") + converter.parse_unity_output() + test_cases = converter.test_cases + + expected_test_cases_Init_Runner = {} + expected_test_cases_Init_Runner['classname'] = [ + 'INIT.FORCED_PREFIX-TEST_INIT_SUCCESS', 'INIT.FORCED_PREFIX-TEST_INIT_WRONG_EEPROM_VERSION', + 'INIT.FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS', 'INIT.FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS2', + 'INIT.FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS3'] + expected_test_cases_Init_Runner['line'] = ['49', '124', '135', '145', '163'] + expected_test_cases_Init_Runner['name'] = ['FORCED_PREFIX-TEST_INIT_SUCCESS', + 'FORCED_PREFIX-TEST_INIT_WRONG_EEPROM_VERSION', + 'FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS', + 'FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS2', + 'FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS3'] + + for tc in test_cases: + # Find some smart way to check the test case class, name and line number + self.assertEqual(tc['classname'], expected_test_cases_Init_Runner['classname'].pop(0)) + self.assertEqual(tc['line'], expected_test_cases_Init_Runner['line'].pop(0)) + self.assertEqual(tc['name'], expected_test_cases_Init_Runner['name'].pop(0)) + self.assertEqual(tc['result'], 'PASS') + + self.assertEqual(tc['file'], 'unit_test/utest_Init.c') + self.assertEqual(tc['suite'], 'INIT') # The suite in test cases remains the same + if __name__ == '__main__': unittest.main() From bb9fd27b761769e344f40fbd52378d09ba873d5d Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Fri, 26 Sep 2025 11:32:30 +0200 Subject: [PATCH 2/4] Add unit test when we ensure that also the no filename test case gets the test case prefix enforced --- tests/unity_parsing_test.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/unity_parsing_test.py b/tests/unity_parsing_test.py index 45708ca..3684bdc 100644 --- a/tests/unity_parsing_test.py +++ b/tests/unity_parsing_test.py @@ -172,6 +172,36 @@ def test_force_test_case_prefix(self): self.assertEqual(tc['file'], 'unit_test/utest_Init.c') self.assertEqual(tc['suite'], 'INIT') # The suite in test cases remains the same + def test_force_test_case_prefix_noname(self): + ''' Verify that when a prefix is forced, it is used as the testsuite name even when utest.c is used.''' + with tempfile.NamedTemporaryFile(mode='w', delete=True) as tmp_output_file: + converter = Unity2Junit(TEST_IN_DIR / 'utest_Noname_Runner.log', tmp_output_file.name, + tc_prefix="FORCED_PREFIX-") + converter.parse_unity_output() + test_cases = converter.test_cases + + expected_test_cases_Noname_Runner = {} + expected_test_cases_Noname_Runner['classname'] = [ + 'UTEST.FORCED_PREFIX-TEST_INIT_SUCCESS', 'UTEST.FORCED_PREFIX-TEST_INIT_WRONG_EEPROM_VERSION', + 'UTEST.FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS', 'UTEST.FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS2', + 'UTEST.FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS3'] + expected_test_cases_Noname_Runner['line'] = ['49', '124', '135', '145', '163'] + expected_test_cases_Noname_Runner['name'] = ['FORCED_PREFIX-TEST_INIT_SUCCESS', + 'FORCED_PREFIX-TEST_INIT_WRONG_EEPROM_VERSION', + 'FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS', + 'FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS2', + 'FORCED_PREFIX-TEST_INIT_I2C_READ_FAILS3'] + + for tc in test_cases: + # Find some smart way to check the test case class, name and line number + self.assertEqual(tc['classname'], expected_test_cases_Noname_Runner['classname'].pop(0)) + self.assertEqual(tc['line'], expected_test_cases_Noname_Runner['line'].pop(0)) + self.assertEqual(tc['name'], expected_test_cases_Noname_Runner['name'].pop(0)) + self.assertEqual(tc['result'], 'PASS') + + self.assertEqual(tc['file'], 'unit_test/utest.c') + self.assertEqual(tc['suite'], 'UTEST') # The suite in test cases remains the same + if __name__ == '__main__': unittest.main() From fdb6e0b8655c4e36e76df33c944581c4d85a41ea Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Fri, 26 Sep 2025 11:39:19 +0200 Subject: [PATCH 3/4] Add option to have an empty test case prefix --- mlx/unity2junit/unity2junit.py | 6 +++--- tests/unity_parsing_test.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/mlx/unity2junit/unity2junit.py b/mlx/unity2junit/unity2junit.py index 47e27a7..ae3b418 100755 --- a/mlx/unity2junit/unity2junit.py +++ b/mlx/unity2junit/unity2junit.py @@ -14,7 +14,7 @@ class Unity2Junit: """Converts a Unity test output log to a JUnit XML report.""" - def __init__(self, log_file, output_file, tc_prefix=""): + def __init__(self, log_file, output_file, tc_prefix=None): self.log_file = log_file self.output_file = output_file self.test_cases = [] @@ -37,7 +37,7 @@ def parse_unity_output(self): filename = os.path.basename(file_path).replace("utest_", "").split('.')[0].upper() self.default_suite_name = filename # Set the default testsuite name - if not self.test_case_prefix: + if self.test_case_prefix is None: self.test_case_prefix = f"SWUTEST_{filename}-" # Modify the test name: replace the underscore between SWUTEST_ and the next part with a hyphen @@ -101,7 +101,7 @@ def main(): parser.add_argument("log_file", help="Path to the Unity test output log file.") parser.add_argument("output_file", help="Path to the output JUnit XML file.") parser.add_argument("--version", "-v", action="version", version=f"%(prog)s {__version__}") - parser.add_argument("--tc-prefix", help="Prefix to add to each test case name.", default="") + parser.add_argument("--tc-prefix", help="Prefix to add to each test case name.", default=None) args = parser.parse_args() converter = Unity2Junit(args.log_file, args.output_file, tc_prefix=args.tc_prefix) diff --git a/tests/unity_parsing_test.py b/tests/unity_parsing_test.py index 3684bdc..99ab814 100644 --- a/tests/unity_parsing_test.py +++ b/tests/unity_parsing_test.py @@ -202,6 +202,36 @@ def test_force_test_case_prefix_noname(self): self.assertEqual(tc['file'], 'unit_test/utest.c') self.assertEqual(tc['suite'], 'UTEST') # The suite in test cases remains the same + def test_force_test_case_prefix_empty(self): + ''' Verify that when an empty prefix is forced, the default prefix is used as the testsuite name.''' + with tempfile.NamedTemporaryFile(mode='w', delete=True) as tmp_output_file: + converter = Unity2Junit(TEST_IN_DIR / 'utest_Init_Runner.log', tmp_output_file.name, + tc_prefix="") + converter.parse_unity_output() + test_cases = converter.test_cases + + expected_test_cases_Init_Runner = {} + expected_test_cases_Init_Runner['classname'] = [ + 'INIT.TEST_INIT_SUCCESS', 'INIT.TEST_INIT_WRONG_EEPROM_VERSION', + 'INIT.TEST_INIT_I2C_READ_FAILS', 'INIT.TEST_INIT_I2C_READ_FAILS2', + 'INIT.TEST_INIT_I2C_READ_FAILS3'] + expected_test_cases_Init_Runner['line'] = ['49', '124', '135', '145', '163'] + expected_test_cases_Init_Runner['name'] = ['TEST_INIT_SUCCESS', + 'TEST_INIT_WRONG_EEPROM_VERSION', + 'TEST_INIT_I2C_READ_FAILS', + 'TEST_INIT_I2C_READ_FAILS2', + 'TEST_INIT_I2C_READ_FAILS3'] + + for tc in test_cases: + # Find some smart way to check the test case class, name and line number + self.assertEqual(tc['classname'], expected_test_cases_Init_Runner['classname'].pop(0)) + self.assertEqual(tc['line'], expected_test_cases_Init_Runner['line'].pop(0)) + self.assertEqual(tc['name'], expected_test_cases_Init_Runner['name'].pop(0)) + self.assertEqual(tc['result'], 'PASS') + + self.assertEqual(tc['file'], 'unit_test/utest_Init.c') + self.assertEqual(tc['suite'], 'INIT') # The suite in test cases remains the same + if __name__ == '__main__': unittest.main() From be08552f4dcc3ad5c22925fa71d8f9d52a669ffd Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Fri, 26 Sep 2025 12:06:32 +0200 Subject: [PATCH 4/4] Add also the case where there is an empty filename --- tests/unity_parsing_test.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/unity_parsing_test.py b/tests/unity_parsing_test.py index 99ab814..04f7dd8 100644 --- a/tests/unity_parsing_test.py +++ b/tests/unity_parsing_test.py @@ -232,6 +232,37 @@ def test_force_test_case_prefix_empty(self): self.assertEqual(tc['file'], 'unit_test/utest_Init.c') self.assertEqual(tc['suite'], 'INIT') # The suite in test cases remains the same + def test_force_test_case_prefix_empty_noname(self): + ''' Verify that when an empty prefix is forced, the default prefix is used as the testsuite name even when + utest.c is used.''' + with tempfile.NamedTemporaryFile(mode='w', delete=True) as tmp_output_file: + converter = Unity2Junit(TEST_IN_DIR / 'utest_Noname_Runner.log', tmp_output_file.name, + tc_prefix="") + converter.parse_unity_output() + test_cases = converter.test_cases + + expected_test_cases_Noname_Runner = {} + expected_test_cases_Noname_Runner['classname'] = [ + 'UTEST.TEST_INIT_SUCCESS', 'UTEST.TEST_INIT_WRONG_EEPROM_VERSION', + 'UTEST.TEST_INIT_I2C_READ_FAILS', 'UTEST.TEST_INIT_I2C_READ_FAILS2', + 'UTEST.TEST_INIT_I2C_READ_FAILS3'] + expected_test_cases_Noname_Runner['line'] = ['49', '124', '135', '145', '163'] + expected_test_cases_Noname_Runner['name'] = ['TEST_INIT_SUCCESS', + 'TEST_INIT_WRONG_EEPROM_VERSION', + 'TEST_INIT_I2C_READ_FAILS', + 'TEST_INIT_I2C_READ_FAILS2', + 'TEST_INIT_I2C_READ_FAILS3'] + + for tc in test_cases: + # Find some smart way to check the test case class, name and line number + self.assertEqual(tc['classname'], expected_test_cases_Noname_Runner['classname'].pop(0)) + self.assertEqual(tc['line'], expected_test_cases_Noname_Runner['line'].pop(0)) + self.assertEqual(tc['name'], expected_test_cases_Noname_Runner['name'].pop(0)) + self.assertEqual(tc['result'], 'PASS') + + self.assertEqual(tc['file'], 'unit_test/utest.c') + self.assertEqual(tc['suite'], 'UTEST') # The suite in test cases remains the same + if __name__ == '__main__': unittest.main()