123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- // Copyright 2013 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/test/gtest_xml_util.h"
- #include <stdint.h>
- #include "base/base64.h"
- #include "base/check.h"
- #include "base/files/file_util.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/stringprintf.h"
- #include "base/test/gtest_util.h"
- #include "base/test/launcher/test_launcher.h"
- #include "third_party/libxml/chromium/xml_reader.h"
- namespace base {
- struct Link {
- // The name of the test case.
- std::string name;
- // The name of the classname of the test.
- std::string classname;
- // The name of the link.
- std::string link_name;
- // The actual link.
- std::string link;
- };
- bool ProcessGTestOutput(const base::FilePath& output_file,
- std::vector<TestResult>* results,
- bool* crashed) {
- DCHECK(results);
- std::string xml_contents;
- if (!ReadFileToString(output_file, &xml_contents))
- return false;
- XmlReader xml_reader;
- if (!xml_reader.Load(xml_contents))
- return false;
- enum {
- STATE_INIT,
- STATE_TESTSUITE,
- STATE_TESTCASE,
- STATE_TEST_RESULT,
- STATE_FAILURE,
- STATE_END,
- } state = STATE_INIT;
- std::vector<Link> links;
- while (xml_reader.Read()) {
- xml_reader.SkipToElement();
- std::string node_name(xml_reader.NodeName());
- switch (state) {
- case STATE_INIT:
- if (node_name == "testsuites" && !xml_reader.IsClosingElement())
- state = STATE_TESTSUITE;
- else
- return false;
- break;
- case STATE_TESTSUITE:
- if (node_name == "testsuites" && xml_reader.IsClosingElement())
- state = STATE_END;
- else if (node_name == "testsuite" && !xml_reader.IsClosingElement())
- state = STATE_TESTCASE;
- else
- return false;
- break;
- case STATE_TESTCASE:
- if (node_name == "testsuite" && xml_reader.IsClosingElement()) {
- state = STATE_TESTSUITE;
- } else if (node_name == "x-teststart" &&
- !xml_reader.IsClosingElement()) {
- // This is our custom extension that helps recognize which test was
- // running when the test binary crashed.
- TestResult result;
- std::string test_case_name;
- if (!xml_reader.NodeAttribute("classname", &test_case_name))
- return false;
- std::string test_name;
- if (!xml_reader.NodeAttribute("name", &test_name))
- return false;
- result.full_name = FormatFullTestName(test_case_name, test_name);
- result.elapsed_time = TimeDelta();
- std::string test_timestamp_str;
- Time test_timestamp;
- if (xml_reader.NodeAttribute("timestamp", &test_timestamp_str) &&
- Time::FromString(test_timestamp_str.c_str(), &test_timestamp)) {
- result.timestamp = test_timestamp;
- }
- // Assume the test crashed - we can correct that later.
- result.status = TestResult::TEST_CRASH;
- results->push_back(result);
- } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) {
- std::string test_status;
- if (!xml_reader.NodeAttribute("status", &test_status))
- return false;
- if (test_status != "run" && test_status != "notrun")
- return false;
- if (test_status != "run")
- break;
- TestResult result;
- std::string test_case_name;
- if (!xml_reader.NodeAttribute("classname", &test_case_name))
- return false;
- std::string test_name;
- if (!xml_reader.NodeAttribute("name", &test_name))
- return false;
- result.full_name = test_case_name + "." + test_name;
- std::string test_time_str;
- if (!xml_reader.NodeAttribute("time", &test_time_str))
- return false;
- result.elapsed_time = Microseconds(
- static_cast<int64_t>(strtod(test_time_str.c_str(), nullptr) *
- Time::kMicrosecondsPerSecond));
- // The timestamp attribute records the local date and time of the test
- // execution. It might be missing in the xml generated by older
- // version of test launcher or gtest.
- // https://github.com/google/googletest/blob/main/docs/advanced.md#generating-an-xml-report
- std::string test_timestamp_str;
- Time test_timestamp;
- if (xml_reader.NodeAttribute("timestamp", &test_timestamp_str) &&
- Time::FromString(test_timestamp_str.c_str(), &test_timestamp)) {
- result.timestamp = test_timestamp;
- }
- result.status = TestResult::TEST_SUCCESS;
- if (!results->empty() &&
- results->back().full_name == result.full_name &&
- results->back().status == TestResult::TEST_CRASH) {
- // Erase the fail-safe "crashed" result - now we know the test did
- // not crash.
- results->pop_back();
- }
- for (const Link& link : links) {
- if (link.name == test_name && link.classname == test_case_name) {
- result.AddLink(link.link_name, link.link);
- }
- }
- links.clear();
- results->push_back(result);
- } else if (node_name == "link" && !xml_reader.IsClosingElement()) {
- Link link;
- if (!xml_reader.NodeAttribute("name", &link.name))
- return false;
- if (!xml_reader.NodeAttribute("classname", &link.classname))
- return false;
- if (!xml_reader.NodeAttribute("link_name", &link.link_name))
- return false;
- if (!xml_reader.ReadElementContent(&link.link))
- return false;
- links.push_back(link);
- } else if (node_name == "link" && xml_reader.IsClosingElement()) {
- // Deliberately empty.
- } else if (node_name == "failure" && !xml_reader.IsClosingElement()) {
- std::string failure_message;
- if (!xml_reader.NodeAttribute("message", &failure_message))
- return false;
- DCHECK(!results->empty());
- results->back().status = TestResult::TEST_FAILURE;
- state = STATE_FAILURE;
- } else if (node_name == "testcase" && xml_reader.IsClosingElement()) {
- // Deliberately empty.
- } else if (node_name == "x-test-result-part" &&
- !xml_reader.IsClosingElement()) {
- std::string result_type;
- if (!xml_reader.NodeAttribute("type", &result_type))
- return false;
- std::string file_name;
- if (!xml_reader.NodeAttribute("file", &file_name))
- return false;
- std::string line_number_str;
- if (!xml_reader.NodeAttribute("line", &line_number_str))
- return false;
- int line_number;
- if (!StringToInt(line_number_str, &line_number))
- return false;
- TestResultPart::Type type;
- if (!TestResultPart::TypeFromString(result_type, &type))
- return false;
- TestResultPart test_result_part;
- test_result_part.type = type;
- test_result_part.file_name = file_name,
- test_result_part.line_number = line_number;
- DCHECK(!results->empty());
- results->back().test_result_parts.push_back(test_result_part);
- state = STATE_TEST_RESULT;
- } else {
- return false;
- }
- break;
- case STATE_TEST_RESULT:
- if (node_name == "summary" && !xml_reader.IsClosingElement()) {
- std::string summary;
- if (!xml_reader.ReadElementContent(&summary))
- return false;
- if (!Base64Decode(summary, &summary))
- return false;
- DCHECK(!results->empty());
- DCHECK(!results->back().test_result_parts.empty());
- results->back().test_result_parts.back().summary = summary;
- } else if (node_name == "summary" && xml_reader.IsClosingElement()) {
- } else if (node_name == "message" && !xml_reader.IsClosingElement()) {
- std::string message;
- if (!xml_reader.ReadElementContent(&message))
- return false;
- if (!Base64Decode(message, &message))
- return false;
- DCHECK(!results->empty());
- DCHECK(!results->back().test_result_parts.empty());
- results->back().test_result_parts.back().message = message;
- } else if (node_name == "message" && xml_reader.IsClosingElement()) {
- } else if (node_name == "x-test-result-part" &&
- xml_reader.IsClosingElement()) {
- state = STATE_TESTCASE;
- } else {
- return false;
- }
- break;
- case STATE_FAILURE:
- if (node_name == "failure" && xml_reader.IsClosingElement())
- state = STATE_TESTCASE;
- else
- return false;
- break;
- case STATE_END:
- // If we are here and there are still XML elements, the file has wrong
- // format.
- return false;
- }
- }
- if (crashed) {
- *crashed = (state != STATE_END);
- }
- return true;
- }
- } // namespace base
|