| // 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/base/in_process_browser_test.h" |
| |
| #include "base/command_line.h" |
| #include "base/debug/stack_trace.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/io_thread.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/base/test_launcher_utils.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/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/public/browser/notification_types.h" |
| #include "content/renderer/mock_content_renderer_client.h" |
| #include "net/base/mock_host_resolver.h" |
| #include "net/test/test_server.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/chromeos/audio_handler.h" |
| #endif |
| |
| // 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) |
| // TODO(phajdan.jr): Make browser_tests self-contained on Mac, remove this. |
| // 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)); |
| #endif // defined(OS_MACOSX) |
| |
| 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."; |
| |
| // Undo TestingBrowserProcess creation in ChromeTestSuite. |
| // TODO(phajdan.jr): Extract a smaller test suite so we don't need this. |
| DCHECK(g_browser_process); |
| 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."; |
| |
| 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::MockContentRendererClient); |
| 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); |
| #endif // defined(OS_CHROMEOS) |
| |
| host_resolver_ = new net::RuleBasedHostResolverProc(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()); |
| |
| BrowserTestBase::SetUp(); |
| } |
| |
| void InProcessBrowserTest::PrepareTestCommandLine(CommandLine* command_line) { |
| // Propagate commandline settings from test_launcher_utils. |
| test_launcher_utils::PrepareBrowserCommandLineForTests(command_line); |
| |
| 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() { |
| DCHECK(!g_browser_process); |
| BrowserTestBase::TearDown(); |
| } |
| |
| void InProcessBrowserTest::AddTabAtIndexToBrowser( |
| Browser* browser, |
| int index, |
| const GURL& url, |
| content::PageTransition 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, |
| content::PageTransition 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); |
| AddBlankTabAndShow(browser); |
| return browser; |
| } |
| |
| Browser* InProcessBrowserTest::CreateIncognitoBrowser() { |
| // Create a new browser with using the incognito profile. |
| Browser* incognito = |
| Browser::Create(browser()->profile()->GetOffTheRecordProfile()); |
| AddBlankTabAndShow(incognito); |
| return incognito; |
| } |
| |
| Browser* InProcessBrowserTest::CreateBrowserForPopup(Profile* profile) { |
| Browser* browser = Browser::CreateForType(Browser::TYPE_POPUP, profile); |
| AddBlankTabAndShow(browser); |
| return browser; |
| } |
| |
| void InProcessBrowserTest::AddBlankTabAndShow(Browser* browser) { |
| ui_test_utils::WindowedNotificationObserver observer( |
| content::NOTIFICATION_LOAD_STOP, |
| NotificationService::AllSources()); |
| browser->AddSelectedTabWithURL( |
| GURL(chrome::kAboutBlankURL), content::PAGE_TRANSITION_START_PAGE); |
| observer.Wait(); |
| |
| browser->window()->Show(); |
| } |
| |
| #if defined(OS_POSIX) |
| // On SIGTERM (sent by the runner on timeouts), dump a stack trace (to make |
| // debugging easier) and also exit with a known error code (so that the test |
| // framework considers this a failure -- http://crbug.com/57578). |
| static void DumpStackTraceSignalHandler(int signal) { |
| base::debug::StackTrace().PrintBacktrace(); |
| _exit(128 + signal); |
| } |
| #endif // defined(OS_POSIX) |
| |
| void InProcessBrowserTest::RunTestOnMainThreadLoop() { |
| #if defined(OS_POSIX) |
| signal(SIGTERM, DumpStackTraceSignalHandler); |
| #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(); |
| pool.Recycle(); |
| |
| browser_ = CreateBrowser(ProfileManager::GetDefaultProfile()); |
| 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 CloseAllBrowsersAndMayExit on a running message loop. |
| // CloseAllBrowsersAndMayExit exits the message loop after everything has been |
| // shut down properly. |
| MessageLoopForUI::current()->PostTask( |
| FROM_HERE, |
| NewRunnableFunction(&BrowserList::AttemptExit)); |
| ui_test_utils::RunMessageLoop(); |
| } |