From b4aae50ce1dfa3aeb885ef22c998119adef9c720 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 30 Jan 2025 16:00:55 -0800 Subject: Export testsuite properties as XML elements, not attributes. With this change, arbitrary property names in the testsuite no longer cause the produced XML output to be ill-formed. PiperOrigin-RevId: 721549090 Change-Id: Iedffa89bf914478f563c8f3b82cd50557762a665 --- googletest/src/gtest.cc | 38 +++------- googletest/test/googletest-json-output-unittest.py | 36 ++++----- googletest/test/gtest_xml_output_unittest.py | 88 +++++++++++----------- googletest/test/gtest_xml_output_unittest_.cc | 6 +- 4 files changed, 80 insertions(+), 88 deletions(-) diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc index 3c1cac6..7ff8254 100644 --- a/googletest/src/gtest.cc +++ b/googletest/src/gtest.cc @@ -4012,16 +4012,11 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { static void PrintXmlUnitTest(::std::ostream* stream, const UnitTest& unit_test); - // Produces a string representing the test properties in a result as space - // delimited XML attributes based on the property key="value" pairs. - // When the std::string is not empty, it includes a space at the beginning, - // to delimit this attribute from prior attributes. - static std::string TestPropertiesAsXmlAttributes(const TestResult& result); - // Streams an XML representation of the test properties of a TestResult // object. static void OutputXmlTestProperties(std::ostream* stream, - const TestResult& result); + const TestResult& result, + const std::string& indent); // The output file. const std::string output_file_; @@ -4355,7 +4350,7 @@ void XmlUnitTestResultPrinter::OutputXmlTestResult(::std::ostream* stream, if (failures == 0 && skips == 0) { *stream << ">\n"; } - OutputXmlTestProperties(stream, result); + OutputXmlTestProperties(stream, result, /*indent=*/" "); *stream << " \n"; } } @@ -4384,9 +4379,10 @@ void XmlUnitTestResultPrinter::PrintXmlTestSuite(std::ostream* stream, OutputXmlAttribute( stream, kTestsuite, "timestamp", FormatEpochTimeInMillisAsIso8601(test_suite.start_timestamp())); - *stream << TestPropertiesAsXmlAttributes(test_suite.ad_hoc_test_result()); } *stream << ">\n"; + OutputXmlTestProperties(stream, test_suite.ad_hoc_test_result(), + /*indent=*/" "); for (int i = 0; i < test_suite.total_test_count(); ++i) { if (test_suite.GetTestInfo(i)->is_reportable()) OutputXmlTestInfo(stream, test_suite.name(), *test_suite.GetTestInfo(i)); @@ -4424,11 +4420,12 @@ void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, OutputXmlAttribute(stream, kTestsuites, "random_seed", StreamableToString(unit_test.random_seed())); } - *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); *stream << ">\n"; + OutputXmlTestProperties(stream, unit_test.ad_hoc_test_result(), + /*indent=*/" "); for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { if (unit_test.GetTestSuite(i)->reportable_test_count() > 0) PrintXmlTestSuite(stream, *unit_test.GetTestSuite(i)); @@ -4465,21 +4462,8 @@ void XmlUnitTestResultPrinter::PrintXmlTestsList( *stream << "\n"; } -// Produces a string representing the test properties in a result as space -// delimited XML attributes based on the property key="value" pairs. -std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( - const TestResult& result) { - Message attributes; - for (int i = 0; i < result.test_property_count(); ++i) { - const TestProperty& property = result.GetTestProperty(i); - attributes << " " << property.key() << "=" << "\"" - << EscapeXmlAttribute(property.value()) << "\""; - } - return attributes.GetString(); -} - void XmlUnitTestResultPrinter::OutputXmlTestProperties( - std::ostream* stream, const TestResult& result) { + std::ostream* stream, const TestResult& result, const std::string& indent) { const std::string kProperties = "properties"; const std::string kProperty = "property"; @@ -4487,15 +4471,15 @@ void XmlUnitTestResultPrinter::OutputXmlTestProperties( return; } - *stream << " <" << kProperties << ">\n"; + *stream << indent << "<" << kProperties << ">\n"; for (int i = 0; i < result.test_property_count(); ++i) { const TestProperty& property = result.GetTestProperty(i); - *stream << " <" << kProperty; + *stream << indent << " <" << kProperty; *stream << " name=\"" << EscapeXmlAttribute(property.key()) << "\""; *stream << " value=\"" << EscapeXmlAttribute(property.value()) << "\""; *stream << "/>\n"; } - *stream << " \n"; + *stream << indent << "\n"; } // End XmlUnitTestResultPrinter diff --git a/googletest/test/googletest-json-output-unittest.py b/googletest/test/googletest-json-output-unittest.py index 270de05..c75051c 100644 --- a/googletest/test/googletest-json-output-unittest.py +++ b/googletest/test/googletest-json-output-unittest.py @@ -323,12 +323,14 @@ EXPECTED_NON_EMPTY = { 'time': '*', 'timestamp': '*', 'SetUpTestSuite': 'yes', + 'SetUpTestSuite (with whitespace)': 'yes and yes', 'TearDownTestSuite': 'aye', + 'TearDownTestSuite (with whitespace)': 'aye and aye', 'testsuite': [ { 'name': 'OneProperty', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 121, + 'line': 125, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -339,7 +341,7 @@ EXPECTED_NON_EMPTY = { { 'name': 'IntValuedProperty', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 125, + 'line': 129, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -350,7 +352,7 @@ EXPECTED_NON_EMPTY = { { 'name': 'ThreeProperties', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 129, + 'line': 133, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -363,7 +365,7 @@ EXPECTED_NON_EMPTY = { { 'name': 'TwoValuesForOneKeyUsesLastValue', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 135, + 'line': 139, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -385,7 +387,7 @@ EXPECTED_NON_EMPTY = { { 'name': 'RecordProperty', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 140, + 'line': 144, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -396,7 +398,7 @@ EXPECTED_NON_EMPTY = { { 'name': 'ExternalUtilityThatCallsRecordIntValuedProperty', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 153, + 'line': 157, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -409,7 +411,7 @@ EXPECTED_NON_EMPTY = { 'ExternalUtilityThatCallsRecordStringValuedProperty' ), 'file': 'gtest_xml_output_unittest_.cc', - 'line': 157, + 'line': 161, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -431,7 +433,7 @@ EXPECTED_NON_EMPTY = { { 'name': 'NoopPassingTest', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 168, + 'line': 172, 'status': 'RUN', 'result': 'SKIPPED', 'timestamp': '*', @@ -471,7 +473,7 @@ EXPECTED_NON_EMPTY = { { 'name': 'NoopPassingTest', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 175, + 'line': 179, 'status': 'RUN', 'result': 'COMPLETED', 'timestamp': '*', @@ -508,7 +510,7 @@ EXPECTED_NON_EMPTY = { 'name': 'HasTypeParamAttribute', 'type_param': 'int', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 189, + 'line': 193, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -528,7 +530,7 @@ EXPECTED_NON_EMPTY = { 'name': 'HasTypeParamAttribute', 'type_param': 'long', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 189, + 'line': 193, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -548,7 +550,7 @@ EXPECTED_NON_EMPTY = { 'name': 'HasTypeParamAttribute', 'type_param': 'int', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 196, + 'line': 200, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -568,7 +570,7 @@ EXPECTED_NON_EMPTY = { 'name': 'HasTypeParamAttribute', 'type_param': 'long', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 196, + 'line': 200, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -589,7 +591,7 @@ EXPECTED_NON_EMPTY = { 'name': 'HasValueParamAttribute/0', 'value_param': '33', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 180, + 'line': 184, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -600,7 +602,7 @@ EXPECTED_NON_EMPTY = { 'name': 'HasValueParamAttribute/1', 'value_param': '42', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 180, + 'line': 184, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -611,7 +613,7 @@ EXPECTED_NON_EMPTY = { 'name': 'AnotherTestThatHasValueParamAttribute/0', 'value_param': '33', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 181, + 'line': 185, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', @@ -622,7 +624,7 @@ EXPECTED_NON_EMPTY = { 'name': 'AnotherTestThatHasValueParamAttribute/1', 'value_param': '42', 'file': 'gtest_xml_output_unittest_.cc', - 'line': 181, + 'line': 185, 'status': 'RUN', 'result': 'COMPLETED', 'time': '*', diff --git a/googletest/test/gtest_xml_output_unittest.py b/googletest/test/gtest_xml_output_unittest.py index f2fc9b9..87a7683 100755 --- a/googletest/test/gtest_xml_output_unittest.py +++ b/googletest/test/gtest_xml_output_unittest.py @@ -67,7 +67,10 @@ else: sys.argv.remove(NO_STACKTRACE_SUPPORT_FLAG) EXPECTED_NON_EMPTY_XML = """ - + + + + @@ -132,49 +135,55 @@ It is good practice to tell why you skip a test. - - + + + + + + + + - + - + - + - + - + - + - + @@ -186,7 +195,7 @@ Expected equality of these values: - + - - - - + + + + - + - + - + - + """ % { 'stack': STACK_TRACE_TEMPLATE, @@ -218,46 +227,39 @@ Expected equality of these values: } EXPECTED_FILTERED_TEST_XML = """ - + + + + """ -ACTUAL_OUTPUT = """ - - - - - - - - - - - - - - - -""" - EXPECTED_SHARDED_TEST_XML = """ - + + + + - - + + + + + + + + - + """ diff --git a/googletest/test/gtest_xml_output_unittest_.cc b/googletest/test/gtest_xml_output_unittest_.cc index 4a39157..fe196b6 100644 --- a/googletest/test/gtest_xml_output_unittest_.cc +++ b/googletest/test/gtest_xml_output_unittest_.cc @@ -112,8 +112,12 @@ TEST(InvalidCharactersTest, InvalidCharactersInMessage) { class PropertyRecordingTest : public Test { public: - static void SetUpTestSuite() { RecordProperty("SetUpTestSuite", "yes"); } + static void SetUpTestSuite() { + RecordProperty("SetUpTestSuite (with whitespace)", "yes and yes"); + RecordProperty("SetUpTestSuite", "yes"); + } static void TearDownTestSuite() { + RecordProperty("TearDownTestSuite (with whitespace)", "aye and aye"); RecordProperty("TearDownTestSuite", "aye"); } }; -- cgit v0.12