| // Copyright (c) 2011 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/in_process_browser_test.h" |
| |
| #include "base/command_line.h" |
| #include "base/file_path.h" |
| #include "base/file_util.h" |
| #include "base/mac/scoped_nsautorelease_pool.h" |
| #include "base/path_service.h" |
| #include "base/string_number_conversions.h" |
| #include "base/test/test_file_util.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/browser_shutdown.h" |
| #include "chrome/browser/intranet_redirect_detector.h" |
| #include "chrome/browser/io_thread.h" |
| #include "chrome/browser/net/url_request_mock_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_navigator.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/logging_chrome.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/test/test_launcher_utils.h" |
| #include "chrome/test/testing_browser_process.h" |
| #include "chrome/test/ui_test_utils.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/renderer_host/render_process_host.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "content/common/main_function_params.h" |
| #include "content/common/notification_type.h" |
| #include "content/renderer/content_renderer_client.h" |
| #include "net/base/mock_host_resolver.h" |
| #include "net/test/test_server.h" |
| #include "sandbox/src/dep.h" |
| |
| #if defined(OS_MACOSX) |
| #include "base/mac/mac_util.h" |
| #endif |
| |
| #if defined(OS_WIN) |
| #include "chrome/browser/ui/views/frame/browser_frame_win.h" |
| #endif |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/chromeos/audio_handler.h" |
| #endif |
| |
| namespace { |
| |
| void InitializeBrowser(Browser* browser) { |
| browser->AddSelectedTabWithURL(GURL(chrome::kAboutBlankURL), |
| PageTransition::START_PAGE); |
| |
| // Wait for the page to finish loading. |
| ui_test_utils::WaitForNavigation( |
| &browser->GetSelectedTabContents()->controller()); |
| |
| browser->window()->Show(); |
| } |
| |
| } // namespace |
| |
| extern int BrowserMain(const MainFunctionParams&); |
| |
| const char kUnitTestShowWindows[] = "show-windows"; |
| |
| // Passed as value of kTestType. |
| static const char kBrowserTestType[] = "browser"; |
| |
| InProcessBrowserTest::InProcessBrowserTest() |
| : browser_(NULL), |
| show_window_(false), |
| dom_automation_enabled_(false), |
| tab_closeable_state_watcher_enabled_(false) { |
| #if defined(OS_MACOSX) |
| base::mac::SetOverrideAmIBundled(true); |
| #endif |
| |
| // Before we run the browser, we have to hack the path to the exe to match |
| // what it would be if Chrome was running, because it is used to fork renderer |
| // processes, on Linux at least (failure to do so will cause a browser_test to |
| // be run instead of a renderer). |
| FilePath chrome_path; |
| CHECK(PathService::Get(base::FILE_EXE, &chrome_path)); |
| chrome_path = chrome_path.DirName(); |
| chrome_path = chrome_path.Append(chrome::kBrowserProcessExecutablePath); |
| CHECK(PathService::Override(base::FILE_EXE, chrome_path)); |
| |
| test_server_.reset(new net::TestServer( |
| net::TestServer::TYPE_HTTP, |
| FilePath(FILE_PATH_LITERAL("chrome/test/data")))); |
| } |
| |
| InProcessBrowserTest::~InProcessBrowserTest() { |
| } |
| |
| void InProcessBrowserTest::SetUp() { |
| // Create a temporary user data directory if required. |
| ASSERT_TRUE(CreateUserDataDirectory()) |
| << "Could not create user data directory."; |
| |
| // The unit test suite creates a testingbrowser, but we want the real thing. |
| // Delete the current one. We'll install the testing one in TearDown. |
| delete g_browser_process; |
| g_browser_process = NULL; |
| |
| // Allow subclasses the opportunity to make changes to the default user data |
| // dir before running any tests. |
| ASSERT_TRUE(SetUpUserDataDirectory()) |
| << "Could not set up user data directory."; |
| |
| // Don't delete the resources when BrowserMain returns. Many ui classes |
| // cache SkBitmaps in a static field so that if we delete the resource |
| // bundle we'll crash. |
| browser_shutdown::delete_resources_on_shutdown = false; |
| |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| // Allow subclasses to change the command line before running any tests. |
| SetUpCommandLine(command_line); |
| // Add command line arguments that are used by all InProcessBrowserTests. |
| PrepareTestCommandLine(command_line); |
| |
| // Single-process mode is not set in BrowserMain, so process it explicitly, |
| // and set up renderer. |
| if (command_line->HasSwitch(switches::kSingleProcess)) { |
| RenderProcessHost::set_run_renderer_in_process(true); |
| single_process_renderer_client_.reset(new content::ContentRendererClient); |
| content::GetContentClient()->set_renderer( |
| single_process_renderer_client_.get()); |
| } |
| |
| #if defined(OS_CHROMEOS) |
| // Make sure that the log directory exists. |
| FilePath log_dir = logging::GetSessionLogFile(*command_line).DirName(); |
| file_util::CreateDirectory(log_dir); |
| |
| // Disable audio mixer as it can cause hang. |
| // see http://crosbug.com/17058. |
| chromeos::AudioHandler::Disable(); |
| |
| // Prevent loading ChromeOS component extension to prevent their interference |
| // with other browser tests. |
| command_line->AppendSwitch(switches::kSkipChromeOSComponents); |
| #endif // defined(OS_CHROMEOS) |
| |
| SandboxInitWrapper sandbox_wrapper; |
| MainFunctionParams params(*command_line, sandbox_wrapper, NULL); |
| params.ui_task = |
| NewRunnableMethod(this, &InProcessBrowserTest::RunTestOnMainThreadLoop); |
| |
| host_resolver_ = new net::RuleBasedHostResolverProc( |
| new IntranetRedirectHostResolverProc(NULL)); |
| |
| // Something inside the browser does this lookup implicitly. Make it fail |
| // to avoid external dependency. It won't break the tests. |
| host_resolver_->AddSimulatedFailure("*.google.com"); |
| |
| // See http://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol |
| // We don't want the test code to use it. |
| host_resolver_->AddSimulatedFailure("wpad"); |
| |
| net::ScopedDefaultHostResolverProc scoped_host_resolver_proc( |
| host_resolver_.get()); |
| |
| SetUpInProcessBrowserTestFixture(); |
| |
| BrowserMain(params); |
| TearDownInProcessBrowserTestFixture(); |
| } |
| |
| void InProcessBrowserTest::PrepareTestCommandLine(CommandLine* command_line) { |
| // Propagate commandline settings from test_launcher_utils. |
| test_launcher_utils::PrepareBrowserCommandLineForTests(command_line); |
| |
| #if defined(OS_WIN) |
| // Hide windows on show. |
| if (!command_line->HasSwitch(kUnitTestShowWindows) && !show_window_) |
| BrowserFrameWin::SetShowState(SW_HIDE); |
| #endif |
| |
| if (dom_automation_enabled_) |
| command_line->AppendSwitch(switches::kDomAutomationController); |
| |
| // This is a Browser test. |
| command_line->AppendSwitchASCII(switches::kTestType, kBrowserTestType); |
| |
| #if defined(OS_WIN) |
| // The Windows sandbox requires that the browser and child processes are the |
| // same binary. So we launch browser_process.exe which loads chrome.dll |
| command_line->AppendSwitchPath(switches::kBrowserSubprocessPath, |
| command_line->GetProgram()); |
| #else |
| // Explicitly set the path of the binary used for child processes, otherwise |
| // they'll try to use browser_tests which doesn't contain ChromeMain. |
| FilePath subprocess_path; |
| PathService::Get(base::FILE_EXE, &subprocess_path); |
| #if defined(OS_MACOSX) |
| // Recreate the real environment, run the helper within the app bundle. |
| subprocess_path = subprocess_path.DirName().DirName(); |
| DCHECK_EQ(subprocess_path.BaseName().value(), "Contents"); |
| subprocess_path = |
| subprocess_path.Append("Versions").Append(chrome::kChromeVersion); |
| subprocess_path = |
| subprocess_path.Append(chrome::kHelperProcessExecutablePath); |
| #endif |
| command_line->AppendSwitchPath(switches::kBrowserSubprocessPath, |
| subprocess_path); |
| #endif |
| |
| // If neccessary, disable TabCloseableStateWatcher. |
| if (!tab_closeable_state_watcher_enabled_) |
| command_line->AppendSwitch(switches::kDisableTabCloseableStateWatcher); |
| } |
| |
| bool InProcessBrowserTest::CreateUserDataDirectory() { |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| FilePath user_data_dir = |
| command_line->GetSwitchValuePath(switches::kUserDataDir); |
| if (user_data_dir.empty()) { |
| if (temp_user_data_dir_.CreateUniqueTempDir() && |
| temp_user_data_dir_.IsValid()) { |
| user_data_dir = temp_user_data_dir_.path(); |
| } else { |
| LOG(ERROR) << "Could not create temporary user data directory \"" |
| << temp_user_data_dir_.path().value() << "\"."; |
| return false; |
| } |
| } |
| return test_launcher_utils::OverrideUserDataDir(user_data_dir); |
| } |
| |
| void InProcessBrowserTest::TearDown() { |
| // Reinstall testing browser process. |
| delete g_browser_process; |
| g_browser_process = new TestingBrowserProcess(); |
| |
| browser_shutdown::delete_resources_on_shutdown = true; |
| |
| #if defined(OS_WIN) |
| BrowserFrameWin::SetShowState(-1); |
| #endif |
| } |
| |
| void InProcessBrowserTest::AddTabAtIndexToBrowser( |
| Browser* browser, |
| int index, |
| const GURL& url, |
| PageTransition::Type transition) { |
| browser::NavigateParams params(browser, url, transition); |
| params.tabstrip_index = index; |
| params.disposition = NEW_FOREGROUND_TAB; |
| browser::Navigate(¶ms); |
| } |
| |
| void InProcessBrowserTest::AddTabAtIndex( |
| int index, |
| const GURL& url, |
| PageTransition::Type transition) { |
| AddTabAtIndexToBrowser(browser(), index, url, transition); |
| } |
| |
| bool InProcessBrowserTest::SetUpUserDataDirectory() { |
| return true; |
| } |
| |
| // Creates a browser with a single tab (about:blank), waits for the tab to |
| // finish loading and shows the browser. |
| Browser* InProcessBrowserTest::CreateBrowser(Profile* profile) { |
| Browser* browser = Browser::Create(profile); |
| InitializeBrowser(browser); |
| return browser; |
| } |
| |
| Browser* InProcessBrowserTest::CreateIncognitoBrowser() { |
| // Create a new browser with using the incognito profile. |
| Browser* incognito = |
| Browser::Create(browser()->profile()->GetOffTheRecordProfile()); |
| InitializeBrowser(incognito); |
| return incognito; |
| } |
| |
| Browser* InProcessBrowserTest::CreateBrowserForPopup(Profile* profile) { |
| Browser* browser = Browser::CreateForType(Browser::TYPE_POPUP, profile); |
| InitializeBrowser(browser); |
| return browser; |
| } |
| |
| void InProcessBrowserTest::RunTestOnMainThreadLoop() { |
| #if defined(OS_POSIX) |
| // Restore default signal handler for SIGTERM, so when the out-of-process |
| // test runner tries to terminate us, we don't catch it and possibly make it |
| // look like a success (http://crbug.com/57578). |
| signal(SIGTERM, SIG_DFL); |
| #endif // defined(OS_POSIX) |
| |
| // On Mac, without the following autorelease pool, code which is directly |
| // executed (as opposed to executed inside a message loop) would autorelease |
| // objects into a higher-level pool. This pool is not recycled in-sync with |
| // the message loops' pools and causes problems with code relying on |
| // deallocation via an autorelease pool (such as browser window closure and |
| // browser shutdown). To avoid this, the following pool is recycled after each |
| // time code is directly executed. |
| base::mac::ScopedNSAutoreleasePool pool; |
| |
| // Pump startup related events. |
| MessageLoopForUI::current()->RunAllPending(); |
| |
| // In the long term it would be great if we could use a TestingProfile |
| // here and only enable services you want tested, but that requires all |
| // consumers of Profile to handle NULL services. |
| Profile* profile = ProfileManager::GetDefaultProfile(); |
| if (!profile) { |
| // We should only be able to get here if the profile already exists and |
| // has been created. |
| NOTREACHED(); |
| return; |
| } |
| pool.Recycle(); |
| |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| NewRunnableFunction(chrome_browser_net::SetUrlRequestMocksEnabled, true)); |
| |
| browser_ = CreateBrowser(profile); |
| pool.Recycle(); |
| |
| // Pump any pending events that were created as a result of creating a |
| // browser. |
| MessageLoopForUI::current()->RunAllPending(); |
| |
| SetUpOnMainThread(); |
| pool.Recycle(); |
| |
| RunTestOnMainThread(); |
| pool.Recycle(); |
| |
| CleanUpOnMainThread(); |
| pool.Recycle(); |
| |
| QuitBrowsers(); |
| pool.Recycle(); |
| } |
| |
| void InProcessBrowserTest::QuitBrowsers() { |
| if (BrowserList::size() == 0) |
| return; |
| |
| // Invoke CloseAllBrowsersAndExit on a running message loop. |
| // CloseAllBrowsersAndExit exits the message loop after everything has been |
| // shut down properly. |
| MessageLoopForUI::current()->PostTask( |
| FROM_HERE, |
| NewRunnableFunction(&BrowserList::CloseAllBrowsersAndExit)); |
| ui_test_utils::RunMessageLoop(); |
| } |