diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc index 707e1a51cc..9089f50d49 100644 --- a/googletest/src/gtest.cc +++ b/googletest/src/gtest.cc @@ -3928,6 +3928,12 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. static void OutputXmlCDataSection(::std::ostream* stream, const char* data); + // Streams a test suite XML stanza containing the given test result. + // + // Requires: result.Failed() + static void OutputXmlTestSuiteForTestResult(::std::ostream* stream, + const TestResult& result); + // Streams an XML representation of a TestResult object. static void OutputXmlTestResult(::std::ostream* stream, const TestResult& result); @@ -4147,6 +4153,43 @@ void XmlUnitTestResultPrinter::OutputXmlAttribute( *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; } +// Streams a test suite XML stanza containing the given test result. +void XmlUnitTestResultPrinter::OutputXmlTestSuiteForTestResult( + ::std::ostream* stream, const TestResult& result) { + // Output the boilerplate for a minimal test suite with one test. + *stream << " "; + + // Output the boilerplate for a minimal test case with a single test. + *stream << " \n"; +} + // Prints an XML representation of a TestInfo object. void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, const char* test_suite_name, @@ -4309,6 +4352,13 @@ void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, if (unit_test.GetTestSuite(i)->reportable_test_count() > 0) PrintXmlTestSuite(stream, *unit_test.GetTestSuite(i)); } + + // If there was a test failure outside of one of the test suites (like in a + // test environment) include that in the output. + if (unit_test.ad_hoc_test_result().Failed()) { + OutputXmlTestSuiteForTestResult(stream, unit_test.ad_hoc_test_result()); + } + *stream << "\n"; } @@ -4399,6 +4449,12 @@ class JsonUnitTestResultPrinter : public EmptyTestEventListener { const std::string& indent, bool comma = true); + // Streams a test suite JSON stanza containing the given test result. + // + // Requires: result.Failed() + static void OutputJsonTestSuiteForTestResult(::std::ostream* stream, + const TestResult& result); + // Streams a JSON representation of a TestResult object. static void OutputJsonTestResult(::std::ostream* stream, const TestResult& result); @@ -4553,6 +4609,48 @@ void JsonUnitTestResultPrinter::OutputJsonKey( *stream << ",\n"; } +// Streams a test suite JSON stanza containing the given test result. +void JsonUnitTestResultPrinter::OutputJsonTestSuiteForTestResult( + ::std::ostream* stream, const TestResult& result) { + // Output the boilerplate for a new test suite. + *stream << Indent(4) << "{\n"; + OutputJsonKey(stream, "testsuite", "name", "NonTestSuiteFailure", Indent(6)); + OutputJsonKey(stream, "testsuite", "tests", 1, Indent(6)); + if (!GTEST_FLAG(list_tests)) { + OutputJsonKey(stream, "testsuite", "failures", 1, Indent(6)); + OutputJsonKey(stream, "testsuite", "disabled", 0, Indent(6)); + OutputJsonKey(stream, "testsuite", "skipped", 0, Indent(6)); + OutputJsonKey(stream, "testsuite", "errors", 0, Indent(6)); + OutputJsonKey(stream, "testsuite", "time", + FormatTimeInMillisAsDuration(result.elapsed_time()), + Indent(6)); + OutputJsonKey(stream, "testsuite", "timestamp", + FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()), + Indent(6)); + } + *stream << Indent(6) << "\"testsuite\": [\n"; + + // Output the boilerplate for a new test case. + *stream << Indent(8) << "{\n"; + OutputJsonKey(stream, "testcase", "name", "", Indent(10)); + OutputJsonKey(stream, "testcase", "status", "RUN", Indent(10)); + OutputJsonKey(stream, "testcase", "result", "COMPLETED", Indent(10)); + OutputJsonKey(stream, "testcase", "timestamp", + FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()), + Indent(10)); + OutputJsonKey(stream, "testcase", "time", + FormatTimeInMillisAsDuration(result.elapsed_time()), + Indent(10)); + OutputJsonKey(stream, "testcase", "classname", "", Indent(10), false); + *stream << TestPropertiesAsJson(result, Indent(10)); + + // Output the actual test result. + OutputJsonTestResult(stream, result); + + // Finish the test suite. + *stream << "\n" << Indent(6) << "]\n" << Indent(4) << "}"; +} + // Prints a JSON representation of a TestInfo object. void JsonUnitTestResultPrinter::OutputJsonTestInfo(::std::ostream* stream, const char* test_suite_name, @@ -4712,6 +4810,12 @@ void JsonUnitTestResultPrinter::PrintJsonUnitTest(std::ostream* stream, } } + // If there was a test failure outside of one of the test suites (like in a + // test environment) include that in the output. + if (unit_test.ad_hoc_test_result().Failed()) { + OutputJsonTestSuiteForTestResult(stream, unit_test.ad_hoc_test_result()); + } + *stream << "\n" << kIndent << "]\n" << "}\n"; } diff --git a/googletest/test/googletest-json-output-unittest.py b/googletest/test/googletest-json-output-unittest.py index e799d47311..41c8565144 100644 --- a/googletest/test/googletest-json-output-unittest.py +++ b/googletest/test/googletest-json-output-unittest.py @@ -612,15 +612,59 @@ }], } -EXPECTED_EMPTY = { - u'tests': 0, - u'failures': 0, - u'disabled': 0, - u'errors': 0, - u'time': u'*', - u'timestamp': u'*', - u'name': u'AllTests', - u'testsuites': [], +EXPECTED_NO_TEST = { + u'tests': + 0, + u'failures': + 0, + u'disabled': + 0, + u'errors': + 0, + u'time': + u'*', + u'timestamp': + u'*', + u'name': + u'AllTests', + u'testsuites': [{ + u'name': + u'NonTestSuiteFailure', + u'tests': + 1, + u'failures': + 1, + u'disabled': + 0, + u'skipped': + 0, + u'errors': + 0, + u'time': + u'*', + u'timestamp': + u'*', + u'testsuite': [{ + u'name': + u'', + u'status': + u'RUN', + u'result': + u'COMPLETED', + u'time': + u'*', + u'timestamp': + u'*', + u'classname': + u'', + u'failures': [{ + u'failure': u'gtest_no_test_unittest.cc:*\n' + u'Expected equality of these values:\n' + u' 1\n 2' + STACK_TRACE_TEMPLATE, + u'type': u'', + }] + }] + }], } GTEST_PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath(GTEST_PROGRAM_NAME) @@ -645,14 +689,14 @@ def testNonEmptyJsonOutput(self): """ self._TestJsonOutput(GTEST_PROGRAM_NAME, EXPECTED_NON_EMPTY, 1) - def testEmptyJsonOutput(self): + def testNoTestJsonOutput(self): """Verifies JSON output for a Google Test binary without actual tests. - Runs a test program that generates an empty JSON output, and - tests that the JSON output is expected. + Runs a test program that generates an JSON output for a binary with no + tests, and tests that the JSON output is expected. """ - self._TestJsonOutput('gtest_no_test_unittest', EXPECTED_EMPTY, 0) + self._TestJsonOutput('gtest_no_test_unittest', EXPECTED_NO_TEST, 0) def testTimestampValue(self): """Checks whether the timestamp attribute in the JSON output is valid. diff --git a/googletest/test/gtest_xml_output_unittest.py b/googletest/test/gtest_xml_output_unittest.py index de8b8c751a..eade7aac88 100755 --- a/googletest/test/gtest_xml_output_unittest.py +++ b/googletest/test/gtest_xml_output_unittest.py @@ -216,10 +216,20 @@ """ -EXPECTED_EMPTY_XML = """ +EXPECTED_NO_TEST_XML = """ -""" + + + + + +""" % { + 'stack': STACK_TRACE_TEMPLATE +} GTEST_PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath(GTEST_PROGRAM_NAME) @@ -242,14 +252,14 @@ def testNonEmptyXmlOutput(self): """ self._TestXmlOutput(GTEST_PROGRAM_NAME, EXPECTED_NON_EMPTY_XML, 1) - def testEmptyXmlOutput(self): + def testNoTestXmlOutput(self): """Verifies XML output for a Google Test binary without actual tests. - Runs a test program that generates an empty XML output, and - tests that the XML output is expected. + Runs a test program that generates an XML output for a binary without tests, + and tests that the XML output is expected. """ - self._TestXmlOutput('gtest_no_test_unittest', EXPECTED_EMPTY_XML, 0) + self._TestXmlOutput('gtest_no_test_unittest', EXPECTED_NO_TEST_XML, 0) def testTimestampValue(self): """Checks whether the timestamp attribute in the XML output is valid.