// Copyright 2015 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_unittest_result_printer.h" #include "base/base64.h" #include "base/command_line.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/test/test_switches.h" #include "base/time/time.h" namespace base { namespace { const int kDefaultTestPartResultsLimit = 10; const char kTestPartLesultsLimitExceeded[] = "Test part results limit exceeded. Use --test-launcher-test-part-limit to " "increase or disable limit."; } // namespace XmlUnitTestResultPrinter::XmlUnitTestResultPrinter() : output_file_(nullptr), open_failed_(false) {} XmlUnitTestResultPrinter::~XmlUnitTestResultPrinter() { if (output_file_ && !open_failed_) { fprintf(output_file_, "\n"); fflush(output_file_); CloseFile(output_file_); } } bool XmlUnitTestResultPrinter::Initialize(const FilePath& output_file_path) { DCHECK(!output_file_); output_file_ = OpenFile(output_file_path, "w"); if (!output_file_) { // If the file open fails, we set the output location to stderr. This is // because in current usage our caller CHECKs the result of this function. // But that in turn causes a LogMessage that comes back to this object, // which in turn causes a (double) crash. By pointing at stderr, there might // be some indication what's going wrong. See https://crbug.com/736783. output_file_ = stderr; open_failed_ = true; return false; } fprintf(output_file_, "\n\n"); fflush(output_file_); return true; } void XmlUnitTestResultPrinter::OnAssert(const char* file, int line, const std::string& summary, const std::string& message) { WriteTestPartResult(file, line, testing::TestPartResult::kFatalFailure, summary, message); } void XmlUnitTestResultPrinter::OnTestCaseStart( const testing::TestCase& test_case) { fprintf(output_file_, " \n"); fflush(output_file_); } void XmlUnitTestResultPrinter::OnTestStart( const testing::TestInfo& test_info) { // This is our custom extension - it helps to recognize which test was // running when the test binary crashed. Note that we cannot even open the // tag here - it requires e.g. run time of the test to be known. fprintf(output_file_, " \n", test_info.name(), test_info.test_case_name()); fflush(output_file_); } void XmlUnitTestResultPrinter::OnTestEnd(const testing::TestInfo& test_info) { fprintf(output_file_, " \n", test_info.name(), static_cast(test_info.result()->elapsed_time()) / Time::kMillisecondsPerSecond, test_info.test_case_name()); if (test_info.result()->Failed()) { fprintf(output_file_, " \n"); } int limit = test_info.result()->total_part_count(); if (CommandLine::ForCurrentProcess()->HasSwitch( switches::kTestLauncherTestPartResultsLimit)) { std::string limit_str = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kTestLauncherTestPartResultsLimit); int test_part_results_limit = std::strtol(limit_str.c_str(), nullptr, 10); if (test_part_results_limit >= 0) limit = std::min(limit, test_part_results_limit); } else { limit = std::min(limit, kDefaultTestPartResultsLimit); } for (int i = 0; i < limit; ++i) { const auto& test_part_result = test_info.result()->GetTestPartResult(i); WriteTestPartResult(test_part_result.file_name(), test_part_result.line_number(), test_part_result.type(), test_part_result.summary(), test_part_result.message()); } if (test_info.result()->total_part_count() > limit) { WriteTestPartResult( "", 0, testing::TestPartResult::kNonFatalFailure, kTestPartLesultsLimitExceeded, kTestPartLesultsLimitExceeded); } fprintf(output_file_, " \n"); fflush(output_file_); } void XmlUnitTestResultPrinter::OnTestCaseEnd( const testing::TestCase& test_case) { fprintf(output_file_, " \n"); fflush(output_file_); } void XmlUnitTestResultPrinter::WriteTestPartResult( const char* file, int line, testing::TestPartResult::Type result_type, const std::string& summary, const std::string& message) { const char* type = "unknown"; switch (result_type) { case testing::TestPartResult::kSuccess: type = "success"; break; case testing::TestPartResult::kNonFatalFailure: type = "failure"; break; case testing::TestPartResult::kFatalFailure: type = "fatal_failure"; break; } std::string summary_encoded; Base64Encode(summary, &summary_encoded); std::string message_encoded; Base64Encode(message, &message_encoded); fprintf(output_file_, " \n" " %s\n" " %s\n" " \n", type, file, line, summary_encoded.c_str(), message_encoded.c_str()); fflush(output_file_); } } // namespace base