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 <stdint.h>
8
9 #include "base/base64.h"
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/test/gtest_util.h"
15 #include "base/test/launcher/test_launcher.h"
16 #include "third_party/libxml/chromium/libxml_utils.h"
17
18 namespace base {
19
20 namespace {
21
22 // This is used for the xml parser to report errors. This assumes the context
23 // is a pointer to a std::string where the error message should be appended.
XmlErrorFunc(void * context,const char * message,...)24 static void XmlErrorFunc(void *context, const char *message, ...) {
25 va_list args;
26 va_start(args, message);
27 std::string* error = static_cast<std::string*>(context);
28 StringAppendV(error, message, args);
29 va_end(args);
30 }
31
32 } // namespace
33
ProcessGTestOutput(const base::FilePath & output_file,std::vector<TestResult> * results,bool * crashed)34 bool ProcessGTestOutput(const base::FilePath& output_file,
35 std::vector<TestResult>* results,
36 bool* crashed) {
37 DCHECK(results);
38
39 std::string xml_contents;
40 if (!ReadFileToString(output_file, &xml_contents))
41 return false;
42
43 // Silence XML errors - otherwise they go to stderr.
44 std::string xml_errors;
45 ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc);
46
47 XmlReader xml_reader;
48 if (!xml_reader.Load(xml_contents))
49 return false;
50
51 enum {
52 STATE_INIT,
53 STATE_TESTSUITE,
54 STATE_TESTCASE,
55 STATE_TEST_RESULT,
56 STATE_FAILURE,
57 STATE_END,
58 } state = STATE_INIT;
59
60 while (xml_reader.Read()) {
61 xml_reader.SkipToElement();
62 std::string node_name(xml_reader.NodeName());
63
64 switch (state) {
65 case STATE_INIT:
66 if (node_name == "testsuites" && !xml_reader.IsClosingElement())
67 state = STATE_TESTSUITE;
68 else
69 return false;
70 break;
71 case STATE_TESTSUITE:
72 if (node_name == "testsuites" && xml_reader.IsClosingElement())
73 state = STATE_END;
74 else if (node_name == "testsuite" && !xml_reader.IsClosingElement())
75 state = STATE_TESTCASE;
76 else
77 return false;
78 break;
79 case STATE_TESTCASE:
80 if (node_name == "testsuite" && xml_reader.IsClosingElement()) {
81 state = STATE_TESTSUITE;
82 } else if (node_name == "x-teststart" &&
83 !xml_reader.IsClosingElement()) {
84 // This is our custom extension that helps recognize which test was
85 // running when the test binary crashed.
86 TestResult result;
87
88 std::string test_case_name;
89 if (!xml_reader.NodeAttribute("classname", &test_case_name))
90 return false;
91 std::string test_name;
92 if (!xml_reader.NodeAttribute("name", &test_name))
93 return false;
94 result.full_name = FormatFullTestName(test_case_name, test_name);
95
96 result.elapsed_time = TimeDelta();
97
98 // Assume the test crashed - we can correct that later.
99 result.status = TestResult::TEST_CRASH;
100
101 results->push_back(result);
102 } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) {
103 std::string test_status;
104 if (!xml_reader.NodeAttribute("status", &test_status))
105 return false;
106
107 if (test_status != "run" && test_status != "notrun")
108 return false;
109 if (test_status != "run")
110 break;
111
112 TestResult result;
113
114 std::string test_case_name;
115 if (!xml_reader.NodeAttribute("classname", &test_case_name))
116 return false;
117 std::string test_name;
118 if (!xml_reader.NodeAttribute("name", &test_name))
119 return false;
120 result.full_name = test_case_name + "." + test_name;
121
122 std::string test_time_str;
123 if (!xml_reader.NodeAttribute("time", &test_time_str))
124 return false;
125 result.elapsed_time = TimeDelta::FromMicroseconds(
126 static_cast<int64_t>(strtod(test_time_str.c_str(), nullptr) *
127 Time::kMicrosecondsPerSecond));
128
129 result.status = TestResult::TEST_SUCCESS;
130
131 if (!results->empty() &&
132 results->back().full_name == result.full_name &&
133 results->back().status == TestResult::TEST_CRASH) {
134 // Erase the fail-safe "crashed" result - now we know the test did
135 // not crash.
136 results->pop_back();
137 }
138
139 results->push_back(result);
140 } else if (node_name == "failure" && !xml_reader.IsClosingElement()) {
141 std::string failure_message;
142 if (!xml_reader.NodeAttribute("message", &failure_message))
143 return false;
144
145 DCHECK(!results->empty());
146 results->back().status = TestResult::TEST_FAILURE;
147
148 state = STATE_FAILURE;
149 } else if (node_name == "testcase" && xml_reader.IsClosingElement()) {
150 // Deliberately empty.
151 } else if (node_name == "x-test-result-part" &&
152 !xml_reader.IsClosingElement()) {
153 std::string result_type;
154 if (!xml_reader.NodeAttribute("type", &result_type))
155 return false;
156
157 std::string file_name;
158 if (!xml_reader.NodeAttribute("file", &file_name))
159 return false;
160
161 std::string line_number_str;
162 if (!xml_reader.NodeAttribute("line", &line_number_str))
163 return false;
164
165 int line_number;
166 if (!StringToInt(line_number_str, &line_number))
167 return false;
168
169 TestResultPart::Type type;
170 if (!TestResultPart::TypeFromString(result_type, &type))
171 return false;
172
173 TestResultPart test_result_part;
174 test_result_part.type = type;
175 test_result_part.file_name = file_name,
176 test_result_part.line_number = line_number;
177 DCHECK(!results->empty());
178 results->back().test_result_parts.push_back(test_result_part);
179
180 state = STATE_TEST_RESULT;
181 } else {
182 return false;
183 }
184 break;
185 case STATE_TEST_RESULT:
186 if (node_name == "summary" && !xml_reader.IsClosingElement()) {
187 std::string summary;
188 if (!xml_reader.ReadElementContent(&summary))
189 return false;
190
191 if (!Base64Decode(summary, &summary))
192 return false;
193
194 DCHECK(!results->empty());
195 DCHECK(!results->back().test_result_parts.empty());
196 results->back().test_result_parts.back().summary = summary;
197 } else if (node_name == "summary" && xml_reader.IsClosingElement()) {
198 } else if (node_name == "message" && !xml_reader.IsClosingElement()) {
199 std::string message;
200 if (!xml_reader.ReadElementContent(&message))
201 return false;
202
203 if (!Base64Decode(message, &message))
204 return false;
205
206 DCHECK(!results->empty());
207 DCHECK(!results->back().test_result_parts.empty());
208 results->back().test_result_parts.back().message = message;
209 } else if (node_name == "message" && xml_reader.IsClosingElement()) {
210 } else if (node_name == "x-test-result-part" &&
211 xml_reader.IsClosingElement()) {
212 state = STATE_TESTCASE;
213 } else {
214 return false;
215 }
216 break;
217 case STATE_FAILURE:
218 if (node_name == "failure" && xml_reader.IsClosingElement())
219 state = STATE_TESTCASE;
220 else
221 return false;
222 break;
223 case STATE_END:
224 // If we are here and there are still XML elements, the file has wrong
225 // format.
226 return false;
227 }
228 }
229
230 *crashed = (state != STATE_END);
231 return true;
232 }
233
234 } // namespace base
235