Home | History | Annotate | Download | only in test
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/test/gtest_xml_util.h"
      6 
      7 #include "base/files/file_util.h"
      8 #include "base/logging.h"
      9 #include "base/strings/stringprintf.h"
     10 #include "base/test/launcher/test_launcher.h"
     11 #include "third_party/libxml/chromium/libxml_utils.h"
     12 
     13 namespace base {
     14 
     15 namespace {
     16 
     17 // This is used for the xml parser to report errors. This assumes the context
     18 // is a pointer to a std::string where the error message should be appended.
     19 static void XmlErrorFunc(void *context, const char *message, ...) {
     20   va_list args;
     21   va_start(args, message);
     22   std::string* error = static_cast<std::string*>(context);
     23   base::StringAppendV(error, message, args);
     24   va_end(args);
     25 }
     26 
     27 }  // namespace
     28 
     29 XmlUnitTestResultPrinter::XmlUnitTestResultPrinter() : output_file_(NULL) {
     30 }
     31 
     32 XmlUnitTestResultPrinter::~XmlUnitTestResultPrinter() {
     33   if (output_file_) {
     34     fprintf(output_file_, "</testsuites>\n");
     35     fflush(output_file_);
     36     base::CloseFile(output_file_);
     37   }
     38 }
     39 
     40 bool XmlUnitTestResultPrinter::Initialize(const FilePath& output_file_path) {
     41   DCHECK(!output_file_);
     42   output_file_ = OpenFile(output_file_path, "w");
     43   if (!output_file_)
     44     return false;
     45 
     46   fprintf(output_file_,
     47           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testsuites>\n");
     48   fflush(output_file_);
     49 
     50   return true;
     51 }
     52 
     53 void XmlUnitTestResultPrinter::OnTestCaseStart(
     54     const testing::TestCase& test_case) {
     55   fprintf(output_file_, "  <testsuite>\n");
     56   fflush(output_file_);
     57 }
     58 
     59 void XmlUnitTestResultPrinter::OnTestStart(const testing::TestInfo& test_info) {
     60   // This is our custom extension - it helps to recognize which test was running
     61   // when the test binary crashed. Note that we cannot even open the <testcase>
     62   // tag here - it requires e.g. run time of the test to be known.
     63   fprintf(output_file_,
     64           "    <x-teststart name=\"%s\" classname=\"%s\" />\n",
     65           test_info.name(),
     66           test_info.test_case_name());
     67   fflush(output_file_);
     68 }
     69 
     70 void XmlUnitTestResultPrinter::OnTestEnd(const testing::TestInfo& test_info) {
     71   fprintf(output_file_,
     72           "    <testcase name=\"%s\" status=\"run\" time=\"%.3f\""
     73           " classname=\"%s\">\n",
     74           test_info.name(),
     75           static_cast<double>(test_info.result()->elapsed_time()) /
     76               Time::kMillisecondsPerSecond,
     77           test_info.test_case_name());
     78   if (test_info.result()->Failed())
     79     fprintf(output_file_, "      <failure message=\"\" type=\"\"></failure>\n");
     80   fprintf(output_file_, "    </testcase>\n");
     81   fflush(output_file_);
     82 }
     83 
     84 void XmlUnitTestResultPrinter::OnTestCaseEnd(
     85     const testing::TestCase& test_case) {
     86   fprintf(output_file_, "  </testsuite>\n");
     87   fflush(output_file_);
     88 }
     89 
     90 bool ProcessGTestOutput(const base::FilePath& output_file,
     91                         std::vector<TestResult>* results,
     92                         bool* crashed) {
     93   DCHECK(results);
     94 
     95   std::string xml_contents;
     96   if (!ReadFileToString(output_file, &xml_contents))
     97     return false;
     98 
     99   // Silence XML errors - otherwise they go to stderr.
    100   std::string xml_errors;
    101   ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc);
    102 
    103   XmlReader xml_reader;
    104   if (!xml_reader.Load(xml_contents))
    105     return false;
    106 
    107   enum {
    108     STATE_INIT,
    109     STATE_TESTSUITE,
    110     STATE_TESTCASE,
    111     STATE_FAILURE,
    112     STATE_END,
    113   } state = STATE_INIT;
    114 
    115   while (xml_reader.Read()) {
    116     xml_reader.SkipToElement();
    117     std::string node_name(xml_reader.NodeName());
    118 
    119     switch (state) {
    120       case STATE_INIT:
    121         if (node_name == "testsuites" && !xml_reader.IsClosingElement())
    122           state = STATE_TESTSUITE;
    123         else
    124           return false;
    125         break;
    126       case STATE_TESTSUITE:
    127         if (node_name == "testsuites" && xml_reader.IsClosingElement())
    128           state = STATE_END;
    129         else if (node_name == "testsuite" && !xml_reader.IsClosingElement())
    130           state = STATE_TESTCASE;
    131         else
    132           return false;
    133         break;
    134       case STATE_TESTCASE:
    135         if (node_name == "testsuite" && xml_reader.IsClosingElement()) {
    136           state = STATE_TESTSUITE;
    137         } else if (node_name == "x-teststart" &&
    138                    !xml_reader.IsClosingElement()) {
    139           // This is our custom extension that helps recognize which test was
    140           // running when the test binary crashed.
    141           TestResult result;
    142 
    143           std::string test_case_name;
    144           if (!xml_reader.NodeAttribute("classname", &test_case_name))
    145             return false;
    146           std::string test_name;
    147           if (!xml_reader.NodeAttribute("name", &test_name))
    148             return false;
    149           result.full_name = TestLauncher::FormatFullTestName(test_case_name,
    150                                                               test_name);
    151 
    152           result.elapsed_time = TimeDelta();
    153 
    154           // Assume the test crashed - we can correct that later.
    155           result.status = TestResult::TEST_CRASH;
    156 
    157           results->push_back(result);
    158         } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) {
    159           std::string test_status;
    160           if (!xml_reader.NodeAttribute("status", &test_status))
    161             return false;
    162 
    163           if (test_status != "run" && test_status != "notrun")
    164             return false;
    165           if (test_status != "run")
    166             break;
    167 
    168           TestResult result;
    169 
    170           std::string test_case_name;
    171           if (!xml_reader.NodeAttribute("classname", &test_case_name))
    172             return false;
    173           std::string test_name;
    174           if (!xml_reader.NodeAttribute("name", &test_name))
    175             return false;
    176           result.full_name = test_case_name + "." + test_name;
    177 
    178           std::string test_time_str;
    179           if (!xml_reader.NodeAttribute("time", &test_time_str))
    180             return false;
    181           result.elapsed_time =
    182               TimeDelta::FromMicroseconds(strtod(test_time_str.c_str(), NULL) *
    183                                           Time::kMicrosecondsPerSecond);
    184 
    185           result.status = TestResult::TEST_SUCCESS;
    186 
    187           if (!results->empty() &&
    188               results->at(results->size() - 1).full_name == result.full_name &&
    189               results->at(results->size() - 1).status ==
    190                   TestResult::TEST_CRASH) {
    191             // Erase the fail-safe "crashed" result - now we know the test did
    192             // not crash.
    193             results->pop_back();
    194           }
    195 
    196           results->push_back(result);
    197         } else if (node_name == "failure" && !xml_reader.IsClosingElement()) {
    198           std::string failure_message;
    199           if (!xml_reader.NodeAttribute("message", &failure_message))
    200             return false;
    201 
    202           DCHECK(!results->empty());
    203           results->at(results->size() - 1).status = TestResult::TEST_FAILURE;
    204 
    205           state = STATE_FAILURE;
    206         } else if (node_name == "testcase" && xml_reader.IsClosingElement()) {
    207           // Deliberately empty.
    208         } else {
    209           return false;
    210         }
    211         break;
    212       case STATE_FAILURE:
    213         if (node_name == "failure" && xml_reader.IsClosingElement())
    214           state = STATE_TESTCASE;
    215         else
    216           return false;
    217         break;
    218       case STATE_END:
    219         // If we are here and there are still XML elements, the file has wrong
    220         // format.
    221         return false;
    222     }
    223   }
    224 
    225   *crashed = (state != STATE_END);
    226   return true;
    227 }
    228 
    229 }  // namespace base
    230