Add public interface for XmlUnitTestResultPrinter

Allowing custom gtest_main implementations to instantiate the
XmlUnitTestResultPrinter with either a given file path or any other kind
of std::ostream to write to.

This is useful for e.g. embedded cases where an XML report is still
wanted, but not file system is available, by instantiating with a
std::stringstream and delivering the data via any custom mean.

Related to #1930
This commit is contained in:
Mara Sophie Grosch 2023-10-29 04:42:37 +01:00
parent 5b7fd63d6d
commit 36bd22a585
2 changed files with 74 additions and 40 deletions

View File

@ -1018,6 +1018,23 @@ class EmptyTestEventListener : public TestEventListener {
void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {}
}; };
class XmlUnitTestResultPrinter : public EmptyTestEventListener {
public:
XmlUnitTestResultPrinter(const char* output_file);
XmlUnitTestResultPrinter(std::ostream* output_stream);
XmlUnitTestResultPrinter(const XmlUnitTestResultPrinter&) = delete;
XmlUnitTestResultPrinter& operator=(const XmlUnitTestResultPrinter&) = delete;
void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override;
void ListTestsMatchingFilter(const std::vector<TestSuite*>& test_suites);
private:
const std::string output_file_;
std::ostream* output_stream_;
};
// TestEventListeners lets users add listeners to track events in Google Test. // TestEventListeners lets users add listeners to track events in Google Test.
class GTEST_API_ TestEventListeners { class GTEST_API_ TestEventListeners {
public: public:

View File

@ -3897,10 +3897,7 @@ void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test,
// This class generates an XML output file. // This class generates an XML output file.
class XmlUnitTestResultPrinter : public EmptyTestEventListener { class XmlUnitTestResultPrinter : public EmptyTestEventListener {
public: public:
explicit XmlUnitTestResultPrinter(const char* output_file); XmlUnitTestResultPrinter() = delete;
void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override;
void ListTestsMatchingFilter(const std::vector<TestSuite*>& test_suites);
// Prints an XML summary of all unit tests. // Prints an XML summary of all unit tests.
static void PrintXmlTestsList(std::ostream* stream, static void PrintXmlTestsList(std::ostream* stream,
@ -3982,40 +3979,9 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
static void OutputXmlTestProperties(std::ostream* stream, static void OutputXmlTestProperties(std::ostream* stream,
const TestResult& result); const TestResult& result);
// The output file. friend ::testing::XmlUnitTestResultPrinter;
const std::string output_file_;
XmlUnitTestResultPrinter(const XmlUnitTestResultPrinter&) = delete;
XmlUnitTestResultPrinter& operator=(const XmlUnitTestResultPrinter&) = delete;
}; };
// Creates a new XmlUnitTestResultPrinter.
XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file)
: output_file_(output_file) {
if (output_file_.empty()) {
GTEST_LOG_(FATAL) << "XML output file may not be null";
}
}
// Called after the unit test ends.
void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
int /*iteration*/) {
FILE* xmlout = OpenFileForWriting(output_file_);
std::stringstream stream;
PrintXmlUnitTest(&stream, unit_test);
fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
fclose(xmlout);
}
void XmlUnitTestResultPrinter::ListTestsMatchingFilter(
const std::vector<TestSuite*>& test_suites) {
FILE* xmlout = OpenFileForWriting(output_file_);
std::stringstream stream;
PrintXmlTestsList(&stream, test_suites);
fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
fclose(xmlout);
}
// Returns an XML-escaped copy of the input string str. If is_attribute // Returns an XML-escaped copy of the input string str. If is_attribute
// is true, the text is meant to appear as an attribute value, and // is true, the text is meant to appear as an attribute value, and
// normalizable whitespace is preserved by replacing it with character // normalizable whitespace is preserved by replacing it with character
@ -5145,6 +5111,59 @@ void TestEventListeners::SuppressEventForwarding(bool suppress) {
repeater_->set_forwarding_enabled(!suppress); repeater_->set_forwarding_enabled(!suppress);
} }
// Creates a new XmlUnitTestResultPrinter.
XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file)
: output_file_(output_file) {
if (output_file_.empty()) {
GTEST_LOG_(FATAL) << "XML output file may not be null";
}
}
// Creates a new XmlUnitTestResultPrinter.
XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(std::ostream* output_stream)
: output_stream_(output_stream) {
if (!output_stream->good()) {
GTEST_LOG_(FATAL) << "XML output is not good";
}
}
// Called after the unit test ends.
void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
int /*iteration*/) {
FILE* xmlout = 0;
if(!output_stream_) {
xmlout = internal::OpenFileForWriting(output_file_);
output_stream_ = new std::stringstream;
}
internal::XmlUnitTestResultPrinter::PrintXmlUnitTest(output_stream_, unit_test);
if(xmlout) {
fprintf(xmlout, "%s", internal::StringStreamToString(static_cast<std::stringstream*>(output_stream_)).c_str());
fclose(xmlout);
delete output_stream_;
output_stream_ = 0;
}
}
void XmlUnitTestResultPrinter::ListTestsMatchingFilter(
const std::vector<TestSuite*>& test_suites) {
FILE* xmlout = 0;
if(!output_stream_) {
xmlout = internal::OpenFileForWriting(output_file_);
output_stream_ = new std::stringstream;
}
internal::XmlUnitTestResultPrinter::PrintXmlTestsList(output_stream_, test_suites);
if(xmlout) {
fprintf(xmlout, "%s", internal::StringStreamToString(static_cast<std::stringstream*>(output_stream_)).c_str());
fclose(xmlout);
delete output_stream_;
output_stream_ = 0;
}
}
// class UnitTest // class UnitTest
// Gets the singleton UnitTest object. The first time this method is // Gets the singleton UnitTest object. The first time this method is
@ -5630,7 +5649,7 @@ void UnitTestImpl::ConfigureXmlOutput() {
const std::string& output_format = UnitTestOptions::GetOutputFormat(); const std::string& output_format = UnitTestOptions::GetOutputFormat();
#if GTEST_HAS_FILE_SYSTEM #if GTEST_HAS_FILE_SYSTEM
if (output_format == "xml") { if (output_format == "xml") {
listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( listeners()->SetDefaultXmlGenerator(new ::testing::XmlUnitTestResultPrinter(
UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); UnitTestOptions::GetAbsolutePathToOutputFile().c_str()));
} else if (output_format == "json") { } else if (output_format == "json") {
listeners()->SetDefaultXmlGenerator(new JsonUnitTestResultPrinter( listeners()->SetDefaultXmlGenerator(new JsonUnitTestResultPrinter(
@ -6206,9 +6225,7 @@ void UnitTestImpl::ListTestsMatchingFilter() {
UnitTestOptions::GetAbsolutePathToOutputFile().c_str()); UnitTestOptions::GetAbsolutePathToOutputFile().c_str());
std::stringstream stream; std::stringstream stream;
if (output_format == "xml") { if (output_format == "xml") {
XmlUnitTestResultPrinter( XmlUnitTestResultPrinter::PrintXmlTestsList(&stream, test_suites_);
UnitTestOptions::GetAbsolutePathToOutputFile().c_str())
.PrintXmlTestsList(&stream, test_suites_);
} else if (output_format == "json") { } else if (output_format == "json") {
JsonUnitTestResultPrinter( JsonUnitTestResultPrinter(
UnitTestOptions::GetAbsolutePathToOutputFile().c_str()) UnitTestOptions::GetAbsolutePathToOutputFile().c_str())