| Index: base/test/gtest_xml_util.cc
|
| diff --git a/base/test/gtest_xml_util.cc b/base/test/gtest_xml_util.cc
|
| index 6187d9f3e5abb7d4eb11dce7ed88c653dadbba57..524369a6bd8144a6b727d2f0ff6227ee2d29a126 100644
|
| --- a/base/test/gtest_xml_util.cc
|
| +++ b/base/test/gtest_xml_util.cc
|
| @@ -6,19 +6,100 @@
|
|
|
| #include "base/file_util.h"
|
| #include "base/logging.h"
|
| +#include "base/strings/stringprintf.h"
|
| #include "base/test/test_launcher.h"
|
| #include "third_party/libxml/chromium/libxml_utils.h"
|
|
|
| namespace base {
|
|
|
| +namespace {
|
| +
|
| +// This is used for the xml parser to report errors. This assumes the context
|
| +// is a pointer to a std::string where the error message should be appended.
|
| +static void XmlErrorFunc(void *context, const char *message, ...) {
|
| + va_list args;
|
| + va_start(args, message);
|
| + std::string* error = static_cast<std::string*>(context);
|
| + base::StringAppendV(error, message, args);
|
| + va_end(args);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter() : output_file_(NULL) {
|
| +}
|
| +
|
| +XmlUnitTestResultPrinter::~XmlUnitTestResultPrinter() {
|
| + if (output_file_) {
|
| + fprintf(output_file_, "</testsuites>\n");
|
| + fflush(output_file_);
|
| + file_util::CloseFile(output_file_);
|
| + }
|
| +}
|
| +
|
| +bool XmlUnitTestResultPrinter::Initialize(const FilePath& output_file_path) {
|
| + DCHECK(!output_file_);
|
| + output_file_ = file_util::OpenFile(output_file_path, "w");
|
| + if (!output_file_)
|
| + return false;
|
| +
|
| + fprintf(output_file_,
|
| + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testsuites>\n");
|
| + fflush(output_file_);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void XmlUnitTestResultPrinter::OnTestCaseStart(
|
| + const testing::TestCase& test_case) {
|
| + fprintf(output_file_, " <testsuite>\n");
|
| + fflush(output_file_);
|
| +}
|
| +
|
| +void XmlUnitTestResultPrinter::OnTestStart(const testing::TestInfo& test_info) {
|
| + // This is our custom extension - it helps to recognize which test was running
|
| + // when the test binary crashed. Note that we cannot even open the <testcase>
|
| + // tag here - it requires e.g. run time of the test to be known.
|
| + fprintf(output_file_,
|
| + " <x-teststart name=\"%s\" classname=\"%s\" />\n",
|
| + test_info.name(),
|
| + test_info.test_case_name());
|
| + fflush(output_file_);
|
| +}
|
| +
|
| +void XmlUnitTestResultPrinter::OnTestEnd(const testing::TestInfo& test_info) {
|
| + fprintf(output_file_,
|
| + " <testcase name=\"%s\" status=\"run\" time=\"%.3f\""
|
| + " classname=\"%s\">\n",
|
| + test_info.name(),
|
| + static_cast<double>(test_info.result()->elapsed_time()) /
|
| + Time::kMillisecondsPerSecond,
|
| + test_info.test_case_name());
|
| + if (test_info.result()->Failed())
|
| + fprintf(output_file_, " <failure message=\"\" type=\"\"></failure>\n");
|
| + fprintf(output_file_, " </testcase>\n");
|
| + fflush(output_file_);
|
| +}
|
| +
|
| +void XmlUnitTestResultPrinter::OnTestCaseEnd(
|
| + const testing::TestCase& test_case) {
|
| + fprintf(output_file_, " </testsuite>\n");
|
| + fflush(output_file_);
|
| +}
|
| +
|
| bool ProcessGTestOutput(const base::FilePath& output_file,
|
| - std::vector<TestResult>* results) {
|
| + std::vector<TestResult>* results,
|
| + bool* crashed) {
|
| DCHECK(results);
|
|
|
| std::string xml_contents;
|
| if (!file_util::ReadFileToString(output_file, &xml_contents))
|
| return false;
|
|
|
| + // Silence XML errors - otherwise they go to stderr.
|
| + std::string xml_errors;
|
| + ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc);
|
| +
|
| XmlReader xml_reader;
|
| if (!xml_reader.Load(xml_contents))
|
| return false;
|
| @@ -53,6 +134,23 @@ bool ProcessGTestOutput(const base::FilePath& output_file,
|
| case STATE_TESTCASE:
|
| if (node_name == "testsuite" && xml_reader.IsClosingElement()) {
|
| state = STATE_TESTSUITE;
|
| + } else if (node_name == "x-teststart" &&
|
| + !xml_reader.IsClosingElement()) {
|
| + // This is our custom extension that helps recognize which test was
|
| + // running when the test binary crashed.
|
| + TestResult result;
|
| + if (!xml_reader.NodeAttribute("classname", &result.test_case_name))
|
| + return false;
|
| + if (!xml_reader.NodeAttribute("name", &result.test_name))
|
| + return false;
|
| +
|
| + result.elapsed_time = TimeDelta();
|
| +
|
| + // Assume the test crashed - we can correct that later.
|
| + result.success = false;
|
| + result.crashed = true;
|
| +
|
| + results->push_back(result);
|
| } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) {
|
| std::string test_status;
|
| if (!xml_reader.NodeAttribute("status", &test_status))
|
| @@ -77,6 +175,16 @@ bool ProcessGTestOutput(const base::FilePath& output_file,
|
| Time::kMicrosecondsPerSecond);
|
|
|
| result.success = true;
|
| + result.crashed = false;
|
| +
|
| + if (!results->empty() &&
|
| + results->at(results->size() - 1).GetFullName() ==
|
| + result.GetFullName() &&
|
| + results->at(results->size() - 1).crashed) {
|
| + // Erase the fail-safe "crashed" result - now we know the test did
|
| + // not crash.
|
| + results->pop_back();
|
| + }
|
|
|
| results->push_back(result);
|
| } else if (node_name == "failure" && !xml_reader.IsClosingElement()) {
|
| @@ -107,7 +215,8 @@ bool ProcessGTestOutput(const base::FilePath& output_file,
|
| }
|
| }
|
|
|
| + *crashed = (state != STATE_END);
|
| return true;
|
| }
|
|
|
| -} // namespace base
|
| +} // namespace base
|
|
|