OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/test/gtest_xml_util.h" | 5 #include "base/test/gtest_xml_util.h" |
6 | 6 |
7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/strings/stringprintf.h" |
9 #include "base/test/test_launcher.h" | 10 #include "base/test/test_launcher.h" |
10 #include "third_party/libxml/chromium/libxml_utils.h" | 11 #include "third_party/libxml/chromium/libxml_utils.h" |
11 | 12 |
12 namespace base { | 13 namespace base { |
13 | 14 |
| 15 namespace { |
| 16 |
| 17 // This is used for the xml parser to report errors. This assumes the context |
| 18 // is a pointer to a std::string where the error message should be appended. |
| 19 static void XmlErrorFunc(void *context, const char *message, ...) { |
| 20 va_list args; |
| 21 va_start(args, message); |
| 22 std::string* error = static_cast<std::string*>(context); |
| 23 base::StringAppendV(error, message, args); |
| 24 va_end(args); |
| 25 } |
| 26 |
| 27 } // namespace |
| 28 |
| 29 XmlUnitTestResultPrinter::XmlUnitTestResultPrinter() : output_file_(NULL) { |
| 30 } |
| 31 |
| 32 XmlUnitTestResultPrinter::~XmlUnitTestResultPrinter() { |
| 33 if (output_file_) { |
| 34 fprintf(output_file_, "</testsuites>\n"); |
| 35 fflush(output_file_); |
| 36 file_util::CloseFile(output_file_); |
| 37 } |
| 38 } |
| 39 |
| 40 bool XmlUnitTestResultPrinter::Initialize(const FilePath& output_file_path) { |
| 41 DCHECK(!output_file_); |
| 42 output_file_ = file_util::OpenFile(output_file_path, "w"); |
| 43 if (!output_file_) |
| 44 return false; |
| 45 |
| 46 fprintf(output_file_, |
| 47 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testsuites>\n"); |
| 48 fflush(output_file_); |
| 49 |
| 50 return true; |
| 51 } |
| 52 |
| 53 void XmlUnitTestResultPrinter::OnTestCaseStart( |
| 54 const testing::TestCase& test_case) { |
| 55 fprintf(output_file_, " <testsuite>\n"); |
| 56 fflush(output_file_); |
| 57 } |
| 58 |
| 59 void XmlUnitTestResultPrinter::OnTestStart(const testing::TestInfo& test_info) { |
| 60 // This is our custom extension - it helps to recognize which test was running |
| 61 // when the test binary crashed. Note that we cannot even open the <testcase> |
| 62 // tag here - it requires e.g. run time of the test to be known. |
| 63 fprintf(output_file_, |
| 64 " <x-teststart name=\"%s\" classname=\"%s\" />\n", |
| 65 test_info.name(), |
| 66 test_info.test_case_name()); |
| 67 fflush(output_file_); |
| 68 } |
| 69 |
| 70 void XmlUnitTestResultPrinter::OnTestEnd(const testing::TestInfo& test_info) { |
| 71 fprintf(output_file_, |
| 72 " <testcase name=\"%s\" status=\"run\" time=\"%.3f\"" |
| 73 " classname=\"%s\">\n", |
| 74 test_info.name(), |
| 75 static_cast<double>(test_info.result()->elapsed_time()) / |
| 76 Time::kMillisecondsPerSecond, |
| 77 test_info.test_case_name()); |
| 78 if (test_info.result()->Failed()) |
| 79 fprintf(output_file_, " <failure message=\"\" type=\"\"></failure>\n"); |
| 80 fprintf(output_file_, " </testcase>\n"); |
| 81 fflush(output_file_); |
| 82 } |
| 83 |
| 84 void XmlUnitTestResultPrinter::OnTestCaseEnd( |
| 85 const testing::TestCase& test_case) { |
| 86 fprintf(output_file_, " </testsuite>\n"); |
| 87 fflush(output_file_); |
| 88 } |
| 89 |
14 bool ProcessGTestOutput(const base::FilePath& output_file, | 90 bool ProcessGTestOutput(const base::FilePath& output_file, |
15 std::vector<TestResult>* results) { | 91 std::vector<TestResult>* results, |
| 92 bool* crashed) { |
16 DCHECK(results); | 93 DCHECK(results); |
17 | 94 |
18 std::string xml_contents; | 95 std::string xml_contents; |
19 if (!file_util::ReadFileToString(output_file, &xml_contents)) | 96 if (!file_util::ReadFileToString(output_file, &xml_contents)) |
20 return false; | 97 return false; |
21 | 98 |
| 99 // Silence XML errors - otherwise they go to stderr. |
| 100 std::string xml_errors; |
| 101 ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc); |
| 102 |
22 XmlReader xml_reader; | 103 XmlReader xml_reader; |
23 if (!xml_reader.Load(xml_contents)) | 104 if (!xml_reader.Load(xml_contents)) |
24 return false; | 105 return false; |
25 | 106 |
26 enum { | 107 enum { |
27 STATE_INIT, | 108 STATE_INIT, |
28 STATE_TESTSUITE, | 109 STATE_TESTSUITE, |
29 STATE_TESTCASE, | 110 STATE_TESTCASE, |
30 STATE_FAILURE, | 111 STATE_FAILURE, |
31 STATE_END, | 112 STATE_END, |
(...skipping 14 matching lines...) Expand all Loading... |
46 if (node_name == "testsuites" && xml_reader.IsClosingElement()) | 127 if (node_name == "testsuites" && xml_reader.IsClosingElement()) |
47 state = STATE_END; | 128 state = STATE_END; |
48 else if (node_name == "testsuite" && !xml_reader.IsClosingElement()) | 129 else if (node_name == "testsuite" && !xml_reader.IsClosingElement()) |
49 state = STATE_TESTCASE; | 130 state = STATE_TESTCASE; |
50 else | 131 else |
51 return false; | 132 return false; |
52 break; | 133 break; |
53 case STATE_TESTCASE: | 134 case STATE_TESTCASE: |
54 if (node_name == "testsuite" && xml_reader.IsClosingElement()) { | 135 if (node_name == "testsuite" && xml_reader.IsClosingElement()) { |
55 state = STATE_TESTSUITE; | 136 state = STATE_TESTSUITE; |
| 137 } else if (node_name == "x-teststart" && |
| 138 !xml_reader.IsClosingElement()) { |
| 139 // This is our custom extension that helps recognize which test was |
| 140 // running when the test binary crashed. |
| 141 TestResult result; |
| 142 if (!xml_reader.NodeAttribute("classname", &result.test_case_name)) |
| 143 return false; |
| 144 if (!xml_reader.NodeAttribute("name", &result.test_name)) |
| 145 return false; |
| 146 |
| 147 result.elapsed_time = TimeDelta(); |
| 148 |
| 149 // Assume the test crashed - we can correct that later. |
| 150 result.success = false; |
| 151 result.crashed = true; |
| 152 |
| 153 results->push_back(result); |
56 } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) { | 154 } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) { |
57 std::string test_status; | 155 std::string test_status; |
58 if (!xml_reader.NodeAttribute("status", &test_status)) | 156 if (!xml_reader.NodeAttribute("status", &test_status)) |
59 return false; | 157 return false; |
60 | 158 |
61 if (test_status != "run" && test_status != "notrun") | 159 if (test_status != "run" && test_status != "notrun") |
62 return false; | 160 return false; |
63 if (test_status != "run") | 161 if (test_status != "run") |
64 break; | 162 break; |
65 | 163 |
66 TestResult result; | 164 TestResult result; |
67 if (!xml_reader.NodeAttribute("classname", &result.test_case_name)) | 165 if (!xml_reader.NodeAttribute("classname", &result.test_case_name)) |
68 return false; | 166 return false; |
69 if (!xml_reader.NodeAttribute("name", &result.test_name)) | 167 if (!xml_reader.NodeAttribute("name", &result.test_name)) |
70 return false; | 168 return false; |
71 | 169 |
72 std::string test_time_str; | 170 std::string test_time_str; |
73 if (!xml_reader.NodeAttribute("time", &test_time_str)) | 171 if (!xml_reader.NodeAttribute("time", &test_time_str)) |
74 return false; | 172 return false; |
75 result.elapsed_time = | 173 result.elapsed_time = |
76 TimeDelta::FromMicroseconds(strtod(test_time_str.c_str(), NULL) * | 174 TimeDelta::FromMicroseconds(strtod(test_time_str.c_str(), NULL) * |
77 Time::kMicrosecondsPerSecond); | 175 Time::kMicrosecondsPerSecond); |
78 | 176 |
79 result.success = true; | 177 result.success = true; |
| 178 result.crashed = false; |
| 179 |
| 180 if (!results->empty() && |
| 181 results->at(results->size() - 1).GetFullName() == |
| 182 result.GetFullName() && |
| 183 results->at(results->size() - 1).crashed) { |
| 184 // Erase the fail-safe "crashed" result - now we know the test did |
| 185 // not crash. |
| 186 results->pop_back(); |
| 187 } |
80 | 188 |
81 results->push_back(result); | 189 results->push_back(result); |
82 } else if (node_name == "failure" && !xml_reader.IsClosingElement()) { | 190 } else if (node_name == "failure" && !xml_reader.IsClosingElement()) { |
83 std::string failure_message; | 191 std::string failure_message; |
84 if (!xml_reader.NodeAttribute("message", &failure_message)) | 192 if (!xml_reader.NodeAttribute("message", &failure_message)) |
85 return false; | 193 return false; |
86 | 194 |
87 DCHECK(!results->empty()); | 195 DCHECK(!results->empty()); |
88 results->at(results->size() - 1).success = false; | 196 results->at(results->size() - 1).success = false; |
89 | 197 |
(...skipping 10 matching lines...) Expand all Loading... |
100 else | 208 else |
101 return false; | 209 return false; |
102 break; | 210 break; |
103 case STATE_END: | 211 case STATE_END: |
104 // If we are here and there are still XML elements, the file has wrong | 212 // If we are here and there are still XML elements, the file has wrong |
105 // format. | 213 // format. |
106 return false; | 214 return false; |
107 } | 215 } |
108 } | 216 } |
109 | 217 |
| 218 *crashed = (state != STATE_END); |
110 return true; | 219 return true; |
111 } | 220 } |
112 | 221 |
113 } // namespace base | 222 } // namespace base |
OLD | NEW |