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