• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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