// Copyright (c) 2009 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.
#ifndef BASE_TEST_TEST_SUITE_H_
#define BASE_TEST_TEST_SUITE_H_
// Defines a basic test suite framework for running gtest based tests. You can
// instantiate this class in your main function and call its Run method to run
// any gtest based tests that are linked into your executable.
#include "base/at_exit.h"
#include "base/base_paths.h"
#include "base/debug_on_start.h"
#include "base/i18n/icu_util.h"
#include "base/multiprocess_test.h"
#include "base/nss_util.h"
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/scoped_nsautorelease_pool.h"
#include "base/scoped_ptr.h"
#include "base/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
#if defined(OS_POSIX) && !defined(OS_MACOSX)
#include <gtk/gtk.h>
#endif
// Match function used by the GetTestCount method.
typedef bool (*TestMatch)(const testing::TestInfo&);
// By setting up a shadow AtExitManager, this test event listener ensures that
// no state is carried between tests (like singletons, lazy instances, etc).
// Of course it won't help if the code under test corrupts memory.
class TestIsolationEnforcer : public testing::EmptyTestEventListener {
public:
virtual void OnTestStart(const testing::TestInfo& test_info) {
ASSERT_FALSE(exit_manager_.get());
exit_manager_.reset(new base::ShadowingAtExitManager());
}
virtual void OnTestEnd(const testing::TestInfo& test_info) {
ASSERT_TRUE(exit_manager_.get());
exit_manager_.reset();
}
private:
scoped_ptr<base::ShadowingAtExitManager> exit_manager_;
};
class TestSuite {
public:
TestSuite(int argc, char** argv) {
base::EnableTerminationOnHeapCorruption();
CommandLine::Init(argc, argv);
testing::InitGoogleTest(&argc, argv);
#if defined(OS_POSIX) && !defined(OS_MACOSX)
g_thread_init(NULL);
gtk_init_check(&argc, &argv);
#endif // defined(OS_LINUX)
// Don't add additional code to this constructor. Instead add it to
// Initialize(). See bug 6436.
}
virtual ~TestSuite() {
CommandLine::Reset();
}
// Returns true if a string starts with FLAKY_.
static bool IsFlaky(const char* name) {
return strncmp(name, "FLAKY_", 6) == 0;
}
// Returns true if the test is marked as flaky.
static bool FlakyTest(const testing::TestInfo& test) {
return IsFlaky(test.name()) || IsFlaky(test.test_case_name());
}
// Returns true if the test failed and is not marked as flaky.
static bool NonFlakyFailures(const testing::TestInfo& test) {
return test.should_run() && test.result()->Failed() && !FlakyTest(test);
}
// Returns the number of tests where the match function returns true.
int GetTestCount(TestMatch test_match) {
testing::UnitTest* instance = testing::UnitTest::GetInstance();
int count = 0;
for (int i = 0; i < instance->total_test_case_count(); ++i) {
const testing::TestCase& test_case = *instance->GetTestCase(i);
for (int j = 0; j < test_case.total_test_count(); ++j) {
if (test_match(*test_case.GetTestInfo(j))) {
count++;
}
}
}
return count;
}
// TODO(phajdan.jr): Enforce isolation for all tests once it's stable.
void EnforceTestIsolation() {
testing::TestEventListeners& listeners =
testing::UnitTest::GetInstance()->listeners();
listeners.Append(new TestIsolationEnforcer);
}
// Don't add additional code to this method. Instead add it to
// Initialize(). See bug 6436.
int Run() {
base::ScopedNSAutoreleasePool scoped_pool;
Initialize();
std::wstring client_func =
CommandLine::ForCurrentProcess()->GetSwitchValue(kRunClientProcess);
// Check to see if we are being run as a client process.
if (!client_func.empty()) {
// Convert our function name to a usable string for GetProcAddress.
std::string func_name(client_func.begin(), client_func.end());
return multi_process_function_list::InvokeChildProcessTest(func_name);
}
int result = RUN_ALL_TESTS();
// Reset the result code if only flaky test failed.
if (result != 0 && GetTestCount(&TestSuite::NonFlakyFailures) == 0) {
result = 0;
}
// Display the number of flaky tests.
int flaky_count = GetTestCount(&TestSuite::FlakyTest);
if (flaky_count) {
printf(" YOU HAVE %d FLAKY %s\n\n", flaky_count,
flaky_count == 1 ? "TEST" : "TESTS");
}
// This MUST happen before Shutdown() since Shutdown() tears down
// objects (such as NotificationService::current()) that Cocoa
// objects use to remove themselves as observers.
scoped_pool.Recycle();
Shutdown();
return result;
}
protected:
#if defined(OS_WIN)
// TODO(phajdan.jr): Clean up the windows-specific hacks.
// See http://crbug.com/29997
// By default, base::LogMessage::~LogMessage calls DebugUtil::BreakDebugger()
// when severity is LOG_FATAL. On Windows, this results in error dialogs
// which are not friendly to buildbots.
// To avoid these problems, we override the LogMessage behaviour by
// replacing the assert handler with UnitTestAssertHandler.
static void UnitTestAssertHandler(const std::string& str) {
// FAIL is a googletest macro, it marks the current test as failed.
// If throw_on_failure is set to true, it also ends the process.
::testing::FLAGS_gtest_throw_on_failure = true;
FAIL() << str;
}
// Disable crash dialogs so that it doesn't gum up the buildbot
virtual void SuppressErrorDialogs() {
UINT new_flags = SEM_FAILCRITICALERRORS |
SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX;
// Preserve existing error mode, as discussed at
// http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx
UINT existing_flags = SetErrorMode(new_flags);
SetErrorMode(existing_flags | new_flags);
}
#endif // defined(OS_WIN)
// Override these for custom initialization and shutdown handling. Use these
// instead of putting complex code in your constructor/destructor.
virtual void Initialize() {
// Initialize logging.
FilePath exe;
PathService::Get(base::FILE_EXE, &exe);
FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log"));
logging::InitLogging(log_filename.value().c_str(),
logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG,
logging::LOCK_LOG_FILE,
logging::DELETE_OLD_LOG_FILE);
// We want process and thread IDs because we may have multiple processes.
// Note: temporarily enabled timestamps in an effort to catch bug 6361.
logging::SetLogItems(true, true, true, true);
CHECK(base::EnableInProcessStackDumping());
#if defined(OS_WIN)
// Make sure we run with high resolution timer to minimize differences
// between production code and test code.
bool result = base::Time::UseHighResolutionTimer(true);
CHECK(result);
// In some cases, we do not want to see standard error dialogs.
if (!IsDebuggerPresent() &&
!CommandLine::ForCurrentProcess()->HasSwitch("show-error-dialogs")) {
SuppressErrorDialogs();
#if !defined(PURIFY)
// When the code in this file moved around, bug 6436 resurfaced.
// As a hack workaround, just #ifdef out this code for Purify builds.
logging::SetLogAssertHandler(UnitTestAssertHandler);
#endif // !defined(PURIFY)
}
#endif // defined(OS_WIN)
icu_util::Initialize();
#if defined(USE_NSS)
// Trying to repeatedly initialize and cleanup NSS and NSPR may result in
// a deadlock. Such repeated initialization will happen when using test
// isolation. Prevent problems by initializing NSS here, so that the cleanup
// will be done only on process exit.
base::EnsureNSSInit();
#endif // defined(USE_NSS)
}
virtual void Shutdown() {
}
// Make sure that we setup an AtExitManager so Singleton objects will be
// destroyed.
base::AtExitManager at_exit_manager_;
};
#endif // BASE_TEST_TEST_SUITE_H_