blob: ba564b73b37cf661e259e62641f44fcada7ace0c [file] [log] [blame]
// Copyright (c) 2012 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 "chrome/test/logging/win/test_log_collector.h"
#include <windows.h>
#include <algorithm>
#include <ios>
#include <memory>
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "chrome/test/base/test_switches.h"
#include "chrome/test/logging/win/file_logger.h"
#include "chrome/test/logging/win/log_file_printer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace logging_win {
namespace {
const char kTraceLogExtension[] = ".etl";
class TestLogCollector {
public:
TestLogCollector();
~TestLogCollector();
void Initialize(testing::UnitTest* unit_test);
void SetUp();
void StartSessionForTest(const testing::TestInfo& test_info);
bool LogTestPartResult(const testing::TestPartResult& test_part_result);
void ProcessSessionForTest(const testing::TestInfo& test_info);
void TearDown();
private:
// An EventListener that generally delegates to a given default result
// printer with a few exceptions; see individual method comments for details.
class EventListener : public testing::TestEventListener {
public:
// Ownership of |default_result_printer| is taken by the new instance.
EventListener(TestLogCollector* test_log_collector,
testing::TestEventListener* default_result_printer);
~EventListener() override;
// Sets up the log collector.
void OnTestProgramStart(const testing::UnitTest& unit_test) override {
test_log_collector_->SetUp();
default_result_printer_->OnTestProgramStart(unit_test);
}
void OnTestIterationStart(const testing::UnitTest& unit_test,
int iteration) override {
default_result_printer_->OnTestIterationStart(unit_test, iteration);
}
void OnEnvironmentsSetUpStart(const testing::UnitTest& unit_test) override {
default_result_printer_->OnEnvironmentsSetUpStart(unit_test);
}
void OnEnvironmentsSetUpEnd(const testing::UnitTest& unit_test) override {
default_result_printer_->OnEnvironmentsSetUpEnd(unit_test);
}
void OnTestCaseStart(const testing::TestCase& test_case) override {
default_result_printer_->OnTestCaseStart(test_case);
}
// Calls back to the collector to start collecting logs for this test.
void OnTestStart(const testing::TestInfo& test_info) override {
default_result_printer_->OnTestStart(test_info);
test_log_collector_->StartSessionForTest(test_info);
}
// Calls back to the collector with the partial result. If the collector
// does not handle it, it is given to the default result printer.
void OnTestPartResult(
const testing::TestPartResult& test_part_result) override {
if (!test_log_collector_->LogTestPartResult(test_part_result))
default_result_printer_->OnTestPartResult(test_part_result);
}
// Calls back to the collector to handle the collected log for the test that
// has just ended.
void OnTestEnd(const testing::TestInfo& test_info) override {
test_log_collector_->ProcessSessionForTest(test_info);
default_result_printer_->OnTestEnd(test_info);
}
void OnTestCaseEnd(const testing::TestCase& test_case) override {
default_result_printer_->OnTestCaseEnd(test_case);
}
void OnEnvironmentsTearDownStart(
const testing::UnitTest& unit_test) override {
default_result_printer_->OnEnvironmentsTearDownStart(unit_test);
}
void OnEnvironmentsTearDownEnd(
const testing::UnitTest& unit_test) override {
default_result_printer_->OnEnvironmentsTearDownEnd(unit_test);
}
void OnTestIterationEnd(const testing::UnitTest& unit_test,
int iteration) override {
default_result_printer_->OnTestIterationEnd(unit_test, iteration);
}
// Tears down the log collector.
void OnTestProgramEnd(const testing::UnitTest& unit_test) override {
default_result_printer_->OnTestProgramEnd(unit_test);
test_log_collector_->TearDown();
}
private:
TestLogCollector* test_log_collector_;
std::unique_ptr<testing::TestEventListener> default_result_printer_;
DISALLOW_COPY_AND_ASSIGN(EventListener);
};
// The Google Test unit test into which the collector has been installed.
testing::UnitTest* unit_test_;
// A temporary directory into which a log file is placed for the duration of
// each test. Created/destroyed at collector SetUp and TearDown.
base::ScopedTempDir log_temp_dir_;
// The test logger. Initialized/Unintitialized at collector SetUp and
// TearDown.
std::unique_ptr<FileLogger> file_logger_;
// The current log file. Valid only during a test.
base::FilePath log_file_;
// True if --also-emit-success-logs was specified on the command line.
bool also_emit_success_logs_;
DISALLOW_COPY_AND_ASSIGN(TestLogCollector);
};
base::LazyInstance<TestLogCollector>::DestructorAtExit g_test_log_collector =
LAZY_INSTANCE_INITIALIZER;
// TestLogCollector::EventListener implementation
TestLogCollector::EventListener::EventListener(
TestLogCollector* test_log_collector,
testing::TestEventListener* default_result_printer)
: test_log_collector_(test_log_collector),
default_result_printer_(default_result_printer) {
}
TestLogCollector::EventListener::~EventListener() {
}
// TestLogCollector implementation
TestLogCollector::TestLogCollector()
: unit_test_(NULL), also_emit_success_logs_(false) {
}
TestLogCollector::~TestLogCollector() {
}
void TestLogCollector::Initialize(testing::UnitTest* unit_test) {
if (unit_test_ != NULL) {
CHECK_EQ(unit_test, unit_test_)
<< "Cannot install the test log collector in multiple unit tests.";
return; // Already initialized.
}
// Remove the default result printer and install the collector's listener
// which delegates to the printer. If the default result printer has already
// been released, log an error and move on.
testing::TestEventListeners& listeners = unit_test->listeners();
testing::TestEventListener* default_result_printer =
listeners.default_result_printer();
if (default_result_printer == NULL) {
LOG(ERROR) << "Failed to initialize the test log collector on account of "
"another component having released the default result "
"printer.";
} else {
// Ownership of |default_release_printer| is passed to the new listener, and
// ownership of the new listener is passed to the unit test.
listeners.Append(
new EventListener(this, listeners.Release(default_result_printer)));
also_emit_success_logs_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAlsoEmitSuccessLogs);
unit_test_ = unit_test;
}
}
// Invoked by the listener at test program start to create the temporary log
// directory and initialize the logger.
void TestLogCollector::SetUp() {
if (!log_temp_dir_.CreateUniqueTempDir()) {
LOG(ERROR) << "Failed to create temporary directory to hold log files.";
} else {
file_logger_.reset(new FileLogger());
file_logger_->Initialize();
}
}
// Invoked by the listener at test start to begin collecting logs in a file.
void TestLogCollector::StartSessionForTest(const testing::TestInfo& test_info) {
if (log_temp_dir_.IsValid()) {
std::string log_file_name(test_info.name());
std::replace(log_file_name.begin(), log_file_name.end(), '/', '_');
log_file_name.append(kTraceLogExtension);
log_file_ = log_temp_dir_.GetPath().AppendASCII(log_file_name);
file_logger_->StartLogging(log_file_);
}
}
// Invoked by the listener when a test result is produced to log an event for
// the result.
bool TestLogCollector::LogTestPartResult(
const testing::TestPartResult& test_part_result) {
// Can't handle the event if no trace session.
if (!file_logger_.get() || !file_logger_->is_logging())
return false;
if (test_part_result.type() != testing::TestPartResult::kSuccess) {
// Approximate Google Test's message formatting.
LOG(ERROR)
<< base::StringPrintf("%s(%d): error: %s", test_part_result.file_name(),
test_part_result.line_number(),
test_part_result.message());
}
return true;
}
// Invoked by the listener at test end to dump the collected log in case of
// error.
void TestLogCollector::ProcessSessionForTest(
const testing::TestInfo& test_info) {
if (file_logger_.get() != NULL && file_logger_->is_logging()) {
file_logger_->StopLogging();
if (also_emit_success_logs_ || test_info.result()->Failed()) {
std::cerr << "----- log messages for "
<< test_info.test_case_name() << "." << test_info.name()
<< " above this line are repeated below -----" << std::endl;
// Dump the log to stderr.
logging_win::PrintLogFile(log_file_, &std::cerr);
std::cerr.flush();
}
if (!base::DeleteFile(log_file_, false))
LOG(ERROR) << "Failed to delete log file " << log_file_.value();
}
log_file_.clear();
}
// Invoked by the listener at test program end to shut down the logger and
// delete the temporary log directory.
void TestLogCollector::TearDown() {
file_logger_.reset();
ignore_result(log_temp_dir_.Delete());
}
} // namespace
void InstallTestLogCollector(testing::UnitTest* unit_test) {
// Must be called before running any tests.
DCHECK(unit_test);
DCHECK(!unit_test->current_test_case());
g_test_log_collector.Get().Initialize(unit_test);
}
} // namespace logging_win