Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions mlx/unity2junit/unity2junit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand All @@ -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."""
Expand All @@ -35,21 +36,31 @@ 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}-"

# 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
Expand Down Expand Up @@ -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()


Expand Down
78 changes: 78 additions & 0 deletions tests/unity_parsing_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()