// 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 "base/test/test_suite.h"
#include "base/at_exit.h"
#include "base/base_paths.h"
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/debugger.h"
#include "base/debug/stack_trace.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/i18n/icu_util.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/process/memory.h"
#include "base/test/gtest_xml_util.h"
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_switches.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
#if defined(OS_MACOSX)
#include "base/mac/scoped_nsautorelease_pool.h"
#if defined(OS_IOS)
#include "base/test/test_listener_ios.h"
#else
#include "base/test/mock_chrome_application_mac.h"
#endif // OS_IOS
#endif // OS_MACOSX
#if defined(OS_ANDROID)
#include "base/test/test_support_android.h"
#endif
#if defined(OS_IOS)
#include "base/test/test_support_ios.h"
#endif
namespace {
class MaybeTestDisabler : public testing::EmptyTestEventListener {
public:
virtual void OnTestStart(const testing::TestInfo& test_info) OVERRIDE {
ASSERT_FALSE(TestSuite::IsMarkedMaybe(test_info))
<< "Probably the OS #ifdefs don't include all of the necessary "
"platforms.\nPlease ensure that no tests have the MAYBE_ prefix "
"after the code is preprocessed.";
}
};
class TestClientInitializer : public testing::EmptyTestEventListener {
public:
TestClientInitializer()
: old_command_line_(CommandLine::NO_PROGRAM) {
}
virtual void OnTestStart(const testing::TestInfo& test_info) OVERRIDE {
old_command_line_ = *CommandLine::ForCurrentProcess();
}
virtual void OnTestEnd(const testing::TestInfo& test_info) OVERRIDE {
*CommandLine::ForCurrentProcess() = old_command_line_;
}
private:
CommandLine old_command_line_;
DISALLOW_COPY_AND_ASSIGN(TestClientInitializer);
};
} // namespace
namespace base {
int RunUnitTestsUsingBaseTestSuite(int argc, char **argv) {
TestSuite test_suite(argc, argv);
return base::LaunchUnitTests(
argc, argv, Bind(&TestSuite::Run, Unretained(&test_suite)));
}
} // namespace base
TestSuite::TestSuite(int argc, char** argv) : initialized_command_line_(false) {
PreInitialize(true);
InitializeFromCommandLine(argc, argv);
}
#if defined(OS_WIN)
TestSuite::TestSuite(int argc, wchar_t** argv)
: initialized_command_line_(false) {
PreInitialize(true);
InitializeFromCommandLine(argc, argv);
}
#endif // defined(OS_WIN)
TestSuite::TestSuite(int argc, char** argv, bool create_at_exit_manager)
: initialized_command_line_(false) {
PreInitialize(create_at_exit_manager);
InitializeFromCommandLine(argc, argv);
}
TestSuite::~TestSuite() {
if (initialized_command_line_)
CommandLine::Reset();
}
void TestSuite::InitializeFromCommandLine(int argc, char** argv) {
initialized_command_line_ = CommandLine::Init(argc, argv);
testing::InitGoogleTest(&argc, argv);
testing::InitGoogleMock(&argc, argv);
#if defined(OS_IOS)
InitIOSRunHook(this, argc, argv);
#endif
}
#if defined(OS_WIN)
void TestSuite::InitializeFromCommandLine(int argc, wchar_t** argv) {
// Windows CommandLine::Init ignores argv anyway.
initialized_command_line_ = CommandLine::Init(argc, NULL);
testing::InitGoogleTest(&argc, argv);
testing::InitGoogleMock(&argc, argv);
}
#endif // defined(OS_WIN)
void TestSuite::PreInitialize(bool create_at_exit_manager) {
#if defined(OS_WIN)
testing::GTEST_FLAG(catch_exceptions) = false;
base::TimeTicks::SetNowIsHighResNowIfSupported();
#endif
base::EnableTerminationOnHeapCorruption();
#if defined(OS_LINUX) && defined(USE_AURA)
// When calling native char conversion functions (e.g wrctomb) we need to
// have the locale set. In the absence of such a call the "C" locale is the
// default. In the gtk code (below) gtk_init() implicitly sets a locale.
setlocale(LC_ALL, "");
#endif // defined(OS_LINUX) && defined(USE_AURA)
// On Android, AtExitManager is created in
// testing/android/native_test_wrapper.cc before main() is called.
#if !defined(OS_ANDROID)
if (create_at_exit_manager)
at_exit_manager_.reset(new base::AtExitManager);
#endif
// Don't add additional code to this function. Instead add it to
// Initialize(). See bug 6436.
}
// static
bool TestSuite::IsMarkedMaybe(const testing::TestInfo& test) {
return strncmp(test.name(), "MAYBE_", 6) == 0;
}
void TestSuite::CatchMaybeTests() {
testing::TestEventListeners& listeners =
testing::UnitTest::GetInstance()->listeners();
listeners.Append(new MaybeTestDisabler);
}
void TestSuite::ResetCommandLine() {
testing::TestEventListeners& listeners =
testing::UnitTest::GetInstance()->listeners();
listeners.Append(new TestClientInitializer);
}
#if !defined(OS_IOS)
void TestSuite::AddTestLauncherResultPrinter() {
// Only add the custom printer if requested.
if (!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kTestLauncherOutput)) {
return;
}
FilePath output_path(CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kTestLauncherOutput));
// Do not add the result printer if output path already exists. It's an
// indicator there is a process printing to that file, and we're likely
// its child. Do not clobber the results in that case.
if (PathExists(output_path)) {
LOG(WARNING) << "Test launcher output path " << output_path.AsUTF8Unsafe()
<< " exists. Not adding test launcher result printer.";
return;
}
XmlUnitTestResultPrinter* printer = new XmlUnitTestResultPrinter;
CHECK(printer->Initialize(output_path));
testing::TestEventListeners& listeners =
testing::UnitTest::GetInstance()->listeners();
listeners.Append(printer);
}
#endif // !defined(OS_IOS)
// Don't add additional code to this method. Instead add it to
// Initialize(). See bug 6436.
int TestSuite::Run() {
#if defined(OS_IOS)
RunTestsFromIOSApp();
#endif
#if defined(OS_MACOSX)
base::mac::ScopedNSAutoreleasePool scoped_pool;
#endif
Initialize();
std::string client_func =
CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kTestChildProcess);
// Check to see if we are being run as a client process.
if (!client_func.empty())
return multi_process_function_list::InvokeChildProcessTest(client_func);
#if defined(OS_IOS)
base::test_listener_ios::RegisterTestEndListener();
#endif
int result = RUN_ALL_TESTS();
#if defined(OS_MACOSX)
// 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();
#endif
Shutdown();
return result;
}
// static
void TestSuite::UnitTestAssertHandler(const std::string& str) {
#if defined(OS_ANDROID)
// Correlating test stdio with logcat can be difficult, so we emit this
// helpful little hint about what was running. Only do this for Android
// because other platforms don't separate out the relevant logs in the same
// way.
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
if (test_info) {
LOG(ERROR) << "Currently running: " << test_info->test_case_name() << "."
<< test_info->name();
fflush(stderr);
}
#endif // defined(OS_ANDROID)
// The logging system actually prints the message before calling the assert
// handler. Just exit now to avoid printing too many stack traces.
_exit(1);
}
void TestSuite::SuppressErrorDialogs() {
#if defined(OS_WIN)
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);
#if defined(_DEBUG) && defined(_HAS_EXCEPTIONS) && (_HAS_EXCEPTIONS == 1)
// Suppress the "Debug Assertion Failed" dialog.
// TODO(hbono): remove this code when gtest has it.
// http://groups.google.com/d/topic/googletestframework/OjuwNlXy5ac/discussion
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
#endif // defined(_DEBUG) && defined(_HAS_EXCEPTIONS) && (_HAS_EXCEPTIONS == 1)
#endif // defined(OS_WIN)
}
void TestSuite::Initialize() {
#if defined(OS_MACOSX) && !defined(OS_IOS)
// Some of the app unit tests spin runloops.
mock_cr_app::RegisterMockCrApp();
#endif
#if defined(OS_IOS)
InitIOSTestMessageLoop();
#endif // OS_IOS
#if defined(OS_ANDROID)
InitAndroidTest();
#else
// Initialize logging.
base::FilePath exe;
PathService::Get(base::FILE_EXE, &exe);
base::FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log"));
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_ALL;
settings.log_file = log_filename.value().c_str();
settings.delete_old = logging::DELETE_OLD_LOG_FILE;
logging::InitLogging(settings);
// 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);
#endif // else defined(OS_ANDROID)
CHECK(base::debug::EnableInProcessStackDumping());
#if defined(OS_WIN)
// Make sure we run with high resolution timer to minimize differences
// between production code and test code.
base::Time::EnableHighResolutionTimer(true);
#endif // defined(OS_WIN)
// In some cases, we do not want to see standard error dialogs.
if (!base::debug::BeingDebugged() &&
!CommandLine::ForCurrentProcess()->HasSwitch("show-error-dialogs")) {
SuppressErrorDialogs();
base::debug::SetSuppressDebugUI(true);
logging::SetLogAssertHandler(UnitTestAssertHandler);
}
base::i18n::InitializeICU();
CatchMaybeTests();
ResetCommandLine();
#if !defined(OS_IOS)
AddTestLauncherResultPrinter();
#endif // !defined(OS_IOS)
TestTimeouts::Initialize();
}
void TestSuite::Shutdown() {
}