blob: 377dc7a081e5579136808d9d64319d2f14ffe9a9 [file] [log] [blame]
// 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(&params);
}
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();
}