Commit 33ef938e authored by kkania@chromium.org's avatar kkania@chromium.org

Add chrome.loadAsync capability to ChromeDriver, which allows the user not to

wait for page loads.
Also, be able to launch chromium and chrome regardless of what branding
we're built with.
BUG=89757
TEST=none


Review URL: http://codereview.chromium.org/7582005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96265 0039d316-1c4b-4281-b951-d872f2087c98
parent b4939695
......@@ -866,6 +866,7 @@
'test/webdriver/session_manager.cc',
'test/webdriver/utility_functions.h',
'test/webdriver/utility_functions.cc',
'test/webdriver/utility_functions_mac.mm',
'test/webdriver/webdriver_error.h',
'test/webdriver/webdriver_error.cc',
'test/webdriver/webdriver_logging.h',
......
......@@ -9,6 +9,7 @@
#endif
#include "base/base_paths.h"
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/environment.h"
......@@ -16,6 +17,7 @@
#include "base/file_util.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/path_service.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
......@@ -29,9 +31,12 @@
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/automation_json_requests.h"
#include "chrome/test/automation/automation_proxy.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/extension_proxy.h"
#include "chrome/test/automation/proxy_launcher.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/webdriver/frame_path.h"
#include "chrome/test/webdriver/utility_functions.h"
#include "chrome/test/webdriver/webdriver_error.h"
#include "ui/gfx/point.h"
......@@ -42,14 +47,71 @@
namespace {
// Iterates through each browser executable path, and checks if the path exists
// in any of the given locations. If found, returns true and sets |browser_exe|.
bool CheckForChromeExe(const std::vector<FilePath>& browser_exes,
const std::vector<FilePath>& locations,
FilePath* browser_exe) {
for (size_t i = 0; i < browser_exes.size(); ++i) {
for (size_t j = 0; j < locations.size(); ++j) {
FilePath path = locations[j].Append(browser_exes[i]);
if (file_util::PathExists(path)) {
*browser_exe = path;
return true;
}
}
}
return false;
}
// Gets the path to the default Chrome executable. Returns true on success.
bool GetDefaultChromeExe(FilePath* browser_exe) {
std::vector<FilePath> locations;
// Add the directory which this module resides in.
// Instead of using chrome constants, we hardcode these constants here so
// that we can locate chrome or chromium regardless of the branding
// chromedriver is built with. It may be argued that then we need to keep
// these in sync with chrome constants. However, if chrome constants changes,
// we need to look for the previous and new versions to support some
// backwards compatibility.
#if defined(OS_WIN)
FilePath browser_exes_array[] = {
FilePath(L"chrome.exe")
};
#elif defined(OS_MACOSX)
FilePath browser_exes_array[] = {
FilePath("Google Chrome.app/Contents/MacOS/Google Chrome"),
FilePath("Chromium.app/Contents/MacOS/Chromium")
};
#elif defined(OS_LINUX)
FilePath browser_exes_array[] = {
FilePath("google-chrome"),
FilePath("chrome"),
FilePath("chromium"),
FilePath("chromium-browser")
};
#endif
std::vector<FilePath> browser_exes(
browser_exes_array, browser_exes_array + arraysize(browser_exes_array));
// Step 1: Check the directory this module resides in. This is done
// before all else so that the tests will pickup the built chrome.
FilePath module_dir;
if (PathService::Get(base::DIR_MODULE, &module_dir))
locations.push_back(module_dir);
if (PathService::Get(base::DIR_MODULE, &module_dir)) {
for (size_t j = 0; j < browser_exes.size(); ++j) {
FilePath path = module_dir.Append(browser_exes[j]);
if (file_util::PathExists(path)) {
*browser_exe = path;
return true;
}
}
}
// Step 2: Add all possible install locations, in order they should be
// searched. If a location can only hold a chromium install, add it to
// |chromium_locations|. Since on some platforms we cannot tell by the binary
// name whether it is chrome or chromium, we search these locations last.
// We attempt to run chrome before chromium, if any install can be found.
std::vector<FilePath> locations;
std::vector<FilePath> chromium_locations;
#if defined(OS_WIN)
// Add the App Paths registry key location.
const wchar_t kSubKey[] =
......@@ -64,6 +126,7 @@ bool GetDefaultChromeExe(FilePath* browser_exe) {
// Add the user-level location for Chrome.
FilePath app_from_google(L"Google\\Chrome\\Application");
FilePath app_from_chromium(L"Chromium\\Application");
scoped_ptr<base::Environment> env(base::Environment::Create());
std::string home_dir;
if (env->GetVar("userprofile", &home_dir)) {
......@@ -75,6 +138,7 @@ bool GetDefaultChromeExe(FilePath* browser_exe) {
default_location = default_location.Append(L"AppData\\Local");
}
locations.push_back(default_location.Append(app_from_google));
chromium_locations.push_back(default_location.Append(app_from_chromium));
}
// Add the system-level location for Chrome.
......@@ -82,22 +146,27 @@ bool GetDefaultChromeExe(FilePath* browser_exe) {
if (env->GetVar("ProgramFiles", &program_dir)) {
locations.push_back(FilePath(UTF8ToWide(program_dir))
.Append(app_from_google));
chromium_locations.push_back(FilePath(UTF8ToWide(program_dir))
.Append(app_from_chromium));
}
if (env->GetVar("ProgramFiles(x86)", &program_dir)) {
locations.push_back(FilePath(UTF8ToWide(program_dir))
.Append(app_from_google));
chromium_locations.push_back(FilePath(UTF8ToWide(program_dir))
.Append(app_from_chromium));
}
#elif defined(OS_MACOSX)
locations.push_back(FilePath("/Applications"));
std::vector<FilePath> app_dirs;
webdriver::GetApplicationDirs(&app_dirs);
locations.insert(locations.end(), app_dirs.begin(), app_dirs.end());
#elif defined(OS_LINUX)
// Proxy launcher doesn't check for google-chrome, only chrome.
FilePath chrome_sym_link("/usr/bin/google-chrome");
if (file_util::PathExists(chrome_sym_link)) {
FilePath chrome;
if (file_util::ReadSymbolicLink(chrome_sym_link, &chrome)) {
locations.push_back(chrome.DirName());
}
}
locations.push_back(FilePath("/opt/google/chrome"));
locations.push_back(FilePath("/usr/local/bin"));
locations.push_back(FilePath("/usr/local/sbin"));
locations.push_back(FilePath("/usr/bin"));
locations.push_back(FilePath("/usr/sbin"));
locations.push_back(FilePath("/bin"));
locations.push_back(FilePath("/sbin"));
#endif
// Add the current directory.
......@@ -105,15 +174,10 @@ bool GetDefaultChromeExe(FilePath* browser_exe) {
if (file_util::GetCurrentDirectory(&current_dir))
locations.push_back(current_dir);
// Determine the default directory.
for (size_t i = 0; i < locations.size(); ++i) {
FilePath path = locations[i].Append(chrome::kBrowserProcessExecutablePath);
if (file_util::PathExists(path)) {
*browser_exe = path;
return true;
}
}
return false;
// Step 3: For each browser exe path, check each location to see if the
// browser is installed there. Check the chromium locations lastly.
return CheckForChromeExe(browser_exes, locations, browser_exe) ||
CheckForChromeExe(browser_exes, chromium_locations, browser_exe);
}
} // namespace
......@@ -430,6 +494,30 @@ void Automation::NavigateToURL(int tab_id,
*error = new Error(kUnknownError, "Navigation error occurred");
}
void Automation::NavigateToURLAsync(int tab_id,
const std::string& url,
Error** error) {
int windex = 0, tab_index = 0;
*error = GetIndicesForTab(tab_id, &windex, &tab_index);
if (*error)
return;
scoped_refptr<BrowserProxy> browser = automation()->GetBrowserWindow(windex);
if (!browser) {
*error = new Error(kUnknownError, "Couldn't obtain browser proxy");
return;
}
scoped_refptr<TabProxy> tab = browser->GetTab(tab_index);
if (!tab) {
*error = new Error(kUnknownError, "Couldn't obtain tab proxy");
return;
}
if (!tab->NavigateToURLAsync(GURL(url))) {
*error = new Error(kUnknownError, "Unable to navigate to url");
return;
}
}
void Automation::GoForward(int tab_id, Error** error) {
int windex = 0, tab_index = 0;
*error = GetIndicesForTab(tab_id, &windex, &tab_index);
......
......@@ -93,6 +93,7 @@ class Automation {
void CaptureEntirePageAsPNG(int tab_id, const FilePath& path, Error** error);
void NavigateToURL(int tab_id, const std::string& url, Error** error);
void NavigateToURLAsync(int tab_id, const std::string& url, Error** error);
void GoForward(int tab_id, Error** error);
void GoBack(int tab_id, Error** error);
void Reload(int tab_id, Error** error);
......
......@@ -25,6 +25,8 @@
#include "chrome/test/webdriver/session_manager.h"
#include "chrome/test/webdriver/webdriver_error.h"
namespace webdriver {
namespace {
bool WriteBase64DataToFile(const FilePath& filename,
......@@ -42,9 +44,18 @@ bool WriteBase64DataToFile(const FilePath& filename,
return true;
}
} // namespace
Error* GetBooleanCapability(
const base::DictionaryValue* dict, const std::string& key, bool* option) {
Value* value = NULL;
if (dict->GetWithoutPathExpansion(key, &value)) {
if (!value->GetAsBoolean(option)) {
return new Error(kUnknownError, key + " must be a boolean");
}
}
return NULL;
}
namespace webdriver {
} // namespace
CreateSession::CreateSession(const std::vector<std::string>& path_segments,
const DictionaryValue* const parameters)
......@@ -175,11 +186,24 @@ void CreateSession::ExecutePost(Response* const response) {
return;
}
Session::Options options;
Error* error = NULL;
error = GetBooleanCapability(capabilities, "chrome.nativeEvents",
&options.use_native_events);
if (!error) {
error = GetBooleanCapability(capabilities, "chrome.loadAsync",
&options.load_async);
}
if (error) {
response->SetError(error);
return;
}
// Session manages its own liftime, so do not call delete.
Session* session = new Session();
Error* error = session->Init(browser_exe,
temp_user_data_dir,
command_line_options);
Session* session = new Session(options);
error = session->Init(browser_exe,
temp_user_data_dir,
command_line_options);
if (error) {
response->SetError(error);
return;
......@@ -194,20 +218,6 @@ void CreateSession::ExecutePost(Response* const response) {
}
}
bool native_events_required = false;
Value* native_events_value = NULL;
if (capabilities->GetWithoutPathExpansion(
"chrome.nativeEvents", &native_events_value)) {
if (native_events_value->GetAsBoolean(&native_events_required)) {
session->set_use_native_events(native_events_required);
}
}
bool screenshot_on_error = false;
if (capabilities->GetBoolean(
"takeScreenshotOnError", &screenshot_on_error)) {
session->set_screenshot_on_error(screenshot_on_error);
}
LOG(INFO) << "Created session " << session->id();
// Redirect to a relative URI. Although prohibited by the HTTP standard,
// this is what the IEDriver does. Finding the actual IP address is
......
......@@ -56,13 +56,11 @@ void SessionWithID::ExecuteGet(Response* const response) {
// Custom non-standard session info.
temp_value->SetWithoutPathExpansion(
"chrome.chromedriverVersion", Value::CreateStringValue("1.0"));
temp_value->SetWithoutPathExpansion(
"chrome.automationVersion",
"chrome.chromedriverVersion",
Value::CreateStringValue(chrome::kChromeVersion));
temp_value->SetWithoutPathExpansion(
"chrome.nativeEvents",
Value::CreateBooleanValue(session_->use_native_events()));
Value::CreateBooleanValue(session_->options().use_native_events));
response->SetValue(temp_value);
}
......
......@@ -50,7 +50,8 @@ void AssertTimeoutSet(const Session& test_session, int expected_timeout,
} // namespace
TEST(ImplicitWaitCommandTest, SettingImplicitWaits) {
Session test_session;
Session::Options options = Session::Options();
Session test_session(options);
ASSERT_EQ(0, test_session.implicit_wait()) << "Sanity check failed";
std::vector<std::string> path_segments;
......
......@@ -41,19 +41,11 @@ bool WebDriverCommand::Init(Response* const response) {
return false;
}
LOG(INFO) << "Waiting for the page to stop loading";
Error* error = session_->WaitForAllTabsToStopLoading();
Error* error = session_->BeforeExecuteCommand();
if (error) {
response->SetError(error);
return false;
}
LOG(INFO) << "Done waiting for the page to stop loading";
error = session_->SwitchToTopFrameIfCurrentFrameInvalid();
if (error) {
response->SetError(error);
return false;
}
response->SetField("sessionId", Value::CreateStringValue(session_id));
return true;
}
......
......@@ -54,15 +54,22 @@ FrameId& FrameId::operator=(const FrameId& other) {
return *this;
}
Session::Session()
Session::Options::Options()
: use_native_events(false),
load_async(false) {
}
Session::Options::~Options() {
}
Session::Session(const Options& options)
: id_(GenerateRandomID()),
current_target_(FrameId(0, FramePath())),
thread_(id_.c_str()),
async_script_timeout_(0),
implicit_wait_(0),
screenshot_on_error_(false),
use_native_events_(false),
has_alert_prompt_text_(false) {
has_alert_prompt_text_(false),
options_(options) {
SessionManager::GetInstance()->Add(this);
}
......@@ -91,6 +98,19 @@ Error* Session::Init(const FilePath& browser_exe,
return error;
}
Error* Session::BeforeExecuteCommand() {
Error* error = NULL;
if (!options_.load_async) {
LOG(INFO) << "Waiting for the page to stop loading";
error = WaitForAllTabsToStopLoading();
LOG(INFO) << "Done waiting for the page to stop loading";
}
if (!error) {
error = SwitchToTopFrameIfCurrentFrameInvalid();
}
return error;
}
void Session::Terminate() {
RunSessionTask(NewRunnableMethod(
this,
......@@ -228,12 +248,21 @@ Error* Session::DragAndDropFilePaths(
Error* Session::NavigateToURL(const std::string& url) {
Error* error = NULL;
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::NavigateToURL,
current_target_.window_id,
url,
&error));
if (options_.load_async) {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::NavigateToURLAsync,
current_target_.window_id,
url,
&error));
} else {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::NavigateToURL,
current_target_.window_id,
url,
&error));
}
return error;
}
......@@ -1070,26 +1099,14 @@ int Session::implicit_wait() const {
return implicit_wait_;
}
void Session::set_screenshot_on_error(bool error) {
screenshot_on_error_ = error;
}
bool Session::screenshot_on_error() const {
return screenshot_on_error_;
}
void Session::set_use_native_events(bool use_native_events) {
use_native_events_ = use_native_events;
}
bool Session::use_native_events() const {
return use_native_events_;
}
const gfx::Point& Session::get_mouse_position() const {
return mouse_position_;
}
const Session::Options& Session::options() const {
return options_;
}
void Session::RunSessionTask(Task* task) {
base::WaitableEvent done_event(false, false);
thread_.message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod(
......@@ -1197,7 +1214,7 @@ void Session::SendKeysOnSessionThread(const string16& keys, Error** error) {
return;
}
for (size_t i = 0; i < key_events.size(); ++i) {
if (use_native_events_) {
if (options_.use_native_events) {
// The automation provider will generate up/down events for us, we
// only need to call it once as compared to the WebKeyEvent method.
// Hence we filter events by their types, keeping only rawkeydown.
......
......@@ -53,9 +53,23 @@ struct FrameId {
// A session manages its own lifetime.
class Session {
public:
struct Options {
Options();
~Options();
// True if the session should simulate OS-level input. Currently only
// applies to keyboard input.
bool use_native_events;
// True if the session should not wait for page loads and navigate
// asynchronously.
bool load_async;
};
// Adds this |Session| to the |SessionManager|. The session manages its own
// lifetime. Do not call delete.
Session();
explicit Session(const Options& options);
// Removes this |Session| from the |SessionManager|.
~Session();
......@@ -69,6 +83,10 @@ class Session {
const FilePath& user_data_dir,
const CommandLine& options);
// Should be called before executing a command. Performs necessary waits
// and frame switching.
Error* BeforeExecuteCommand();
// Terminates this session and deletes itself.
void Terminate();
......@@ -296,14 +314,10 @@ class Session {
void set_implicit_wait(int timeout_ms);
int implicit_wait() const;
void set_screenshot_on_error(bool error);
bool screenshot_on_error() const;
void set_use_native_events(bool use_native_events);
bool use_native_events() const;
const gfx::Point& get_mouse_position() const;
const Options& options() const;
private:
void RunSessionTask(Task* task);
void RunSessionTaskOnSessionThread(
......@@ -357,15 +371,6 @@ class Session {
// Time (in ms) of how long to wait while searching for a single element.
int implicit_wait_;
// Since screenshots can be very large when in base64 PNG format; the
// client is allowed to dyamically enable/disable screenshots on error
// during the lifetime of the session.
bool screenshot_on_error_;
// True if the session should simulate OS-level input. Currently only applies
// to keyboard input.
bool use_native_events_;
// Vector of the |WebElementId|s for each frame of the current target frame
// path. The first refers to the first frame element in the root document.
// If the target frame is window.top, this will be empty.
......@@ -382,6 +387,8 @@ class Session {
std::string alert_prompt_text_;
bool has_alert_prompt_text_;
Options options_;
DISALLOW_COPY_AND_ASSIGN(Session);
};
......
......@@ -6,6 +6,9 @@
#define CHROME_TEST_WEBDRIVER_UTILITY_FUNCTIONS_H_
#include <string>
#include <vector>
class FilePath;
namespace base {
class Value;
......@@ -19,6 +22,11 @@ std::string GenerateRandomID();
// Returns the equivalent JSON string for the given value.
std::string JsonStringify(const base::Value* value);
#if defined(OS_MACOSX)
// Gets the paths to the user and local application directory.
void GetApplicationDirs(std::vector<FilePath>* app_dirs);
#endif
} // namespace webdriver
#endif // CHROME_TEST_WEBDRIVER_UTILITY_FUNCTIONS_H_
// 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/webdriver/utility_functions.h"
#import <Foundation/Foundation.h>
#include "base/file_path.h"
#include "base/mac/foundation_util.h"
namespace webdriver {
void GetApplicationDirs(std::vector<FilePath>* app_dirs) {
FilePath user_app_dir;
if (base::mac::GetUserDirectory(NSApplicationDirectory, &user_app_dir))
app_dirs->push_back(user_app_dir);
FilePath local_app_dir;
if (base::mac::GetLocalDirectory(NSApplicationDirectory, &local_app_dir))
app_dirs->push_back(local_app_dir);
}
} // namespace webdriver
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment