diff --git a/launch_testing/launch_testing/junitxml.py b/launch_testing/launch_testing/junitxml.py index 3a8117a48..d00e2b790 100644 --- a/launch_testing/launch_testing/junitxml.py +++ b/launch_testing/launch_testing/junitxml.py @@ -16,7 +16,7 @@ import xml.etree.ElementTree as ET -def unittestResultsToXml(*, name='launch_test', test_results={}): +def unittestResultsToXml(*, name='launch_test', test_results={}, classname_prefix=None): """ Serialize multiple unittest.TestResult objects into an XML document. @@ -46,12 +46,12 @@ def unittestResultsToXml(*, name='launch_test', test_results={}): test_suites.set('time', str(round(time, 3))) for (name, test_result) in test_results.items(): - test_suites.append(unittestResultToXml(str(name), test_result)) + test_suites.append(unittestResultToXml(str(name), test_result, classname_prefix)) return ET.ElementTree(test_suites) -def unittestResultToXml(name, test_result): +def unittestResultToXml(name, test_result, classname_prefix=None): """ Serialize a single unittest.TestResult to an XML element. @@ -68,12 +68,12 @@ def unittestResultToXml(name, test_result): test_suite.set('time', str(round(sum(test_result.testTimes.values()), 3))) for case in test_result.testCases: - test_suite.append(unittestCaseToXml(test_result, case)) + test_suite.append(unittestCaseToXml(test_result, case, classname_prefix)) return test_suite -def unittestCaseToXml(test_result, test_case): +def unittestCaseToXml(test_result, test_case, classname_prefix=None): """ Serialize a unittest.TestCase into an XML element. @@ -85,6 +85,8 @@ class needs to be a launch_testing.TestResult class case_xml = ET.Element('testcase') full_methodname, _, qualifiers = test_case.id().partition(' ') full_classname, _, methodname = full_methodname.rpartition('.') + if classname_prefix: + full_classname = '{}.{}'.format(classname_prefix, full_classname) case_xml.set('classname', full_classname) case_xml.set('name', (methodname + ' ' + qualifiers).strip()) case_xml.set('time', str(round(test_result.testTimes[test_case], 3))) diff --git a/launch_testing/launch_testing/launch_test.py b/launch_testing/launch_testing/launch_test.py index 0a855f6bb..5d1b473ad 100644 --- a/launch_testing/launch_testing/launch_test.py +++ b/launch_testing/launch_testing/launch_test.py @@ -41,6 +41,10 @@ def add_arguments(parser): '--package-name', action='store', default=None, help='Name of the package the test is in. Useful to aggregate xUnit reports.' ) + parser.add_argument( + '--test-name', action='store', default=None, + help='Unique test name to include in xUnit test case class names.' + ) parser.add_argument( '-v', '--verbose', action='store_true', default=False, help='Run with verbose output' ) @@ -116,7 +120,7 @@ def run(parser, args, test_runner_cls=LaunchTestRunner): xml_report = unittestResultsToXml( test_results=results, name='{}.{}'.format( args.package_name, launch_test_file_basename - ) + ), classname_prefix=args.test_name ) xml_report.write(args.xmlpath, encoding='utf-8', xml_declaration=True) diff --git a/launch_testing/test/launch_testing/test_xml_output.py b/launch_testing/test/launch_testing/test_xml_output.py index 6bcfae61e..7622dd285 100644 --- a/launch_testing/test/launch_testing/test_xml_output.py +++ b/launch_testing/test/launch_testing/test_xml_output.py @@ -19,6 +19,7 @@ import xml.etree.ElementTree as ET import ament_index_python +from launch_testing.junitxml import unittestResultToXml from launch_testing.junitxml import unittestResultsToXml from launch_testing.test_result import FailResult from launch_testing.test_result import SkipResult @@ -47,7 +48,8 @@ def setUpClass(cls): 'launch_test', path, '--junit-xml', os.path.join(cls.tmpdir.name, 'junit.xml'), - '--package-name', 'test_xml_output' + '--package-name', 'test_xml_output', + '--test-name', 'unique_ctest_target' ], ).returncode @@ -73,6 +75,10 @@ def test_pre_and_post(self): case_names = [case.attrib['name'] for case in test_suite] self.assertIn('test_count_to_four', case_names) self.assertIn('test_full_output', case_names) + self.assertTrue(all( + case.attrib['classname'].startswith('unique_ctest_target.') + for case in test_suite + )) @pytest.mark.usefixtures('source_test_loader_class_fixture') @@ -199,6 +205,19 @@ def test_multiple_test_results(self): child_names = [chld.attrib['name'] for chld in xml_tree.getroot()] self.assertEqual(set(child_names), {'launch_1', 'launch_2', 'launch_3'}) + def test_classname_prefix_keeps_same_cases_unique(self): + test_result = self.unit_test_result_factory([lambda self: None]) + first = unittestResultToXml('launch_1', test_result, 'ctest_target_1') + second = unittestResultToXml('launch_2', test_result, 'ctest_target_2') + self.assertEqual( + 'ctest_target_1.test_xml_output.TestHost', + first.find('testcase').attrib['classname'], + ) + self.assertEqual( + 'ctest_target_2.test_xml_output.TestHost', + second.find('testcase').attrib['classname'], + ) + def test_result_that_ran(self): """ Test we have output as a result of a test being run. diff --git a/launch_testing_ament_cmake/cmake/add_launch_test.cmake b/launch_testing_ament_cmake/cmake/add_launch_test.cmake index f1aa6917d..9c85e5ce5 100644 --- a/launch_testing_ament_cmake/cmake/add_launch_test.cmake +++ b/launch_testing_ament_cmake/cmake/add_launch_test.cmake @@ -127,6 +127,7 @@ function(add_launch_test filename) "${_launch_test_ARGS}" "--junit-xml=${_launch_test_RESULT_FILE}" "--package-name=${PROJECT_NAME}" + "--test-name=${_launch_test_TARGET}" ) ament_add_test(