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/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 = test_case_name + "." + test_name; 150 151 result.elapsed_time = TimeDelta(); 152 153 // Assume the test crashed - we can correct that later. 154 result.status = TestResult::TEST_CRASH; 155 156 results->push_back(result); 157 } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) { 158 std::string test_status; 159 if (!xml_reader.NodeAttribute("status", &test_status)) 160 return false; 161 162 if (test_status != "run" && test_status != "notrun") 163 return false; 164 if (test_status != "run") 165 break; 166 167 TestResult result; 168 169 std::string test_case_name; 170 if (!xml_reader.NodeAttribute("classname", &test_case_name)) 171 return false; 172 std::string test_name; 173 if (!xml_reader.NodeAttribute("name", &test_name)) 174 return false; 175 result.full_name = test_case_name + "." + test_name; 176 177 std::string test_time_str; 178 if (!xml_reader.NodeAttribute("time", &test_time_str)) 179 return false; 180 result.elapsed_time = 181 TimeDelta::FromMicroseconds(strtod(test_time_str.c_str(), NULL) * 182 Time::kMicrosecondsPerSecond); 183 184 result.status = TestResult::TEST_SUCCESS; 185 186 if (!results->empty() && 187 results->at(results->size() - 1).full_name == result.full_name && 188 results->at(results->size() - 1).status == 189 TestResult::TEST_CRASH) { 190 // Erase the fail-safe "crashed" result - now we know the test did 191 // not crash. 192 results->pop_back(); 193 } 194 195 results->push_back(result); 196 } else if (node_name == "failure" && !xml_reader.IsClosingElement()) { 197 std::string failure_message; 198 if (!xml_reader.NodeAttribute("message", &failure_message)) 199 return false; 200 201 DCHECK(!results->empty()); 202 results->at(results->size() - 1).status = TestResult::TEST_FAILURE; 203 204 state = STATE_FAILURE; 205 } else if (node_name == "testcase" && xml_reader.IsClosingElement()) { 206 // Deliberately empty. 207 } else { 208 return false; 209 } 210 break; 211 case STATE_FAILURE: 212 if (node_name == "failure" && xml_reader.IsClosingElement()) 213 state = STATE_TESTCASE; 214 else 215 return false; 216 break; 217 case STATE_END: 218 // If we are here and there are still XML elements, the file has wrong 219 // format. 220 return false; 221 } 222 } 223 224 *crashed = (state != STATE_END); 225 return true; 226 } 227 228 } // namespace base 229