diff --git a/mlx/unity2junit/unity2junit.py b/mlx/unity2junit/unity2junit.py index ae3b418..07e80ea 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=None): + def __init__(self, log_file, output_file, tc_prefix=None, suite_name=None): self.log_file = log_file self.output_file = output_file self.test_cases = [] @@ -23,6 +23,7 @@ def __init__(self, log_file, output_file, tc_prefix=None): self.failures = 0 self.skipped = 0 self.test_case_prefix = tc_prefix + self.suite_name = suite_name def parse_unity_output(self): """Parses the Unity log file and populates test case data.""" @@ -35,7 +36,6 @@ def parse_unity_output(self): # Extract filename without extension filename = os.path.basename(file_path).replace("utest_", "").split('.')[0].upper() - self.default_suite_name = filename # Set the default testsuite name if self.test_case_prefix is None: self.test_case_prefix = f"SWUTEST_{filename}-" @@ -43,13 +43,24 @@ def parse_unity_output(self): # Modify the test name: replace the underscore between SWUTEST_ and the next part with a hyphen formatted_test_name = f"{self.test_case_prefix}{test_name.upper()}" + # Determine the classname to use + if self.suite_name is None: + self.default_suite_name = filename + formatted_classname = f"{self.default_suite_name}.{formatted_test_name}" + else: + self.default_suite_name = self.suite_name + if self.suite_name == "": + formatted_classname = f"{formatted_test_name}" + else: + formatted_classname = f"{self.suite_name}.{formatted_test_name}" + test_case = { "name": formatted_test_name, - "classname": f"{filename}.{formatted_test_name}", + "classname": formatted_classname, "file": file_path.strip(), "line": line_number.strip(), "result": result.strip(), - "suite": filename + "suite": self.default_suite_name } if result.strip() == "FAIL": self.failures += 1 @@ -102,9 +113,11 @@ def main(): 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=None) + parser.add_argument("--suite-name", help="Force a specific suite name, overriding the filename rule of using \ + string after utest_ as suitename.", default=None) args = parser.parse_args() - converter = Unity2Junit(args.log_file, args.output_file, tc_prefix=args.tc_prefix) + converter = Unity2Junit(args.log_file, args.output_file, tc_prefix=args.tc_prefix, suite_name=args.suite_name) converter.convert() diff --git a/tests/unity_parsing_test.py b/tests/unity_parsing_test.py index 04f7dd8..21c7ab6 100644 --- a/tests/unity_parsing_test.py +++ b/tests/unity_parsing_test.py @@ -263,6 +263,84 @@ def test_force_test_case_prefix_empty_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_suite_name(self): + ''' Verify that when a suite name is forced, it is used.''' + with tempfile.NamedTemporaryFile(mode='w', delete=True) as tmp_output_file: + converter = Unity2Junit(TEST_IN_DIR / 'utest_Init_Runner.log', tmp_output_file.name, + suite_name="FORCED_SUITE") + converter.parse_unity_output() + test_cases = converter.test_cases + + expected_classnames = [ + 'FORCED_SUITE.SWUTEST_INIT-TEST_INIT_SUCCESS', + 'FORCED_SUITE.SWUTEST_INIT-TEST_INIT_WRONG_EEPROM_VERSION', + 'FORCED_SUITE.SWUTEST_INIT-TEST_INIT_I2C_READ_FAILS', + 'FORCED_SUITE.SWUTEST_INIT-TEST_INIT_I2C_READ_FAILS2', + 'FORCED_SUITE.SWUTEST_INIT-TEST_INIT_I2C_READ_FAILS3'] + + for tc in test_cases: + self.assertEqual(tc['classname'], expected_classnames.pop(0)) + self.assertEqual(tc['suite'], 'FORCED_SUITE') # The suite in test cases remains the same + + def test_force_suite_name_noname(self): + ''' Verify that when a suite name is forced, it is used, even for utest.c.''' + with tempfile.NamedTemporaryFile(mode='w', delete=True) as tmp_output_file: + converter = Unity2Junit(TEST_IN_DIR / 'utest_Noname_Runner.log', tmp_output_file.name, + suite_name="FORCED_SUITE") + converter.parse_unity_output() + test_cases = converter.test_cases + + expected_classnames = [ + 'FORCED_SUITE.SWUTEST_UTEST-TEST_INIT_SUCCESS', + 'FORCED_SUITE.SWUTEST_UTEST-TEST_INIT_WRONG_EEPROM_VERSION', + 'FORCED_SUITE.SWUTEST_UTEST-TEST_INIT_I2C_READ_FAILS', + 'FORCED_SUITE.SWUTEST_UTEST-TEST_INIT_I2C_READ_FAILS2', + 'FORCED_SUITE.SWUTEST_UTEST-TEST_INIT_I2C_READ_FAILS3'] + + for tc in test_cases: + self.assertEqual(tc['classname'], expected_classnames.pop(0)) + self.assertEqual(tc['suite'], 'FORCED_SUITE') # The suite in test cases remains the same + + def test_force_suite_name_empty(self): + ''' Verify that when an empty suite name is forced, the classname has no prefix.''' + with tempfile.NamedTemporaryFile(mode='w', delete=True) as tmp_output_file: + converter = Unity2Junit(TEST_IN_DIR / 'utest_Init_Runner.log', tmp_output_file.name, + suite_name="") + converter.parse_unity_output() + test_cases = converter.test_cases + + expected_classnames = [ + 'SWUTEST_INIT-TEST_INIT_SUCCESS', + 'SWUTEST_INIT-TEST_INIT_WRONG_EEPROM_VERSION', + 'SWUTEST_INIT-TEST_INIT_I2C_READ_FAILS', + 'SWUTEST_INIT-TEST_INIT_I2C_READ_FAILS2', + 'SWUTEST_INIT-TEST_INIT_I2C_READ_FAILS3'] + + for tc in test_cases: + self.assertEqual(tc['classname'], expected_classnames.pop(0)) + self.assertEqual(tc['suite'], '') # The suite in test cases remains the same + + def test_force_suite_name_empty_noname(self): + ''' Verify that when an empty suite name is forced, the classname has no prefix, even for utest.c.''' + with tempfile.NamedTemporaryFile(mode='w', delete=True) as tmp_output_file: + converter = Unity2Junit(TEST_IN_DIR / 'utest_Noname_Runner.log', tmp_output_file.name, + suite_name="") + converter.parse_unity_output() + test_cases = converter.test_cases + + expected_classnames = [ + 'SWUTEST_UTEST-TEST_INIT_SUCCESS', + 'SWUTEST_UTEST-TEST_INIT_WRONG_EEPROM_VERSION', + 'SWUTEST_UTEST-TEST_INIT_I2C_READ_FAILS', + 'SWUTEST_UTEST-TEST_INIT_I2C_READ_FAILS2', + 'SWUTEST_UTEST-TEST_INIT_I2C_READ_FAILS3'] + + for tc in test_cases: + self.assertEqual(tc['classname'], expected_classnames.pop(0)) + self.assertEqual(tc['suite'], '') # The suite in test cases remains the same + + self.assertEqual(converter.default_suite_name, '') + if __name__ == '__main__': unittest.main()