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 "base/files/file_util.h" 8 #include "base/logging.h" 9 #include "base/strings/stringprintf.h" 10 #include "base/test/launcher/test_launcher.h" 11 #include "third_party/libxml/chromium/libxml_utils.h" 12 13 namespace base { 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 base::CloseFile(output_file_); 37 } 38 } 39 40 bool XmlUnitTestResultPrinter::Initialize(const FilePath& output_file_path) { 41 DCHECK(!output_file_); 42 output_file_ = 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 90 bool ProcessGTestOutput(const base::FilePath& output_file, 91 std::vector<TestResult>* results, 92 bool* crashed) { 93 DCHECK(results); 94 95 std::string xml_contents; 96 if (!ReadFileToString(output_file, &xml_contents)) 97 return false; 98 99 // Silence XML errors - otherwise they go to stderr. 100 std::string xml_errors; 101 ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc); 102 103 XmlReader xml_reader; 104 if (!xml_reader.Load(xml_contents)) 105 return false; 106 107 enum { 108 STATE_INIT, 109 STATE_TESTSUITE, 110 STATE_TESTCASE, 111 STATE_FAILURE, 112 STATE_END, 113 } state = STATE_INIT; 114 115 while (xml_reader.Read()) { 116 xml_reader.SkipToElement(); 117 std::string node_name(xml_reader.NodeName()); 118 119 switch (state) { 120 case STATE_INIT: 121 if (node_name == "testsuites" && !xml_reader.IsClosingElement()) 122 state = STATE_TESTSUITE; 123 else 124 return false; 125 break; 126 case STATE_TESTSUITE: 127 if (node_name == "testsuites" && xml_reader.IsClosingElement()) 128 state = STATE_END; 129 else if (node_name == "testsuite" && !xml_reader.IsClosingElement()) 130 state = STATE_TESTCASE; 131 else 132 return false; 133 break; 134 case STATE_TESTCASE: 135 if (node_name == "testsuite" && xml_reader.IsClosingElement()) { 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 143 std::string test_case_name; 144 if (!xml_reader.NodeAttribute("classname", &test_case_name)) 145 return false; 146 std::string test_name; 147 if (!xml_reader.NodeAttribute("name", &test_name)) 148 return false; 149 result.full_name = TestLauncher::FormatFullTestName(test_case_name, 150 test_name); 151 152 result.elapsed_time = TimeDelta(); 153 154 // Assume the test crashed - we can correct that later. 155 result.status = TestResult::TEST_CRASH; 156 157 results->push_back(result); 158 } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) { 159 std::string test_status; 160 if (!xml_reader.NodeAttribute("status", &test_status)) 161 return false; 162 163 if (test_status != "run" && test_status != "notrun") 164 return false; 165 if (test_status != "run") 166 break; 167 168 TestResult result; 169 170 std::string test_case_name; 171 if (!xml_reader.NodeAttribute("classname", &test_case_name)) 172 return false; 173 std::string test_name; 174 if (!xml_reader.NodeAttribute("name", &test_name)) 175 return false; 176 result.full_name = test_case_name + "." + test_name; 177 178 std::string test_time_str; 179 if (!xml_reader.NodeAttribute("time", &test_time_str)) 180 return false; 181 result.elapsed_time = 182 TimeDelta::FromMicroseconds(strtod(test_time_str.c_str(), NULL) * 183 Time::kMicrosecondsPerSecond); 184 185 result.status = TestResult::TEST_SUCCESS; 186 187 if (!results->empty() && 188 results->at(results->size() - 1).full_name == result.full_name && 189 results->at(results->size() - 1).status == 190 TestResult::TEST_CRASH) { 191 // Erase the fail-safe "crashed" result - now we know the test did 192 // not crash. 193 results->pop_back(); 194 } 195 196 results->push_back(result); 197 } else if (node_name == "failure" && !xml_reader.IsClosingElement()) { 198 std::string failure_message; 199 if (!xml_reader.NodeAttribute("message", &failure_message)) 200 return false; 201 202 DCHECK(!results->empty()); 203 results->at(results->size() - 1).status = TestResult::TEST_FAILURE; 204 205 state = STATE_FAILURE; 206 } else if (node_name == "testcase" && xml_reader.IsClosingElement()) { 207 // Deliberately empty. 208 } else { 209 return false; 210 } 211 break; 212 case STATE_FAILURE: 213 if (node_name == "failure" && xml_reader.IsClosingElement()) 214 state = STATE_TESTCASE; 215 else 216 return false; 217 break; 218 case STATE_END: 219 // If we are here and there are still XML elements, the file has wrong 220 // format. 221 return false; 222 } 223 } 224 225 *crashed = (state != STATE_END); 226 return true; 227 } 228 229 } // namespace base 230