diff options
author | nirnimesh@chromium.org <nirnimesh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-08 02:04:59 +0000 |
---|---|---|
committer | nirnimesh@chromium.org <nirnimesh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-08 02:04:59 +0000 |
commit | c405290f60432b22e8e862377b4f00e6177b4451 (patch) | |
tree | a710eb4876a4ea9013715ba90a27139d5da835bc /chrome/test/automation | |
parent | 079443435c92b0294d63682c39a26df3391018c4 (diff) | |
download | chromium_src-c405290f60432b22e8e862377b4f00e6177b4451.zip chromium_src-c405290f60432b22e8e862377b4f00e6177b4451.tar.gz chromium_src-c405290f60432b22e8e862377b4f00e6177b4451.tar.bz2 |
Moves everything related to launching and terminating the browser from UITestBase into ProxyLauncher.
The primary changes are in ui_test.* and proxy_launcher.*. The changes in the remaining files are mostly just changing namespaces from UITestBase:: to ProxyLauncher::.
BUG=None.
TEST=All tests should pass. No functionality change.
Review URL: http://codereview.chromium.org/5967003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70827 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test/automation')
-rw-r--r-- | chrome/test/automation/automation_proxy_uitest.cc | 9 | ||||
-rw-r--r-- | chrome/test/automation/automation_proxy_uitest.h | 2 | ||||
-rw-r--r-- | chrome/test/automation/proxy_launcher.cc | 486 | ||||
-rw-r--r-- | chrome/test/automation/proxy_launcher.h | 277 |
4 files changed, 751 insertions, 23 deletions
diff --git a/chrome/test/automation/automation_proxy_uitest.cc b/chrome/test/automation/automation_proxy_uitest.cc index 465b610..ab65c43 100644 --- a/chrome/test/automation/automation_proxy_uitest.cc +++ b/chrome/test/automation/automation_proxy_uitest.cc @@ -63,8 +63,9 @@ class ExternalTabUITestMockLauncher : public ProxyLauncher { return *mock_; } - void InitializeConnection(UITestBase* ui_test_base) const { - ui_test_base->LaunchBrowserAndServer(); + void InitializeConnection(const LaunchState& state, + bool wait_for_initial_loads) { + LaunchBrowserAndServer(state, wait_for_initial_loads); } std::string PrefixedChannelID() const { @@ -885,6 +886,10 @@ template <typename T> T** ReceivePointer(scoped_refptr<T>& p) { // NOLINT return reinterpret_cast<T**>(&p); } +ExternalTabUITest::ExternalTabUITest() : UITest(MessageLoop::TYPE_UI) { + launcher_.reset(CreateProxyLauncher()); +} + // Replace the default automation proxy with our mock client. ProxyLauncher* ExternalTabUITest::CreateProxyLauncher() { return new ExternalTabUITestMockLauncher(&mock_); diff --git a/chrome/test/automation/automation_proxy_uitest.h b/chrome/test/automation/automation_proxy_uitest.h index c08aa91..2ce19d4 100644 --- a/chrome/test/automation/automation_proxy_uitest.h +++ b/chrome/test/automation/automation_proxy_uitest.h @@ -109,7 +109,7 @@ class ExternalTabUITestMockClient : public AutomationProxy { // Base your external tab UI tests on this. class ExternalTabUITest : public UITest { public: - ExternalTabUITest() : UITest(MessageLoop::TYPE_UI) {} + ExternalTabUITest(); // Override UITest's CreateProxyLauncher to provide the unit test // with our special implementation of AutomationProxy. // This function is called from within UITest::SetUp(). diff --git a/chrome/test/automation/proxy_launcher.cc b/chrome/test/automation/proxy_launcher.cc index 1d1583c..d30f212 100644 --- a/chrome/test/automation/proxy_launcher.cc +++ b/chrome/test/automation/proxy_launcher.cc @@ -4,14 +4,482 @@ #include "chrome/test/automation/proxy_launcher.h" -#include "base/threading/platform_thread.h" +#include "app/sql/connection.h" +#include "base/file_util.h" +#include "base/string_number_conversions.h" +#include "base/string_split.h" +#include "base/string_util.h" +#include "base/stringprintf.h" +#include "base/test/test_file_util.h" +#include "base/test/test_timeouts.h" +#include "base/utf_string_conversions.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/common/automation_constants.h" +#include "chrome/common/child_process_info.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/debug_flags.h" #include "chrome/common/logging_chrome.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/chrome_process_util.h" +#include "chrome/test/test_launcher_utils.h" +#include "chrome/test/test_switches.h" #include "chrome/test/automation/automation_proxy.h" #include "chrome/test/ui/ui_test.h" +namespace { + +// Passed as value of kTestType. +const char kUITestType[] = "ui"; + // Default path of named testing interface. -static const char kInterfacePath[] = "/var/tmp/ChromeTestingInterface"; +const char kInterfacePath[] = "/var/tmp/ChromeTestingInterface"; + +// Rewrite the preferences file to point to the proper image directory. +void RewritePreferencesFile(const FilePath& user_data_dir) { + const FilePath pref_template_path( + user_data_dir.AppendASCII("Default").AppendASCII("PreferencesTemplate")); + const FilePath pref_path( + user_data_dir.AppendASCII("Default").AppendASCII("Preferences")); + + // Read in preferences template. + std::string pref_string; + EXPECT_TRUE(file_util::ReadFileToString(pref_template_path, &pref_string)); + string16 format_string = ASCIIToUTF16(pref_string); + + // Make sure temp directory has the proper format for writing to prefs file. +#if defined(OS_POSIX) + std::wstring user_data_dir_w(ASCIIToWide(user_data_dir.value())); +#elif defined(OS_WIN) + std::wstring user_data_dir_w(user_data_dir.value()); + // In Windows, the FilePath will write '\' for the path separators; change + // these to a separator that won't trigger escapes. + std::replace(user_data_dir_w.begin(), + user_data_dir_w.end(), '\\', '/'); +#endif + + // Rewrite prefs file. + std::vector<string16> subst; + subst.push_back(WideToUTF16(user_data_dir_w)); + const std::string prefs_string = + UTF16ToASCII(ReplaceStringPlaceholders(format_string, subst, NULL)); + EXPECT_TRUE(file_util::WriteFile(pref_path, prefs_string.c_str(), + prefs_string.size())); + file_util::EvictFileFromSystemCache(pref_path); +} + +// We want to have a current history database when we start the browser so +// things like the NTP will have thumbnails. This method updates the dates +// in the history to be more recent. +void UpdateHistoryDates(const FilePath& user_data_dir) { + // Migrate the times in the segment_usage table to yesterday so we get + // actual thumbnails on the NTP. + sql::Connection db; + FilePath history = + user_data_dir.AppendASCII("Default").AppendASCII("History"); + // Not all test profiles have a history file. + if (!file_util::PathExists(history)) + return; + + ASSERT_TRUE(db.Open(history)); + base::Time yesterday = base::Time::Now() - base::TimeDelta::FromDays(1); + std::string yesterday_str = base::Int64ToString(yesterday.ToInternalValue()); + std::string query = StringPrintf( + "UPDATE segment_usage " + "SET time_slot = %s " + "WHERE id IN (SELECT id FROM segment_usage WHERE time_slot > 0);", + yesterday_str.c_str()); + ASSERT_TRUE(db.Execute(query.c_str())); + db.Close(); + file_util::EvictFileFromSystemCache(history); +} + +} // namespace + +// ProxyLauncher functions + +bool ProxyLauncher::in_process_renderer_ = false; +bool ProxyLauncher::no_sandbox_ = false; +bool ProxyLauncher::full_memory_dump_ = false; +bool ProxyLauncher::safe_plugins_ = false; +bool ProxyLauncher::show_error_dialogs_ = true; +bool ProxyLauncher::dump_histograms_on_exit_ = false; +bool ProxyLauncher::enable_dcheck_ = false; +bool ProxyLauncher::silent_dump_on_dcheck_ = false; +bool ProxyLauncher::disable_breakpad_ = false; +std::string ProxyLauncher::js_flags_ = ""; +std::string ProxyLauncher::log_level_ = ""; + +ProxyLauncher::ProxyLauncher() + : process_(base::kNullProcessHandle), + process_id_(-1) {} + +ProxyLauncher::~ProxyLauncher() {} + +void ProxyLauncher::WaitForBrowserLaunch(bool wait_for_initial_loads) { + ASSERT_EQ(AUTOMATION_SUCCESS, automation_proxy_->WaitForAppLaunch()) + << "Error while awaiting automation ping from browser process"; + if (wait_for_initial_loads) + ASSERT_TRUE(automation_proxy_->WaitForInitialLoads()); + else + base::PlatformThread::Sleep(TestTimeouts::action_timeout_ms()); + + EXPECT_TRUE(automation()->SetFilteredInet(ShouldFilterInet())); +} + +void ProxyLauncher::LaunchBrowserAndServer(const LaunchState& state, + bool wait_for_initial_loads) { + // Set up IPC testing interface as a server. + automation_proxy_.reset(CreateAutomationProxy( + TestTimeouts::command_execution_timeout_ms())); + + LaunchBrowser(state); + WaitForBrowserLaunch(wait_for_initial_loads); +} + +void ProxyLauncher::ConnectToRunningBrowser(bool wait_for_initial_loads) { + // Set up IPC testing interface as a client. + automation_proxy_.reset(CreateAutomationProxy( + TestTimeouts::command_execution_timeout_ms())); + WaitForBrowserLaunch(wait_for_initial_loads); +} + +void ProxyLauncher::CloseBrowserAndServer(ShutdownType shutdown_type) { + QuitBrowser(shutdown_type); + CleanupAppProcesses(); + + // Suppress spammy failures that seem to be occurring when running + // the UI tests in single-process mode. + // TODO(jhughes): figure out why this is necessary at all, and fix it + if (!in_process_renderer_) + AssertAppNotRunning( + StringPrintf(L"Unable to quit all browser processes. Original PID %d", + &process_id_)); + + automation_proxy_.reset(); // Shut down IPC testing interface. +} + +void ProxyLauncher::LaunchBrowser(const LaunchState& state) { + if (state.clear_profile || !temp_profile_dir_.IsValid()) { + temp_profile_dir_.Delete(); + ASSERT_TRUE(temp_profile_dir_.CreateUniqueTempDir()); + + ASSERT_TRUE(test_launcher_utils::OverrideUserDataDir(user_data_dir())); + } + + if (!state.template_user_data.empty()) { + // Recursively copy the template directory to the user_data_dir. + ASSERT_TRUE(file_util::CopyRecursiveDirNoCache( + state.template_user_data, + user_data_dir())); + // If we're using the complex theme data, we need to write the + // user_data_dir_ to our preferences file. + if (state.profile_type == COMPLEX_THEME) { + RewritePreferencesFile(user_data_dir()); + } + + // Update the history file to include recent dates. + UpdateHistoryDates(user_data_dir()); + } + + ASSERT_TRUE(LaunchBrowserHelper(state, false, &process_)); + process_id_ = base::GetProcId(process_); +} + +#if !defined(OS_MACOSX) +bool ProxyLauncher::LaunchAnotherBrowserBlockUntilClosed( + const LaunchState& state) { + return LaunchBrowserHelper(state, true, NULL); +} +#endif + +void ProxyLauncher::QuitBrowser(ShutdownType shutdown_type) { + if (SESSION_ENDING == shutdown_type) { + TerminateBrowser(); + return; + } + + // There's nothing to do here if the browser is not running. + // WARNING: There is a race condition here where the browser may shut down + // after this check but before some later automation call. Your test should + // use WaitForBrowserProcessToQuit() if it intentionally + // causes the browser to shut down. + if (IsBrowserRunning()) { + base::TimeTicks quit_start = base::TimeTicks::Now(); + EXPECT_TRUE(automation()->SetFilteredInet(false)); + + if (WINDOW_CLOSE == shutdown_type) { + int window_count = 0; + EXPECT_TRUE(automation()->GetBrowserWindowCount(&window_count)); + + // Synchronously close all but the last browser window. Closing them + // one-by-one may help with stability. + while (window_count > 1) { + scoped_refptr<BrowserProxy> browser_proxy = + automation()->GetBrowserWindow(0); + EXPECT_TRUE(browser_proxy.get()); + if (browser_proxy.get()) { + EXPECT_TRUE(browser_proxy->RunCommand(IDC_CLOSE_WINDOW)); + EXPECT_TRUE(automation()->GetBrowserWindowCount(&window_count)); + } else { + break; + } + } + + // Close the last window asynchronously, because the browser may + // shutdown faster than it will be able to send a synchronous response + // to our message. + scoped_refptr<BrowserProxy> browser_proxy = + automation()->GetBrowserWindow(0); + EXPECT_TRUE(browser_proxy.get()); + if (browser_proxy.get()) { + EXPECT_TRUE(browser_proxy->ApplyAccelerator(IDC_CLOSE_WINDOW)); + browser_proxy = NULL; + } + } else if (USER_QUIT == shutdown_type) { + scoped_refptr<BrowserProxy> browser_proxy = + automation()->GetBrowserWindow(0); + EXPECT_TRUE(browser_proxy.get()); + if (browser_proxy.get()) { + EXPECT_TRUE(browser_proxy->RunCommandAsync(IDC_EXIT)); + } + } else { + NOTREACHED() << "Invalid shutdown type " << shutdown_type; + } + + // Now, drop the automation IPC channel so that the automation provider in + // the browser notices and drops its reference to the browser process. + automation()->Disconnect(); + + // Wait for the browser process to quit. It should quit once all tabs have + // been closed. + if (!WaitForBrowserProcessToQuit( + TestTimeouts::wait_for_terminate_timeout_ms())) { + // We need to force the browser to quit because it didn't quit fast + // enough. Take no chance and kill every chrome processes. + CleanupAppProcesses(); + } + browser_quit_time_ = base::TimeTicks::Now() - quit_start; + } + + // Don't forget to close the handle + base::CloseProcessHandle(process_); + process_ = base::kNullProcessHandle; + process_id_ = -1; +} + +void ProxyLauncher::TerminateBrowser() { + if (IsBrowserRunning()) { + base::TimeTicks quit_start = base::TimeTicks::Now(); + EXPECT_TRUE(automation()->SetFilteredInet(false)); +#if defined(OS_WIN) + scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + ASSERT_TRUE(browser->TerminateSession()); +#endif // defined(OS_WIN) + + // Now, drop the automation IPC channel so that the automation provider in + // the browser notices and drops its reference to the browser process. + automation()->Disconnect(); + +#if defined(OS_POSIX) + EXPECT_EQ(kill(process_, SIGTERM), 0); +#endif // OS_POSIX + + if (!WaitForBrowserProcessToQuit( + TestTimeouts::wait_for_terminate_timeout_ms())) { + // We need to force the browser to quit because it didn't quit fast + // enough. Take no chance and kill every chrome processes. + CleanupAppProcesses(); + } + browser_quit_time_ = base::TimeTicks::Now() - quit_start; + } + + // Don't forget to close the handle + base::CloseProcessHandle(process_); + process_ = base::kNullProcessHandle; + process_id_ = -1; +} + +void ProxyLauncher::AssertAppNotRunning(const std::wstring& error_message) { + std::wstring final_error_message(error_message); + + ChromeProcessList processes = GetRunningChromeProcesses(process_id_); + if (!processes.empty()) { + final_error_message += L" Leftover PIDs: ["; + for (ChromeProcessList::const_iterator it = processes.begin(); + it != processes.end(); ++it) { + final_error_message += StringPrintf(L" %d", *it); + } + final_error_message += L" ]"; + } + ASSERT_TRUE(processes.empty()) << final_error_message; +} + +void ProxyLauncher::CleanupAppProcesses() { + TerminateAllChromeProcesses(process_id_); +} + +bool ProxyLauncher::WaitForBrowserProcessToQuit(int timeout) { +#ifdef WAIT_FOR_DEBUGGER_ON_OPEN + timeout = 500000; +#endif + return base::WaitForSingleProcess(process_, timeout); +} + +bool ProxyLauncher::IsBrowserRunning() { + return CrashAwareSleep(0); +} + +bool ProxyLauncher::CrashAwareSleep(int timeout_ms) { + return base::CrashAwareSleep(process_, timeout_ms); +} + +void ProxyLauncher::PrepareTestCommandline(CommandLine* command_line, + bool include_testing_id) { + // Propagate commandline settings from test_launcher_utils. + test_launcher_utils::PrepareBrowserCommandLineForTests(command_line); + + // Add any explicit command line flags passed to the process. + CommandLine::StringType extra_chrome_flags = + CommandLine::ForCurrentProcess()->GetSwitchValueNative( + switches::kExtraChromeFlags); + if (!extra_chrome_flags.empty()) { + // Split by spaces and append to command line + std::vector<CommandLine::StringType> flags; + base::SplitString(extra_chrome_flags, ' ', &flags); + for (size_t i = 0; i < flags.size(); ++i) + command_line->AppendArgNative(flags[i]); + } + + // No default browser check, it would create an info-bar (if we are not the + // default browser) that could conflicts with some tests expectations. + command_line->AppendSwitch(switches::kNoDefaultBrowserCheck); + + // This is a UI test. + command_line->AppendSwitchASCII(switches::kTestType, kUITestType); + + // Tell the browser to use a temporary directory just for this test. + command_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir()); + + if (include_testing_id) + command_line->AppendSwitchASCII(switches::kTestingChannelID, + PrefixedChannelID()); + + if (!show_error_dialogs_ && + !CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableErrorDialogs)) { + command_line->AppendSwitch(switches::kNoErrorDialogs); + } + if (in_process_renderer_) + command_line->AppendSwitch(switches::kSingleProcess); + if (no_sandbox_) + command_line->AppendSwitch(switches::kNoSandbox); + if (full_memory_dump_) + command_line->AppendSwitch(switches::kFullMemoryCrashReport); + if (safe_plugins_) + command_line->AppendSwitch(switches::kSafePlugins); + if (enable_dcheck_) + command_line->AppendSwitch(switches::kEnableDCHECK); + if (silent_dump_on_dcheck_) + command_line->AppendSwitch(switches::kSilentDumpOnDCHECK); + if (disable_breakpad_) + command_line->AppendSwitch(switches::kDisableBreakpad); + + if (!js_flags_.empty()) + command_line->AppendSwitchASCII(switches::kJavaScriptFlags, js_flags_); + if (!log_level_.empty()) + command_line->AppendSwitchASCII(switches::kLoggingLevel, log_level_); + + command_line->AppendSwitch(switches::kMetricsRecordingOnly); + + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableErrorDialogs)) + command_line->AppendSwitch(switches::kEnableLogging); + + if (dump_histograms_on_exit_) + command_line->AppendSwitch(switches::kDumpHistogramsOnExit); + +#ifdef WAIT_FOR_DEBUGGER_ON_OPEN + command_line->AppendSwitch(switches::kDebugOnStart); +#endif + + // The tests assume that file:// URIs can freely access other file:// URIs. + command_line->AppendSwitch(switches::kAllowFileAccessFromFiles); + + // Disable TabCloseableStateWatcher for tests. + command_line->AppendSwitch(switches::kDisableTabCloseableStateWatcher); + + // Allow file:// access on ChromeOS. + command_line->AppendSwitch(switches::kAllowFileAccess); +} + +bool ProxyLauncher::LaunchBrowserHelper(const LaunchState& state, bool wait, + base::ProcessHandle* process) { + FilePath command = state.browser_directory.Append( + chrome::kBrowserProcessExecutablePath); + + CommandLine command_line(command); + + // Add command line arguments that should be applied to all UI tests. + PrepareTestCommandline(&command_line, state.include_testing_id); + DebugFlags::ProcessDebugFlags( + &command_line, ChildProcessInfo::UNKNOWN_PROCESS, false); + command_line.AppendArguments(state.arguments, false); + + // TODO(phajdan.jr): Only run it for "main" browser launch. + browser_launch_time_ = base::TimeTicks::Now(); + +#if defined(OS_WIN) + bool started = base::LaunchApp(command_line, wait, + !state.show_window, process); +#elif defined(OS_POSIX) + // Sometimes one needs to run the browser under a special environment + // (e.g. valgrind) without also running the test harness (e.g. python) + // under the special environment. Provide a way to wrap the browser + // commandline with a special prefix to invoke the special environment. + const char* browser_wrapper = getenv("BROWSER_WRAPPER"); + if (browser_wrapper) { + command_line.PrependWrapper(browser_wrapper); + VLOG(1) << "BROWSER_WRAPPER was set, prefixing command_line with " + << browser_wrapper; + } + + base::file_handle_mapping_vector fds; + if (automation_proxy_.get()) + fds = automation_proxy_->fds_to_map(); + + bool started = base::LaunchApp(command_line.argv(), fds, wait, process); +#endif + + return started; +} + +AutomationProxy* ProxyLauncher::automation() const { + EXPECT_TRUE(automation_proxy_.get()); + return automation_proxy_.get(); +} + +FilePath ProxyLauncher::user_data_dir() const { + EXPECT_TRUE(temp_profile_dir_.IsValid()); + return temp_profile_dir_.path(); +} + +base::ProcessHandle ProxyLauncher::process() const { + return process_; +} + +base::ProcessId ProxyLauncher::process_id() const { + return process_id_; +} + +base::TimeTicks ProxyLauncher::browser_launch_time() const { + return browser_launch_time_; +} + +base::TimeDelta ProxyLauncher::browser_quit_time() const { + return browser_quit_time_; +} // NamedProxyLauncher functions @@ -30,10 +498,11 @@ AutomationProxy* NamedProxyLauncher::CreateAutomationProxy( return proxy; } -void NamedProxyLauncher::InitializeConnection(UITestBase* ui_test_base) const { +void NamedProxyLauncher::InitializeConnection(const LaunchState& state, + bool wait_for_initial_loads) { if (launch_browser_) { // Set up IPC testing interface as a client. - ui_test_base->LaunchBrowser(); + LaunchBrowser(state); // Wait for browser to be ready for connections. struct stat file_info; @@ -41,7 +510,7 @@ void NamedProxyLauncher::InitializeConnection(UITestBase* ui_test_base) const { base::PlatformThread::Sleep(automation::kSleepTime); } - ui_test_base->ConnectToRunningBrowser(); + ConnectToRunningBrowser(wait_for_initial_loads); } std::string NamedProxyLauncher::PrefixedChannelID() const { @@ -65,12 +534,11 @@ AutomationProxy* AnonymousProxyLauncher::CreateAutomationProxy( return proxy; } -void AnonymousProxyLauncher::InitializeConnection( - UITestBase* ui_test_base) const { - ui_test_base->LaunchBrowserAndServer(); +void AnonymousProxyLauncher::InitializeConnection(const LaunchState& state, + bool wait_for_initial_loads) { + LaunchBrowserAndServer(state, wait_for_initial_loads); } std::string AnonymousProxyLauncher::PrefixedChannelID() const { return channel_id_; } - diff --git a/chrome/test/automation/proxy_launcher.h b/chrome/test/automation/proxy_launcher.h index 0f4d04d..497d376 100644 --- a/chrome/test/automation/proxy_launcher.h +++ b/chrome/test/automation/proxy_launcher.h @@ -8,30 +8,284 @@ #include <string> #include "base/basictypes.h" +#include "base/command_line.h" +#include "base/process.h" +#include "base/scoped_ptr.h" +#include "base/scoped_temp_dir.h" +#include "base/time.h" class AutomationProxy; -class UITestBase; -// Subclass from this class to use a different implementation of AutomationProxy -// or to use different channel IDs inside a class that derives from UITest. +// Base class for all ProxyLauncher implementations. Contains functionality +// fo launching, terminating, and connecting tests to browser processes. This +// class determines which AutomationProxy implementation is used by a test. +// Command line arguments passed to the browser are set in this class. +// +// Subclass from this class to use a different AutomationProxy +// implementation or to override browser launching behavior. class ProxyLauncher { public: - ProxyLauncher() {} - virtual ~ProxyLauncher() {} + // Profile theme type choices. + enum ProfileType { + DEFAULT_THEME = 0, + COMPLEX_THEME = 1, + NATIVE_THEME = 2, + CUSTOM_FRAME = 3, + CUSTOM_FRAME_NATIVE_THEME = 4, + }; + + // Different ways to quit the browser. + enum ShutdownType { + WINDOW_CLOSE, + USER_QUIT, + SESSION_ENDING, + }; + + // POD containing state variables that determine how to launch browser. + typedef struct { + // If true the profile is cleared before launching. + bool clear_profile; + + // If set, the profiles in this path are copied + // into the user data directory for the test. + FilePath& template_user_data; + + // Profile theme type. + ProfileType profile_type; + + // Path to the browser executable. + FilePath browser_directory; + + // Command line arguments passed to the browser. + CommandLine& arguments; + + // Should we supply the testing channel id on the command line? + bool include_testing_id; + + // If true, the window is shown. Otherwise it is hidden. + bool show_window; + } LaunchState; + + ProxyLauncher(); + + virtual ~ProxyLauncher(); // Creates an automation proxy. virtual AutomationProxy* CreateAutomationProxy( int execution_timeout) = 0; - // Launches the browser if needed and establishes a connection - // connection with it using the specified UITestBase. - virtual void InitializeConnection(UITestBase* ui_test_base) const = 0; + // Launches the browser if needed and establishes a connection with it. + virtual void InitializeConnection(const LaunchState& state, + bool wait_for_initial_loads) = 0; // Returns the automation proxy's channel with any prefixes prepended, // for passing as a command line parameter over to the browser. virtual std::string PrefixedChannelID() const = 0; + // Launches the browser and IPC testing connection in server mode. + void LaunchBrowserAndServer(const LaunchState& state, + bool wait_for_initial_loads); + + // Launches the IPC testing connection in client mode, + // which then attempts to connect to a browser. + void ConnectToRunningBrowser(bool wait_for_initial_loads); + + // Only for pyauto. + void set_command_execution_timeout_ms(int timeout); + + // Closes the browser and IPC testing server. + void CloseBrowserAndServer(ShutdownType shutdown_type); + + // Launches the browser with the given command line. + // TODO(phajdan.jr): Make LaunchBrowser private. Tests should use + // LaunchAnotherBrowserBlockUntilClosed. + void LaunchBrowser(const LaunchState& state); + +#if !defined(OS_MACOSX) + // This function is not defined on the Mac because the concept + // doesn't apply to Mac; you can't have N browser processes. + + // Launches another browser process and waits for it to finish. + // Returns true on success. + bool LaunchAnotherBrowserBlockUntilClosed(const LaunchState& state); +#endif + + // Exits out of browser instance. + void QuitBrowser(ShutdownType shutdown_type); + + // Terminates the browser, simulates end of session. + void TerminateBrowser(); + + // Check that no processes related to Chrome exist, displaying + // the given message if any do. + void AssertAppNotRunning(const std::wstring& error_message); + + // Returns true when the browser process is running, independent if any + // renderer process exists or not. It will returns false if an user closed the + // window or if the browser process died by itself. + bool IsBrowserRunning(); + + // Returns true when timeout_ms milliseconds have elapsed. + // Returns false if the browser process died while waiting. + bool CrashAwareSleep(int timeout_ms); + + // Wait for the browser process to shut down on its own (i.e. as a result of + // some action that your test has taken). + bool WaitForBrowserProcessToQuit(int timeout); + + AutomationProxy* automation() const; + + // Return the user data directory being used by the browser instance. + FilePath user_data_dir() const; + + // Get the handle of browser process connected to the automation. This + // function only returns a reference to the handle so the caller does not + // own the handle returned. + base::ProcessHandle process() const; + + // Return the process id of the browser process (-1 on error). + base::ProcessId process_id() const; + + // Return the time when the browser was run. + base::TimeTicks browser_launch_time() const; + + // Return how long the shutdown took. + base::TimeDelta browser_quit_time() const; + + // Get/Set a flag to run the renderer in-process when running the tests. + static bool in_process_renderer() { return in_process_renderer_; } + static void set_in_process_renderer(bool value) { + in_process_renderer_ = value; + } + + // Get/Set a flag to run the renderer outside the sandbox when running tests. + static bool no_sandbox() { return no_sandbox_; } + static void set_no_sandbox(bool value) { + no_sandbox_ = value; + } + + // Get/Set a flag to run with DCHECKs enabled in release. + static bool enable_dcheck() { return enable_dcheck_; } + static void set_enable_dcheck(bool value) { + enable_dcheck_ = value; + } + + // Get/Set a flag to dump the process memory without crashing on DCHECKs. + static bool silent_dump_on_dcheck() { return silent_dump_on_dcheck_; } + static void set_silent_dump_on_dcheck(bool value) { + silent_dump_on_dcheck_ = value; + } + + // Get/Set a flag to disable breakpad handling. + static bool disable_breakpad() { return disable_breakpad_; } + static void set_disable_breakpad(bool value) { + disable_breakpad_ = value; + } + + // Get/Set a flag to run the plugin processes inside the sandbox when running + // the tests + static bool safe_plugins() { return safe_plugins_; } + static void set_safe_plugins(bool value) { + safe_plugins_ = value; + } + + static bool show_error_dialogs() { return show_error_dialogs_; } + static void set_show_error_dialogs(bool value) { + show_error_dialogs_ = value; + } + + static bool full_memory_dump() { return full_memory_dump_; } + static void set_full_memory_dump(bool value) { + full_memory_dump_ = value; + } + + static bool dump_histograms_on_exit() { return dump_histograms_on_exit_; } + static void set_dump_histograms_on_exit(bool value) { + dump_histograms_on_exit_ = value; + } + + static const std::string& js_flags() { return js_flags_; } + static void set_js_flags(const std::string& value) { + js_flags_ = value; + } + + static const std::string& log_level() { return log_level_; } + static void set_log_level(const std::string& value) { + log_level_ = value; + } + + protected: + virtual bool ShouldFilterInet() { + return true; + } + private: + void WaitForBrowserLaunch(bool wait_for_initial_loads); + + // Prepare command line that will be used to launch the child browser process. + void PrepareTestCommandline(CommandLine* command_line, + bool include_testing_id); + + bool LaunchBrowserHelper(const LaunchState& state, bool wait, + base::ProcessHandle* process); + + // Wait a certain amount of time for all the app processes to exit, + // forcibly killing them if they haven't exited by then. + // It has the side-effect of killing every browser window opened in your + // session, even those unrelated in the test. + void CleanupAppProcesses(); + + scoped_ptr<AutomationProxy> automation_proxy_; + + // We use a temporary directory for profile to avoid issues with being + // unable to delete some files because they're in use, etc. + ScopedTempDir temp_profile_dir_; + + // Handle to the first Chrome process. + base::ProcessHandle process_; + + // PID of |process_| (for debugging). + base::ProcessId process_id_; + + // Time when the browser was run. + base::TimeTicks browser_launch_time_; + + // How long the shutdown took. + base::TimeDelta browser_quit_time_; + + // True if we're in single process mode. + static bool in_process_renderer_; + + // If true, runs the renderer outside the sandbox. + static bool no_sandbox_; + + // If true, runs plugin processes inside the sandbox. + static bool safe_plugins_; + + // If true, write full memory dump during crash. + static bool full_memory_dump_; + + // If true, a user is paying attention to the test, so show error dialogs. + static bool show_error_dialogs_; + + // Include histograms in log on exit. + static bool dump_histograms_on_exit_; + + // Enable dchecks in release mode. + static bool enable_dcheck_; + + // Dump process memory on dcheck without crashing. + static bool silent_dump_on_dcheck_; + + // Disable breakpad on the browser. + static bool disable_breakpad_; + + // Flags passed to the JS engine. + static std::string js_flags_; + + // Logging level. + static std::string log_level_; + DISALLOW_COPY_AND_ASSIGN(ProxyLauncher); }; @@ -46,7 +300,8 @@ class NamedProxyLauncher : public ProxyLauncher { NamedProxyLauncher(bool launch_browser, bool disconnect_on_failure); virtual AutomationProxy* CreateAutomationProxy(int execution_timeout); - virtual void InitializeConnection(UITestBase* ui_test_base) const; + virtual void InitializeConnection(const LaunchState& state, + bool wait_for_initial_loads); virtual std::string PrefixedChannelID() const; protected: @@ -63,7 +318,8 @@ class AnonymousProxyLauncher : public ProxyLauncher { public: explicit AnonymousProxyLauncher(bool disconnect_on_failure); virtual AutomationProxy* CreateAutomationProxy(int execution_timeout); - virtual void InitializeConnection(UITestBase* ui_test_base) const; + virtual void InitializeConnection(const LaunchState& state, + bool wait_for_initial_loads); virtual std::string PrefixedChannelID() const; protected: @@ -75,4 +331,3 @@ class AnonymousProxyLauncher : public ProxyLauncher { }; #endif // CHROME_TEST_AUTOMATION_PROXY_LAUNCHER_H_ - |