Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3515)

Unified Diff: chrome/test/automation/proxy_launcher.cc

Issue 5967003: Refactor UITestBase/ProxyLauncher. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Add POD struct to hold some launcher variables. Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/test/automation/proxy_launcher.cc
diff --git a/chrome/test/automation/proxy_launcher.cc b/chrome/test/automation/proxy_launcher.cc
index 1d1583cb2a5ed3dd82035de6aa7101d9951e1370..d30f21230a0f2251a20aeaf58dff311937950a7e 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_;
}
-

Powered by Google App Engine
This is Rietveld 408576698