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