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