|  | // 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 <signal.h> | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #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/profiler.h" | 
|  | #include "base/debug/stack_trace.h" | 
|  | #include "base/feature_list.h" | 
|  | #include "base/files/file_path.h" | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/i18n/icu_util.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/path_service.h" | 
|  | #include "base/process/launch.h" | 
|  | #include "base/process/memory.h" | 
|  | #include "base/test/gtest_xml_unittest_result_printer.h" | 
|  | #include "base/test/gtest_xml_util.h" | 
|  | #include "base/test/icu_test_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 "build/build_config.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" | 
|  | #endif  // OS_IOS | 
|  | #endif  // OS_MACOSX | 
|  |  | 
|  | #if !defined(OS_WIN) | 
|  | #include "base/i18n/rtl.h" | 
|  | #if !defined(OS_IOS) | 
|  | #include "base/strings/string_util.h" | 
|  | #include "third_party/icu/source/common/unicode/uloc.h" | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | #if defined(OS_ANDROID) | 
|  | #include "base/test/test_support_android.h" | 
|  | #endif | 
|  |  | 
|  | #if defined(OS_IOS) | 
|  | #include "base/test/test_support_ios.h" | 
|  | #endif | 
|  |  | 
|  | #if defined(OS_LINUX) | 
|  | #include "base/test/fontconfig_util_linux.h" | 
|  | #endif | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class MaybeTestDisabler : public testing::EmptyTestEventListener { | 
|  | public: | 
|  | 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) { | 
|  | } | 
|  |  | 
|  | void OnTestStart(const testing::TestInfo& test_info) override { | 
|  | old_command_line_ = *CommandLine::ForCurrentProcess(); | 
|  | } | 
|  |  | 
|  | void OnTestEnd(const testing::TestInfo& test_info) override { | 
|  | *CommandLine::ForCurrentProcess() = old_command_line_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | CommandLine old_command_line_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(TestClientInitializer); | 
|  | }; | 
|  |  | 
|  | std::string GetProfileName() { | 
|  | static const char kDefaultProfileName[] = "test-profile-{pid}"; | 
|  | CR_DEFINE_STATIC_LOCAL(std::string, profile_name, ()); | 
|  | if (profile_name.empty()) { | 
|  | const base::CommandLine& command_line = | 
|  | *base::CommandLine::ForCurrentProcess(); | 
|  | if (command_line.HasSwitch(switches::kProfilingFile)) | 
|  | profile_name = command_line.GetSwitchValueASCII(switches::kProfilingFile); | 
|  | else | 
|  | profile_name = std::string(kDefaultProfileName); | 
|  | } | 
|  | return profile_name; | 
|  | } | 
|  |  | 
|  | void InitializeLogging() { | 
|  | #if defined(OS_ANDROID) | 
|  | InitAndroidTestLogging(); | 
|  | #else | 
|  | FilePath exe; | 
|  | PathService::Get(FILE_EXE, &exe); | 
|  | 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  // !defined(OS_ANDROID) | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | int RunUnitTestsUsingBaseTestSuite(int argc, char **argv) { | 
|  | TestSuite test_suite(argc, argv); | 
|  | return LaunchUnitTests(argc, argv, | 
|  | Bind(&TestSuite::Run, Unretained(&test_suite))); | 
|  | } | 
|  |  | 
|  | TestSuite::TestSuite(int argc, char** argv) : initialized_command_line_(false) { | 
|  | PreInitialize(); | 
|  | InitializeFromCommandLine(argc, argv); | 
|  | // Logging must be initialized before any thread has a chance to call logging | 
|  | // functions. | 
|  | InitializeLogging(); | 
|  | } | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | TestSuite::TestSuite(int argc, wchar_t** argv) | 
|  | : initialized_command_line_(false) { | 
|  | PreInitialize(); | 
|  | InitializeFromCommandLine(argc, argv); | 
|  | // Logging must be initialized before any thread has a chance to call logging | 
|  | // functions. | 
|  | InitializeLogging(); | 
|  | } | 
|  | #endif  // defined(OS_WIN) | 
|  |  | 
|  | 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() { | 
|  | #if defined(OS_WIN) | 
|  | testing::GTEST_FLAG(catch_exceptions) = false; | 
|  | #endif | 
|  | 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) | 
|  | at_exit_manager_.reset(new 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); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | printer_ = new XmlUnitTestResultPrinter; | 
|  | CHECK(printer_->Initialize(output_path)); | 
|  | testing::TestEventListeners& listeners = | 
|  | testing::UnitTest::GetInstance()->listeners(); | 
|  | listeners.Append(printer_); | 
|  | } | 
|  |  | 
|  | // 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) | 
|  | 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) | 
|  | 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; | 
|  | } | 
|  |  | 
|  | void TestSuite::UnitTestAssertHandler(const char* file, | 
|  | int line, | 
|  | const base::StringPiece summary, | 
|  | const base::StringPiece stack_trace) { | 
|  | #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) | 
|  |  | 
|  | // XmlUnitTestResultPrinter inherits gtest format, where assert has summary | 
|  | // and message. In GTest, summary is just a logged text, and message is a | 
|  | // logged text, concatenated with stack trace of assert. | 
|  | // Concatenate summary and stack_trace here, to pass it as a message. | 
|  | if (printer_) { | 
|  | const std::string summary_str = summary.as_string(); | 
|  | const std::string stack_trace_str = summary_str + stack_trace.as_string(); | 
|  | printer_->OnAssert(file, line, summary_str, stack_trace_str); | 
|  | } | 
|  |  | 
|  | // The logging system actually prints the message before calling the assert | 
|  | // handler. Just exit now to avoid printing too many stack traces. | 
|  | _exit(1); | 
|  | } | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | namespace { | 
|  |  | 
|  | // Disable optimizations to prevent function folding or other transformations | 
|  | // that will make the call stacks on failures more confusing. | 
|  | #pragma optimize("", off) | 
|  | // Handlers for invalid parameter, pure call, and abort. They generate a | 
|  | // breakpoint to ensure that we get a call stack on these failures. | 
|  | void InvalidParameter(const wchar_t* expression, | 
|  | const wchar_t* function, | 
|  | const wchar_t* file, | 
|  | unsigned int line, | 
|  | uintptr_t reserved) { | 
|  | // CRT printed message is sufficient. | 
|  | __debugbreak(); | 
|  | _exit(1); | 
|  | } | 
|  |  | 
|  | void PureCall() { | 
|  | fprintf(stderr, "Pure-virtual function call. Terminating.\n"); | 
|  | __debugbreak(); | 
|  | _exit(1); | 
|  | } | 
|  |  | 
|  | void AbortHandler(int signal) { | 
|  | // Print EOL after the CRT abort message. | 
|  | fprintf(stderr, "\n"); | 
|  | __debugbreak(); | 
|  | } | 
|  | #pragma optimize("", on) | 
|  |  | 
|  | }  // namespace | 
|  | #endif | 
|  |  | 
|  | 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) | 
|  | // Suppress the "Debug Assertion Failed" dialog. | 
|  | // TODO(hbono): remove this code when gtest has it. | 
|  | // http://groups.google.com/d/topic/googletestframework/OjuwNlXy5ac/discussion | 
|  | _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); | 
|  | _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); | 
|  | _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); | 
|  | _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); | 
|  | #endif  // defined(_DEBUG) | 
|  |  | 
|  | // See crbug.com/783040 for test code to trigger all of these failures. | 
|  | _set_invalid_parameter_handler(InvalidParameter); | 
|  | _set_purecall_handler(PureCall); | 
|  | signal(SIGABRT, AbortHandler); | 
|  | #endif  // defined(OS_WIN) | 
|  | } | 
|  |  | 
|  | void TestSuite::Initialize() { | 
|  | const CommandLine* command_line = CommandLine::ForCurrentProcess(); | 
|  | #if !defined(OS_IOS) | 
|  | if (command_line->HasSwitch(switches::kWaitForDebugger)) { | 
|  | debug::WaitForDebugger(60, true); | 
|  | } | 
|  | #endif | 
|  | // Set up a FeatureList instance, so that code using that API will not hit a | 
|  | // an error that it's not set. It will be cleared automatically. | 
|  | // TestFeatureForBrowserTest1 and TestFeatureForBrowserTest2 used in | 
|  | // ContentBrowserTestScopedFeatureListTest to ensure ScopedFeatureList keeps | 
|  | // features from command line. | 
|  | std::string enabled = | 
|  | command_line->GetSwitchValueASCII(switches::kEnableFeatures); | 
|  | std::string disabled = | 
|  | command_line->GetSwitchValueASCII(switches::kDisableFeatures); | 
|  | enabled += ",TestFeatureForBrowserTest1"; | 
|  | disabled += ",TestFeatureForBrowserTest2"; | 
|  | scoped_feature_list_.InitFromCommandLine(enabled, disabled); | 
|  |  | 
|  | // The enable-features and disable-features flags were just slurped into a | 
|  | // FeatureList, so remove them from the command line. Tests should enable and | 
|  | // disable features via the ScopedFeatureList API rather than command-line | 
|  | // flags. | 
|  | CommandLine new_command_line(command_line->GetProgram()); | 
|  | CommandLine::SwitchMap switches = command_line->GetSwitches(); | 
|  |  | 
|  | switches.erase(switches::kEnableFeatures); | 
|  | switches.erase(switches::kDisableFeatures); | 
|  |  | 
|  | for (const auto& iter : switches) | 
|  | new_command_line.AppendSwitchNative(iter.first, iter.second); | 
|  |  | 
|  | *CommandLine::ForCurrentProcess() = new_command_line; | 
|  |  | 
|  | #if defined(OS_IOS) | 
|  | InitIOSTestMessageLoop(); | 
|  | #endif  // OS_IOS | 
|  |  | 
|  | #if defined(OS_ANDROID) | 
|  | InitAndroidTestMessageLoop(); | 
|  | #endif  // else defined(OS_ANDROID) | 
|  |  | 
|  | CHECK(debug::EnableInProcessStackDumping()); | 
|  | #if defined(OS_WIN) | 
|  | RouteStdioToConsole(true); | 
|  | // Make sure we run with high resolution timer to minimize differences | 
|  | // between production code and test code. | 
|  | Time::EnableHighResolutionTimer(true); | 
|  | #endif  // defined(OS_WIN) | 
|  |  | 
|  | // In some cases, we do not want to see standard error dialogs. | 
|  | if (!debug::BeingDebugged() && | 
|  | !command_line->HasSwitch("show-error-dialogs")) { | 
|  | SuppressErrorDialogs(); | 
|  | debug::SetSuppressDebugUI(true); | 
|  | assert_handler_ = std::make_unique<logging::ScopedLogAssertHandler>( | 
|  | base::Bind(&TestSuite::UnitTestAssertHandler, base::Unretained(this))); | 
|  | } | 
|  |  | 
|  | base::test::InitializeICUForTesting(); | 
|  |  | 
|  | // On the Mac OS X command line, the default locale is *_POSIX. In Chromium, | 
|  | // the locale is set via an OS X locale API and is never *_POSIX. | 
|  | // Some tests (such as those involving word break iterator) will behave | 
|  | // differently and fail if we use *POSIX locale. Setting it to en_US here | 
|  | // does not affect tests that explicitly overrides the locale for testing. | 
|  | // This can be an issue on all platforms other than Windows. | 
|  | // TODO(jshin): Should we set the locale via an OS X locale API here? | 
|  | #if !defined(OS_WIN) | 
|  | #if defined(OS_IOS) | 
|  | i18n::SetICUDefaultLocale("en_US"); | 
|  | #else | 
|  | std::string default_locale(uloc_getDefault()); | 
|  | if (EndsWith(default_locale, "POSIX", CompareCase::INSENSITIVE_ASCII)) | 
|  | i18n::SetICUDefaultLocale("en_US"); | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | #if defined(OS_LINUX) | 
|  | // TODO(thomasanderson): Call TearDownFontconfig() in Shutdown().  It would | 
|  | // currently crash because of leaked FcFontSet's in font_fallback_linux.cc. | 
|  | SetUpFontconfig(); | 
|  | #endif | 
|  |  | 
|  | CatchMaybeTests(); | 
|  | ResetCommandLine(); | 
|  | AddTestLauncherResultPrinter(); | 
|  |  | 
|  | TestTimeouts::Initialize(); | 
|  |  | 
|  | trace_to_file_.BeginTracingFromCommandLineOptions(); | 
|  |  | 
|  | base::debug::StartProfiling(GetProfileName()); | 
|  | } | 
|  |  | 
|  | void TestSuite::Shutdown() { | 
|  | base::debug::StopProfiling(); | 
|  | } | 
|  |  | 
|  | }  // namespace base |