// 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. #include "chrome_frame/test/net/fake_external_tab.h" #include #include "app/app_paths.h" #include "app/resource_bundle.h" #include "app/win_util.h" #include "base/command_line.h" #include "base/file_util.h" #include "base/i18n/icu_util.h" #include "base/path_service.h" #include "base/scoped_bstr_win.h" #include "base/scoped_comptr_win.h" #include "base/scoped_variant_win.h" #include "chrome/browser/browser_prefs.h" #include "chrome/browser/process_singleton.h" #include "chrome/browser/profile_manager.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths_internal.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome_frame/utils.h" #include "chrome_frame/test/chrome_frame_test_utils.h" #include "chrome_frame/test/net/dialog_watchdog.h" #include "chrome_frame/test/net/test_automation_resource_message_filter.h" namespace { // A special command line switch to allow developers to manually launch the // browser and debug CF inside the browser. const wchar_t kManualBrowserLaunch[] = L"manual-browser"; // Pops up a message box after the test environment has been set up // and before tearing it down. Useful for when debugging tests and not // the test environment that's been set up. const wchar_t kPromptAfterSetup[] = L"prompt-after-setup"; const int kTestServerPort = 4666; // The test HTML we use to initialize Chrome Frame. // Note that there's a little trick in there to avoid an extra URL request // that the browser will otherwise make for the site's favicon. // If we don't do this the browser will create a new URL request after // the CF page has been initialized and that URL request will confuse the // global URL instance counter in the unit tests and subsequently trip // some DCHECKs. const char kChromeFrameHtml[] = "" "" "" "Chrome Frame should now be loaded"; bool ShouldLaunchBrowser() { return !CommandLine::ForCurrentProcess()->HasSwitch(kManualBrowserLaunch); } bool PromptAfterSetup() { return CommandLine::ForCurrentProcess()->HasSwitch(kPromptAfterSetup); } } // end namespace FakeExternalTab::FakeExternalTab() { PathService::Get(chrome::DIR_USER_DATA, &overridden_user_dir_); GetProfilePath(&user_data_dir_); PathService::Override(chrome::DIR_USER_DATA, user_data_dir_); process_singleton_.reset(new ProcessSingleton(user_data_dir_)); } FakeExternalTab::~FakeExternalTab() { if (!overridden_user_dir_.empty()) { PathService::Override(chrome::DIR_USER_DATA, overridden_user_dir_); } } std::wstring FakeExternalTab::GetProfileName() { return L"iexplore"; } bool FakeExternalTab::GetProfilePath(FilePath* path) { if (!chrome::GetChromeFrameUserDataDirectory(path)) return false; *path = path->Append(GetProfileName()); return true; } void FakeExternalTab::Initialize() { DCHECK(g_browser_process == NULL); // The gears plugin causes the PluginRequestInterceptor to kick in and it // will cause problems when it tries to intercept URL requests. PathService::Override(chrome::FILE_GEARS_PLUGIN, FilePath()); icu_util::Initialize(); chrome::RegisterPathProvider(); app::RegisterPathProvider(); ResourceBundle::InitSharedInstance(L"en-US"); ResourceBundle::GetSharedInstance().LoadThemeResources(); const CommandLine* cmd = CommandLine::ForCurrentProcess(); browser_process_.reset(new BrowserProcessImpl(*cmd)); RenderProcessHost::set_run_renderer_in_process(true); // BrowserProcessImpl's constructor should set g_browser_process. DCHECK(g_browser_process); Profile* profile = g_browser_process->profile_manager()-> GetDefaultProfile(FilePath(user_data())); PrefService* prefs = profile->GetPrefs(); PrefService* local_state = browser_process_->local_state(); local_state->RegisterStringPref(prefs::kApplicationLocale, L""); local_state->RegisterBooleanPref(prefs::kMetricsReportingEnabled, false); browser::RegisterAllPrefs(prefs, local_state); // Override some settings to avoid hitting some preferences that have not // been registered. prefs->SetBoolean(prefs::kPasswordManagerEnabled, false); prefs->SetBoolean(prefs::kAlternateErrorPagesEnabled, false); prefs->SetBoolean(prefs::kSafeBrowsingEnabled, false); profile->InitExtensions(); } void FakeExternalTab::Shutdown() { browser_process_.reset(); g_browser_process = NULL; process_singleton_.reset(); ResourceBundle::CleanupSharedInstance(); } CFUrlRequestUnittestRunner::CFUrlRequestUnittestRunner(int argc, char** argv) : NetTestSuite(argc, argv), chrome_frame_html_("/chrome_frame", kChromeFrameHtml) { fake_chrome_.Initialize(); pss_subclass_.reset(new ProcessSingletonSubclass(this)); EXPECT_TRUE(pss_subclass_->Subclass(fake_chrome_.user_data())); StartChromeFrameInHostBrowser(); // Register the main thread by instantiating it, but don't call any methods. main_thread_.reset(new ChromeThread(ChromeThread::UI, MessageLoop::current())); DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); } CFUrlRequestUnittestRunner::~CFUrlRequestUnittestRunner() { fake_chrome_.Shutdown(); } DWORD WINAPI NavigateIE(void* param) { return 0; win_util::ScopedCOMInitializer com; BSTR url = reinterpret_cast(param); bool found = false; int retries = 0; const int kMaxRetries = 20; while (!found && retries < kMaxRetries) { ScopedComPtr windows; HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL, IID_IShellWindows, reinterpret_cast(windows.Receive())); DCHECK(SUCCEEDED(hr)) << "CoCreateInstance"; if (SUCCEEDED(hr)) { hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); long count = 0; // NOLINT windows->get_Count(&count); VARIANT i = { VT_I4 }; for (i.lVal = 0; i.lVal < count; ++i.lVal) { ScopedComPtr folder; windows->Item(i, folder.Receive()); if (folder != NULL) { ScopedComPtr browser; if (SUCCEEDED(browser.QueryFrom(folder))) { found = true; browser->Stop(); Sleep(1000); VARIANT empty = ScopedVariant::kEmptyVariant; hr = browser->Navigate(url, &empty, &empty, &empty, &empty); DCHECK(SUCCEEDED(hr)) << "Failed to navigate"; break; } } } } if (!found) { DLOG(INFO) << "Waiting for browser to initialize..."; ::Sleep(100); retries++; } } DCHECK(retries < kMaxRetries); DCHECK(found); ::SysFreeString(url); return 0; } void CFUrlRequestUnittestRunner::StartChromeFrameInHostBrowser() { if (!ShouldLaunchBrowser()) return; win_util::ScopedCOMInitializer com; chrome_frame_test::CloseAllIEWindows(); test_http_server_.reset(new test_server::SimpleWebServer(kTestServerPort)); test_http_server_->AddResponse(&chrome_frame_html_); std::wstring url(StringPrintf(L"http://localhost:%i/chrome_frame", kTestServerPort).c_str()); // Launch IE. This launches IE correctly on Vista too. ScopedHandle ie_process(chrome_frame_test::LaunchIE(url)); EXPECT_TRUE(ie_process.IsValid()); // NOTE: If you're running IE8 and CF is not being loaded, you need to // disable IE8's prebinding until CF properly handles that situation. // // HKCU\Software\Microsoft\Internet Explorer\Main // Value name: EnablePreBinding (REG_DWORD) // Value: 0 } void CFUrlRequestUnittestRunner::ShutDownHostBrowser() { if (ShouldLaunchBrowser()) { win_util::ScopedCOMInitializer com; chrome_frame_test::CloseAllIEWindows(); } } // Override virtual void Initialize to not call icu initialize void CFUrlRequestUnittestRunner::Initialize() { DCHECK(::GetCurrentThreadId() == test_thread_id_); // Start by replicating some of the steps that would otherwise be // done by TestSuite::Initialize. We can't call the base class // directly because it will attempt to initialize some things such as // ICU that have already been initialized for this process. InitializeLogging(); base::Time::EnableHiResClockForTests(); #if !defined(PURIFY) && defined(OS_WIN) logging::SetLogAssertHandler(UnitTestAssertHandler); #endif // !defined(PURIFY) // Next, do some initialization for NetTestSuite. NetTestSuite::InitializeTestThread(); } void CFUrlRequestUnittestRunner::Shutdown() { DCHECK(::GetCurrentThreadId() == test_thread_id_); NetTestSuite::Shutdown(); } void CFUrlRequestUnittestRunner::OnConnectAutomationProviderToChannel( const std::string& channel_id) { Profile* profile = g_browser_process->profile_manager()-> GetDefaultProfile(fake_chrome_.user_data()); AutomationProviderList* list = g_browser_process->InitAutomationProviderList(); DCHECK(list); list->AddProvider(TestAutomationProvider::NewAutomationProvider(profile, channel_id, this)); } void CFUrlRequestUnittestRunner::OnInitialTabLoaded() { test_http_server_.reset(); StartTests(); } void CFUrlRequestUnittestRunner::RunMainUIThread() { DCHECK(MessageLoop::current()); DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); MessageLoop::current()->Run(); } void CFUrlRequestUnittestRunner::StartTests() { if (PromptAfterSetup()) MessageBoxA(NULL, "click ok to run", "", MB_OK); DCHECK_EQ(test_thread_.IsValid(), false); test_thread_.Set(::CreateThread(NULL, 0, RunAllUnittests, this, 0, &test_thread_id_)); DCHECK(test_thread_.IsValid()); } // static DWORD CFUrlRequestUnittestRunner::RunAllUnittests(void* param) { PlatformThread::SetName("CFUrlRequestUnittestRunner"); CFUrlRequestUnittestRunner* me = reinterpret_cast(param); me->Run(); me->fake_chrome_.ui_loop()->PostTask(FROM_HERE, NewRunnableFunction(TakeDownBrowser, me)); return 0; } // static void CFUrlRequestUnittestRunner::TakeDownBrowser( CFUrlRequestUnittestRunner* me) { if (PromptAfterSetup()) MessageBoxA(NULL, "click ok to exit", "", MB_OK); me->ShutDownHostBrowser(); } void CFUrlRequestUnittestRunner::InitializeLogging() { 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); } void FilterDisabledTests() { if (::testing::FLAGS_gtest_filter.length() && ::testing::FLAGS_gtest_filter.Compare("*") != 0) { // Don't override user specified filters. return; } const char* disabled_tests[] = { // Tests disabled since they're testing the same functionality used // by the TestAutomationProvider. "URLRequestTest.InterceptNetworkError", "URLRequestTest.InterceptRestartRequired", "URLRequestTest.InterceptRespectsCancelMain", "URLRequestTest.InterceptRespectsCancelRedirect", "URLRequestTest.InterceptRespectsCancelFinal", "URLRequestTest.InterceptRespectsCancelInRestart", "URLRequestTest.InterceptRedirect", "URLRequestTest.InterceptServerError", "URLRequestTestFTP.*", // Tests that are currently not working: // Temporarily disabled because they needs user input (login dialog). "URLRequestTestHTTP.BasicAuth", "URLRequestTestHTTP.BasicAuthWithCookies", // HTTPS tests temporarily disabled due to the certificate error dialog. // TODO(tommi): The tests currently fail though, so need to fix. "HTTPSRequestTest.HTTPSMismatchedTest", "HTTPSRequestTest.HTTPSExpiredTest", // Tests chrome's network stack's cache (might not apply to CF). "URLRequestTestHTTP.VaryHeader", // I suspect we can only get this one to work (if at all) on IE8 and // later by using the new INTERNET_OPTION_SUPPRESS_BEHAVIOR flags // See http://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx "URLRequestTest.DoNotSaveCookies", }; std::string filter("-"); // All following filters will be negative. for (int i = 0; i < arraysize(disabled_tests); ++i) { if (i > 0) filter += ":"; filter += disabled_tests[i]; } ::testing::FLAGS_gtest_filter = filter; } int main(int argc, char** argv) { DialogWatchdog watchdog; // See url_request_unittest.cc for these credentials. SupplyProxyCredentials credentials("user", "secret"); watchdog.AddObserver(&credentials); testing::InitGoogleTest(&argc, argv); FilterDisabledTests(); CFUrlRequestUnittestRunner test_suite(argc, argv); test_suite.RunMainUIThread(); return 0; }