1 // Copyright 2015 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_unittest_result_printer.h"
6 
7 #include "base/base64.h"
8 #include "base/command_line.h"
9 #include "base/files/file_util.h"
10 #include "base/logging.h"
11 #include "base/test/test_switches.h"
12 #include "base/time/time.h"
13 
14 namespace base {
15 
16 namespace {
17 const int kDefaultTestPartResultsLimit = 10;
18 
19 const char kTestPartLesultsLimitExceeded[] =
20     "Test part results limit exceeded. Use --test-launcher-test-part-limit to "
21     "increase or disable limit.";
22 }  // namespace
23 
XmlUnitTestResultPrinter()24 XmlUnitTestResultPrinter::XmlUnitTestResultPrinter()
25     : output_file_(nullptr), open_failed_(false) {}
26 
~XmlUnitTestResultPrinter()27 XmlUnitTestResultPrinter::~XmlUnitTestResultPrinter() {
28   if (output_file_ && !open_failed_) {
29     fprintf(output_file_, "</testsuites>\n");
30     fflush(output_file_);
31     CloseFile(output_file_);
32   }
33 }
34 
Initialize(const FilePath & output_file_path)35 bool XmlUnitTestResultPrinter::Initialize(const FilePath& output_file_path) {
36   DCHECK(!output_file_);
37   output_file_ = OpenFile(output_file_path, "w");
38   if (!output_file_) {
39     // If the file open fails, we set the output location to stderr. This is
40     // because in current usage our caller CHECKs the result of this function.
41     // But that in turn causes a LogMessage that comes back to this object,
42     // which in turn causes a (double) crash. By pointing at stderr, there might
43     // be some indication what's going wrong. See https://crbug.com/736783.
44     output_file_ = stderr;
45     open_failed_ = true;
46     return false;
47   }
48 
49   fprintf(output_file_,
50           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testsuites>\n");
51   fflush(output_file_);
52 
53   return true;
54 }
55 
OnAssert(const char * file,int line,const std::string & summary,const std::string & message)56 void XmlUnitTestResultPrinter::OnAssert(const char* file,
57                                         int line,
58                                         const std::string& summary,
59                                         const std::string& message) {
60   WriteTestPartResult(file, line, testing::TestPartResult::kFatalFailure,
61                       summary, message);
62 }
63 
OnTestCaseStart(const testing::TestCase & test_case)64 void XmlUnitTestResultPrinter::OnTestCaseStart(
65     const testing::TestCase& test_case) {
66   fprintf(output_file_, "  <testsuite>\n");
67   fflush(output_file_);
68 }
69 
OnTestStart(const testing::TestInfo & test_info)70 void XmlUnitTestResultPrinter::OnTestStart(
71     const testing::TestInfo& test_info) {
72   // This is our custom extension - it helps to recognize which test was
73   // running when the test binary crashed. Note that we cannot even open the
74   // <testcase> tag here - it requires e.g. run time of the test to be known.
75   fprintf(output_file_,
76           "    <x-teststart name=\"%s\" classname=\"%s\" />\n",
77           test_info.name(),
78           test_info.test_case_name());
79   fflush(output_file_);
80 }
81 
OnTestEnd(const testing::TestInfo & test_info)82 void XmlUnitTestResultPrinter::OnTestEnd(const testing::TestInfo& test_info) {
83   fprintf(output_file_,
84           "    <testcase name=\"%s\" status=\"run\" time=\"%.3f\""
85           " classname=\"%s\">\n",
86           test_info.name(),
87           static_cast<double>(test_info.result()->elapsed_time()) /
88               Time::kMillisecondsPerSecond,
89           test_info.test_case_name());
90   if (test_info.result()->Failed()) {
91     fprintf(output_file_,
92             "      <failure message=\"\" type=\"\"></failure>\n");
93   }
94 
95   int limit = test_info.result()->total_part_count();
96   if (CommandLine::ForCurrentProcess()->HasSwitch(
97           switches::kTestLauncherTestPartResultsLimit)) {
98     std::string limit_str =
99         CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
100             switches::kTestLauncherTestPartResultsLimit);
101     int test_part_results_limit = std::strtol(limit_str.c_str(), nullptr, 10);
102     if (test_part_results_limit >= 0)
103       limit = std::min(limit, test_part_results_limit);
104   } else {
105     limit = std::min(limit, kDefaultTestPartResultsLimit);
106   }
107 
108   for (int i = 0; i < limit; ++i) {
109     const auto& test_part_result = test_info.result()->GetTestPartResult(i);
110     WriteTestPartResult(test_part_result.file_name(),
111                         test_part_result.line_number(), test_part_result.type(),
112                         test_part_result.summary(), test_part_result.message());
113   }
114 
115   if (test_info.result()->total_part_count() > limit) {
116     WriteTestPartResult(
117         "<unknown>", 0, testing::TestPartResult::kNonFatalFailure,
118         kTestPartLesultsLimitExceeded, kTestPartLesultsLimitExceeded);
119   }
120 
121   fprintf(output_file_, "    </testcase>\n");
122   fflush(output_file_);
123 }
124 
OnTestCaseEnd(const testing::TestCase & test_case)125 void XmlUnitTestResultPrinter::OnTestCaseEnd(
126     const testing::TestCase& test_case) {
127   fprintf(output_file_, "  </testsuite>\n");
128   fflush(output_file_);
129 }
130 
WriteTestPartResult(const char * file,int line,testing::TestPartResult::Type result_type,const std::string & summary,const std::string & message)131 void XmlUnitTestResultPrinter::WriteTestPartResult(
132     const char* file,
133     int line,
134     testing::TestPartResult::Type result_type,
135     const std::string& summary,
136     const std::string& message) {
137   const char* type = "unknown";
138   switch (result_type) {
139     case testing::TestPartResult::kSuccess:
140       type = "success";
141       break;
142     case testing::TestPartResult::kNonFatalFailure:
143       type = "failure";
144       break;
145     case testing::TestPartResult::kFatalFailure:
146       type = "fatal_failure";
147       break;
148   }
149   std::string summary_encoded;
150   Base64Encode(summary, &summary_encoded);
151   std::string message_encoded;
152   Base64Encode(message, &message_encoded);
153   fprintf(output_file_,
154           "      <x-test-result-part type=\"%s\" file=\"%s\" line=\"%d\">\n"
155           "        <summary>%s</summary>\n"
156           "        <message>%s</message>\n"
157           "      </x-test-result-part>\n",
158           type, file, line, summary_encoded.c_str(), message_encoded.c_str());
159   fflush(output_file_);
160 }
161 
162 }  // namespace base
163