Commit c0798000 authored by kkania@chromium.org's avatar kkania@chromium.org

[chromedriver] Use single path for handling android/desktop args.

-also allow extensions to be loaded via --load-extension
-change useExistingBrowser to debuggerAddress
BUG=chromedriver:488

Review URL: https://chromiumcodereview.appspot.com/23643005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@220610 0039d316-1c4b-4281-b951-d872f2087c98
parent c069e4bd
...@@ -8,11 +8,13 @@ ...@@ -8,11 +8,13 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/json/string_escape.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h" #include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/test/chromedriver/chrome/log.h" #include "chrome/test/chromedriver/chrome/log.h"
#include "chrome/test/chromedriver/chrome/status.h" #include "chrome/test/chromedriver/chrome/status.h"
...@@ -27,7 +29,7 @@ Status ParseBoolean( ...@@ -27,7 +29,7 @@ Status ParseBoolean(
const base::Value& option, const base::Value& option,
Capabilities* capabilities) { Capabilities* capabilities) {
if (!option.GetAsBoolean(to_set)) if (!option.GetAsBoolean(to_set))
return Status(kUnknownError, "value must be a boolean"); return Status(kUnknownError, "must be a boolean");
return Status(kOk); return Status(kOk);
} }
...@@ -36,13 +38,35 @@ Status ParseString(std::string* to_set, ...@@ -36,13 +38,35 @@ Status ParseString(std::string* to_set,
Capabilities* capabilities) { Capabilities* capabilities) {
std::string str; std::string str;
if (!option.GetAsString(&str)) if (!option.GetAsString(&str))
return Status(kUnknownError, "value must be a string"); return Status(kUnknownError, "must be a string");
if (str.empty()) if (str.empty())
return Status(kUnknownError, "value cannot be empty"); return Status(kUnknownError, "cannot be empty");
*to_set = str; *to_set = str;
return Status(kOk); return Status(kOk);
} }
Status ParseFilePath(base::FilePath* to_set,
const base::Value& option,
Capabilities* capabilities) {
base::FilePath::StringType str;
if (!option.GetAsString(&str))
return Status(kUnknownError, "must be a string");
if (str.empty())
return Status(kUnknownError, "cannot be empty");
*to_set = base::FilePath(str);
return Status(kOk);
}
Status ParseDict(scoped_ptr<base::DictionaryValue>* to_set,
const base::Value& option,
Capabilities* capabilities) {
const base::DictionaryValue* dict = NULL;
if (!option.GetAsDictionary(&dict))
return Status(kUnknownError, "must be a dictionary");
to_set->reset(dict->DeepCopy());
return Status(kOk);
}
Status IgnoreDeprecatedOption( Status IgnoreDeprecatedOption(
Log* log, Log* log,
const char* option_name, const char* option_name,
...@@ -58,72 +82,30 @@ Status IgnoreCapability(const base::Value& option, Capabilities* capabilities) { ...@@ -58,72 +82,30 @@ Status IgnoreCapability(const base::Value& option, Capabilities* capabilities) {
return Status(kOk); return Status(kOk);
} }
Status ParseChromeBinary(
const base::Value& option,
Capabilities* capabilities) {
base::FilePath::StringType path_str;
if (!option.GetAsString(&path_str))
return Status(kUnknownError, "'binary' must be a string");
base::FilePath chrome_exe(path_str);
capabilities->command.SetProgram(chrome_exe);
return Status(kOk);
}
Status ParseLogPath(const base::Value& option, Capabilities* capabilities) { Status ParseLogPath(const base::Value& option, Capabilities* capabilities) {
if (!option.GetAsString(&capabilities->log_path)) if (!option.GetAsString(&capabilities->log_path))
return Status(kUnknownError, "'logPath' must be a string"); return Status(kUnknownError, "must be a string");
return Status(kOk); return Status(kOk);
} }
Status ParseArgs(bool is_android, Status ParseSwitches(const base::Value& option,
const base::Value& option, Capabilities* capabilities) {
Capabilities* capabilities) { const base::ListValue* switches_list = NULL;
const base::ListValue* args_list = NULL; if (!option.GetAsList(&switches_list))
if (!option.GetAsList(&args_list)) return Status(kUnknownError, "must be a list");
return Status(kUnknownError, "'args' must be a list"); for (size_t i = 0; i < switches_list->GetSize(); ++i) {
for (size_t i = 0; i < args_list->GetSize(); ++i) {
std::string arg_string; std::string arg_string;
if (!args_list->GetString(i, &arg_string)) if (!switches_list->GetString(i, &arg_string))
return Status(kUnknownError, "each argument must be a string"); return Status(kUnknownError, "each argument must be a string");
if (is_android) { capabilities->switches.SetUnparsedSwitch(arg_string);
capabilities->android_args += "--" + arg_string + " ";
} else {
size_t separator_index = arg_string.find("=");
if (separator_index != std::string::npos) {
CommandLine::StringType arg_string_native;
if (!args_list->GetString(i, &arg_string_native))
return Status(kUnknownError, "each argument must be a string");
capabilities->command.AppendSwitchNative(
arg_string.substr(0, separator_index),
arg_string_native.substr(separator_index + 1));
} else {
capabilities->command.AppendSwitch(arg_string);
}
}
} }
return Status(kOk); return Status(kOk);
} }
Status ParsePrefs(const base::Value& option, Capabilities* capabilities) {
const base::DictionaryValue* prefs = NULL;
if (!option.GetAsDictionary(&prefs))
return Status(kUnknownError, "'prefs' must be a dictionary");
capabilities->prefs.reset(prefs->DeepCopy());
return Status(kOk);
}
Status ParseLocalState(const base::Value& option, Capabilities* capabilities) {
const base::DictionaryValue* local_state = NULL;
if (!option.GetAsDictionary(&local_state))
return Status(kUnknownError, "'localState' must be a dictionary");
capabilities->local_state.reset(local_state->DeepCopy());
return Status(kOk);
}
Status ParseExtensions(const base::Value& option, Capabilities* capabilities) { Status ParseExtensions(const base::Value& option, Capabilities* capabilities) {
const base::ListValue* extensions = NULL; const base::ListValue* extensions = NULL;
if (!option.GetAsList(&extensions)) if (!option.GetAsList(&extensions))
return Status(kUnknownError, "'extensions' must be a list"); return Status(kUnknownError, "must be a list");
for (size_t i = 0; i < extensions->GetSize(); ++i) { for (size_t i = 0; i < extensions->GetSize(); ++i) {
std::string extension; std::string extension;
if (!extensions->GetString(i, &extension)) { if (!extensions->GetString(i, &extension)) {
...@@ -138,22 +120,22 @@ Status ParseExtensions(const base::Value& option, Capabilities* capabilities) { ...@@ -138,22 +120,22 @@ Status ParseExtensions(const base::Value& option, Capabilities* capabilities) {
Status ParseProxy(const base::Value& option, Capabilities* capabilities) { Status ParseProxy(const base::Value& option, Capabilities* capabilities) {
const base::DictionaryValue* proxy_dict; const base::DictionaryValue* proxy_dict;
if (!option.GetAsDictionary(&proxy_dict)) if (!option.GetAsDictionary(&proxy_dict))
return Status(kUnknownError, "'proxy' must be a dictionary"); return Status(kUnknownError, "must be a dictionary");
std::string proxy_type; std::string proxy_type;
if (!proxy_dict->GetString("proxyType", &proxy_type)) if (!proxy_dict->GetString("proxyType", &proxy_type))
return Status(kUnknownError, "'proxyType' must be a string"); return Status(kUnknownError, "'proxyType' must be a string");
proxy_type = StringToLowerASCII(proxy_type); proxy_type = StringToLowerASCII(proxy_type);
if (proxy_type == "direct") { if (proxy_type == "direct") {
capabilities->command.AppendSwitch("no-proxy-server"); capabilities->switches.SetSwitch("no-proxy-server");
} else if (proxy_type == "system") { } else if (proxy_type == "system") {
// Chrome default. // Chrome default.
} else if (proxy_type == "pac") { } else if (proxy_type == "pac") {
CommandLine::StringType proxy_pac_url; CommandLine::StringType proxy_pac_url;
if (!proxy_dict->GetString("proxyAutoconfigUrl", &proxy_pac_url)) if (!proxy_dict->GetString("proxyAutoconfigUrl", &proxy_pac_url))
return Status(kUnknownError, "'proxyAutoconfigUrl' must be a string"); return Status(kUnknownError, "'proxyAutoconfigUrl' must be a string");
capabilities->command.AppendSwitchNative("proxy-pac-url", proxy_pac_url); capabilities->switches.SetSwitch("proxy-pac-url", proxy_pac_url);
} else if (proxy_type == "autodetect") { } else if (proxy_type == "autodetect") {
capabilities->command.AppendSwitch("proxy-auto-detect"); capabilities->switches.SetSwitch("proxy-auto-detect");
} else if (proxy_type == "manual") { } else if (proxy_type == "manual") {
const char* proxy_servers_options[][2] = { const char* proxy_servers_options[][2] = {
{"ftpProxy", "ftp"}, {"httpProxy", "http"}, {"sslProxy", "https"}}; {"ftpProxy", "ftp"}, {"httpProxy", "http"}, {"sslProxy", "https"}};
...@@ -192,10 +174,10 @@ Status ParseProxy(const base::Value& option, Capabilities* capabilities) { ...@@ -192,10 +174,10 @@ Status ParseProxy(const base::Value& option, Capabilities* capabilities) {
"proxy capabilities were found"); "proxy capabilities were found");
} }
if (!proxy_servers.empty()) if (!proxy_servers.empty())
capabilities->command.AppendSwitchASCII("proxy-server", proxy_servers); capabilities->switches.SetSwitch("proxy-server", proxy_servers);
if (!proxy_bypass_list.empty()) { if (!proxy_bypass_list.empty()) {
capabilities->command.AppendSwitchASCII("proxy-bypass-list", capabilities->switches.SetSwitch("proxy-bypass-list",
proxy_bypass_list); proxy_bypass_list);
} }
} else { } else {
return Status(kUnknownError, "unrecognized proxy type:" + proxy_type); return Status(kUnknownError, "unrecognized proxy type:" + proxy_type);
...@@ -207,7 +189,7 @@ Status ParseExcludeSwitches(const base::Value& option, ...@@ -207,7 +189,7 @@ Status ParseExcludeSwitches(const base::Value& option,
Capabilities* capabilities) { Capabilities* capabilities) {
const base::ListValue* switches = NULL; const base::ListValue* switches = NULL;
if (!option.GetAsList(&switches)) if (!option.GetAsList(&switches))
return Status(kUnknownError, "'excludeSwitches' must be a list"); return Status(kUnknownError, "must be a list");
for (size_t i = 0; i < switches->GetSize(); ++i) { for (size_t i = 0; i < switches->GetSize(); ++i) {
std::string switch_name; std::string switch_name;
if (!switches->GetString(i, &switch_name)) { if (!switches->GetString(i, &switch_name)) {
...@@ -233,9 +215,9 @@ Status ParseUseExistingBrowser(const base::Value& option, ...@@ -233,9 +215,9 @@ Status ParseUseExistingBrowser(const base::Value& option,
int port = 0; int port = 0;
base::StringToInt(values[1], &port); base::StringToInt(values[1], &port);
if (port <= 0) if (port <= 0)
return Status(kUnknownError, "port must be >= 0"); return Status(kUnknownError, "port must be > 0");
capabilities->use_existing_browser = NetAddress(values[0], port); capabilities->debugger_address = NetAddress(values[0], port);
return Status(kOk); return Status(kOk);
} }
...@@ -243,7 +225,7 @@ Status ParseLoggingPrefs(const base::Value& option, ...@@ -243,7 +225,7 @@ Status ParseLoggingPrefs(const base::Value& option,
Capabilities* capabilities) { Capabilities* capabilities) {
const base::DictionaryValue* logging_prefs_dict = NULL; const base::DictionaryValue* logging_prefs_dict = NULL;
if (!option.GetAsDictionary(&logging_prefs_dict)) if (!option.GetAsDictionary(&logging_prefs_dict))
return Status(kUnknownError, "'loggingPrefs' must be a dictionary"); return Status(kUnknownError, "must be a dictionary");
// TODO(klm): verify log types. // TODO(klm): verify log types.
// TODO(klm): verify log levels. // TODO(klm): verify log levels.
...@@ -257,14 +239,15 @@ Status ParseChromeOptions( ...@@ -257,14 +239,15 @@ Status ParseChromeOptions(
Capabilities* capabilities) { Capabilities* capabilities) {
const base::DictionaryValue* chrome_options = NULL; const base::DictionaryValue* chrome_options = NULL;
if (!capability.GetAsDictionary(&chrome_options)) if (!capability.GetAsDictionary(&chrome_options))
return Status(kUnknownError, "'chromeOptions' must be a dictionary"); return Status(kUnknownError, "must be a dictionary");
bool is_android = chrome_options->HasKey("androidPackage"); bool is_android = chrome_options->HasKey("androidPackage");
bool is_existing = chrome_options->HasKey("useExistingBrowser"); bool is_existing = chrome_options->HasKey("debuggerAddress");
std::map<std::string, Parser> parser_map; std::map<std::string, Parser> parser_map;
// Ignore 'binary' and 'extensions' capability, since the Java client // Ignore 'args', 'binary' and 'extensions' capabilities by default, since the
// always passes them. // Java client always passes them.
parser_map["args"] = base::Bind(&IgnoreCapability);
parser_map["binary"] = base::Bind(&IgnoreCapability); parser_map["binary"] = base::Bind(&IgnoreCapability);
parser_map["extensions"] = base::Bind(&IgnoreCapability); parser_map["extensions"] = base::Bind(&IgnoreCapability);
if (is_android) { if (is_android) {
...@@ -276,23 +259,23 @@ Status ParseChromeOptions( ...@@ -276,23 +259,23 @@ Status ParseChromeOptions(
base::Bind(&ParseString, &capabilities->android_package); base::Bind(&ParseString, &capabilities->android_package);
parser_map["androidProcess"] = parser_map["androidProcess"] =
base::Bind(&ParseString, &capabilities->android_process); base::Bind(&ParseString, &capabilities->android_process);
parser_map["args"] = base::Bind(&ParseArgs, true); parser_map["args"] = base::Bind(&ParseSwitches);
} else if (is_existing) { } else if (is_existing) {
parser_map["args"] = base::Bind(&IgnoreCapability); parser_map["debuggerAddress"] = base::Bind(&ParseUseExistingBrowser);
parser_map["useExistingBrowser"] = base::Bind(&ParseUseExistingBrowser);
} else { } else {
parser_map["forceDevToolsScreenshot"] = base::Bind( parser_map["args"] = base::Bind(&ParseSwitches);
&ParseBoolean, &capabilities->force_devtools_screenshot); parser_map["binary"] = base::Bind(&ParseFilePath, &capabilities->binary);
parser_map["args"] = base::Bind(&ParseArgs, false);
parser_map["binary"] = base::Bind(&ParseChromeBinary);
parser_map["detach"] = base::Bind(&ParseBoolean, &capabilities->detach); parser_map["detach"] = base::Bind(&ParseBoolean, &capabilities->detach);
parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches); parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches);
parser_map["extensions"] = base::Bind(&ParseExtensions); parser_map["extensions"] = base::Bind(&ParseExtensions);
parser_map["forceDevToolsScreenshot"] = base::Bind(
&ParseBoolean, &capabilities->force_devtools_screenshot);
parser_map["loadAsync"] = parser_map["loadAsync"] =
base::Bind(&IgnoreDeprecatedOption, log, "loadAsync"); base::Bind(&IgnoreDeprecatedOption, log, "loadAsync");
parser_map["localState"] = base::Bind(&ParseLocalState); parser_map["localState"] =
base::Bind(&ParseDict, &capabilities->local_state);
parser_map["logPath"] = base::Bind(&ParseLogPath); parser_map["logPath"] = base::Bind(&ParseLogPath);
parser_map["prefs"] = base::Bind(&ParsePrefs); parser_map["prefs"] = base::Bind(&ParseDict, &capabilities->prefs);
} }
for (base::DictionaryValue::Iterator it(*chrome_options); !it.IsAtEnd(); for (base::DictionaryValue::Iterator it(*chrome_options); !it.IsAtEnd();
...@@ -310,10 +293,116 @@ Status ParseChromeOptions( ...@@ -310,10 +293,116 @@ Status ParseChromeOptions(
} // namespace } // namespace
Switches::Switches() {}
Switches::~Switches() {}
void Switches::SetSwitch(const std::string& name) {
SetSwitch(name, NativeString());
}
void Switches::SetSwitch(const std::string& name, const std::string& value) {
#if defined(OS_WIN)
SetSwitch(name, UTF8ToUTF16(value));
#else
switch_map_[name] = value;
#endif
}
void Switches::SetSwitch(const std::string& name, const string16& value) {
#if defined(OS_WIN)
switch_map_[name] = value;
#else
SetSwitch(name, UTF16ToUTF8(value));
#endif
}
void Switches::SetSwitch(const std::string& name, const base::FilePath& value) {
SetSwitch(name, value.value());
}
void Switches::SetFromSwitches(const Switches& switches) {
for (SwitchMap::const_iterator iter = switches.switch_map_.begin();
iter != switches.switch_map_.end();
++iter) {
switch_map_[iter->first] = iter->second;
}
}
void Switches::SetUnparsedSwitch(const std::string& unparsed_switch) {
std::string value;
size_t equals_index = unparsed_switch.find('=');
if (equals_index != std::string::npos)
value = unparsed_switch.substr(equals_index + 1);
std::string name;
size_t start_index = 0;
if (unparsed_switch.substr(0, 2) == "--")
start_index = 2;
name = unparsed_switch.substr(start_index, equals_index - start_index);
SetSwitch(name, value);
}
void Switches::RemoveSwitch(const std::string& name) {
switch_map_.erase(name);
}
bool Switches::HasSwitch(const std::string& name) const {
return switch_map_.count(name) > 0;
}
std::string Switches::GetSwitchValue(const std::string& name) const {
NativeString value = GetSwitchValueNative(name);
#if defined(OS_WIN)
return UTF16ToUTF8(value);
#else
return value;
#endif
}
Switches::NativeString Switches::GetSwitchValueNative(
const std::string& name) const {
SwitchMap::const_iterator iter = switch_map_.find(name);
if (iter == switch_map_.end())
return NativeString();
return iter->second;
}
size_t Switches::GetSize() const {
return switch_map_.size();
}
void Switches::AppendToCommandLine(CommandLine* command) const {
for (SwitchMap::const_iterator iter = switch_map_.begin();
iter != switch_map_.end();
++iter) {
command->AppendSwitchNative(iter->first, iter->second);
}
}
std::string Switches::ToString() const {
std::string str;
SwitchMap::const_iterator iter = switch_map_.begin();
while (iter != switch_map_.end()) {
str += "--" + iter->first;
std::string value = GetSwitchValue(iter->first);
if (value.length()) {
if (value.find(' ') != std::string::npos)
value = base::GetDoubleQuotedJson(value);
str += "=" + value;
}
++iter;
if (iter == switch_map_.end())
break;
str += " ";
}
return str;
}
Capabilities::Capabilities() Capabilities::Capabilities()
: force_devtools_screenshot(false), : detach(false),
detach(false), force_devtools_screenshot(false) {}
command(CommandLine::NO_PROGRAM) {}
Capabilities::~Capabilities() {} Capabilities::~Capabilities() {}
...@@ -322,7 +411,7 @@ bool Capabilities::IsAndroid() const { ...@@ -322,7 +411,7 @@ bool Capabilities::IsAndroid() const {
} }
bool Capabilities::IsExistingBrowser() const { bool Capabilities::IsExistingBrowser() const {
return use_existing_browser.IsValid(); return debugger_address.IsValid();
} }
Status Capabilities::Parse( Status Capabilities::Parse(
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_ #ifndef CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_
#define CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_ #define CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_
#include <map>
#include <set> #include <set>
#include <string> #include <string>
#include <vector> #include <vector>
...@@ -12,15 +13,50 @@ ...@@ -12,15 +13,50 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
#include "chrome/test/chromedriver/net/net_util.h" #include "chrome/test/chromedriver/net/net_util.h"
namespace base { namespace base {
class DictionaryValue; class DictionaryValue;
} }
class CommandLine;
class Log; class Log;
class Status; class Status;
class Switches {
public:
typedef base::FilePath::StringType NativeString;
Switches();
~Switches();
void SetSwitch(const std::string& name);
void SetSwitch(const std::string& name, const std::string& value);
void SetSwitch(const std::string& name, const string16& value);
void SetSwitch(const std::string& name, const base::FilePath& value);
// In case of same key, |switches| will override.
void SetFromSwitches(const Switches& switches);
// Sets a switch from the capabilities, of the form [--]name[=value].
void SetUnparsedSwitch(const std::string& unparsed_switch);
void RemoveSwitch(const std::string& name);
bool HasSwitch(const std::string& name) const;
std::string GetSwitchValue(const std::string& name) const;
NativeString GetSwitchValueNative(const std::string& name) const;
size_t GetSize() const;
void AppendToCommandLine(CommandLine* command) const;
std::string ToString() const;
private:
typedef std::map<std::string, NativeString> SwitchMap;
SwitchMap switch_map_;
};
struct Capabilities { struct Capabilities {
Capabilities(); Capabilities();
~Capabilities(); ~Capabilities();
...@@ -33,34 +69,43 @@ struct Capabilities { ...@@ -33,34 +69,43 @@ struct Capabilities {
Status Parse(const base::DictionaryValue& desired_caps, Log* log); Status Parse(const base::DictionaryValue& desired_caps, Log* log);
// True if should always use DevTools for taking screenshots. std::string android_activity;
// This is experimental and may be removed at a later point.
bool force_devtools_screenshot; std::string android_device_serial;
std::string android_package;
std::string android_process;
base::FilePath binary;
// If provided, the remote debugging address to connect to.
NetAddress debugger_address;
// Whether the lifetime of the started Chrome browser process should be // Whether the lifetime of the started Chrome browser process should be
// bound to ChromeDriver's process. If true, Chrome will not quit if // bound to ChromeDriver's process. If true, Chrome will not quit if
// ChromeDriver dies. // ChromeDriver dies.
bool detach; bool detach;
std::string android_package; // Set of switches which should be removed from default list when launching
std::string android_activity; // Chrome.
std::string android_process; std::set<std::string> exclude_switches;
std::string android_device_serial;
std::string android_args;
std::string log_path;
CommandLine command;
scoped_ptr<base::DictionaryValue> prefs;
scoped_ptr<base::DictionaryValue> local_state;
std::vector<std::string> extensions; std::vector<std::string> extensions;
// True if should always use DevTools for taking screenshots.
// This is experimental and may be removed at a later point.
bool force_devtools_screenshot;
scoped_ptr<base::DictionaryValue> local_state;
std::string log_path;
scoped_ptr<base::DictionaryValue> logging_prefs; scoped_ptr<base::DictionaryValue> logging_prefs;
// Set of switches which should be removed from default list when launching scoped_ptr<base::DictionaryValue> prefs;
// Chrome.
std::set<std::string> exclude_switches;
// If provided, the remote debugging address to connect to. Switches switches;
NetAddress use_existing_browser;
}; };
#endif // CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_ #endif // CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_
...@@ -9,6 +9,94 @@ ...@@ -9,6 +9,94 @@
#include "chrome/test/chromedriver/chrome/status.h" #include "chrome/test/chromedriver/chrome/status.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
TEST(Switches, Empty) {
Switches switches;
CommandLine cmd(CommandLine::NO_PROGRAM);
switches.AppendToCommandLine(&cmd);
ASSERT_EQ(0u, cmd.GetSwitches().size());
ASSERT_EQ("", switches.ToString());
}
TEST(Switches, NoValue) {
Switches switches;
switches.SetSwitch("hello");
ASSERT_TRUE(switches.HasSwitch("hello"));
ASSERT_EQ("", switches.GetSwitchValue("hello"));
CommandLine cmd(CommandLine::NO_PROGRAM);
switches.AppendToCommandLine(&cmd);
ASSERT_TRUE(cmd.HasSwitch("hello"));
ASSERT_EQ(FILE_PATH_LITERAL(""), cmd.GetSwitchValueNative("hello"));
ASSERT_EQ("--hello", switches.ToString());
}
TEST(Switches, Value) {
Switches switches;
switches.SetSwitch("hello", "there");
ASSERT_TRUE(switches.HasSwitch("hello"));
ASSERT_EQ("there", switches.GetSwitchValue("hello"));
CommandLine cmd(CommandLine::NO_PROGRAM);
switches.AppendToCommandLine(&cmd);
ASSERT_TRUE(cmd.HasSwitch("hello"));
ASSERT_EQ(FILE_PATH_LITERAL("there"), cmd.GetSwitchValueNative("hello"));
ASSERT_EQ("--hello=there", switches.ToString());
}
TEST(Switches, FromOther) {
Switches switches;
switches.SetSwitch("a", "1");
switches.SetSwitch("b", "1");
Switches switches2;
switches2.SetSwitch("b", "2");
switches2.SetSwitch("c", "2");
switches.SetFromSwitches(switches2);
ASSERT_EQ("--a=1 --b=2 --c=2", switches.ToString());
}
TEST(Switches, Remove) {
Switches switches;
switches.SetSwitch("a", "1");
switches.RemoveSwitch("a");
ASSERT_FALSE(switches.HasSwitch("a"));
}
TEST(Switches, Quoting) {
Switches switches;
switches.SetSwitch("hello", "a b");
switches.SetSwitch("hello2", " '\" ");
ASSERT_EQ("--hello=\"a b\" --hello2=\" '\\\" \"", switches.ToString());
}
TEST(Switches, Multiple) {
Switches switches;
switches.SetSwitch("switch");
switches.SetSwitch("hello", "there");
CommandLine cmd(CommandLine::NO_PROGRAM);
switches.AppendToCommandLine(&cmd);
ASSERT_TRUE(cmd.HasSwitch("switch"));
ASSERT_TRUE(cmd.HasSwitch("hello"));
ASSERT_EQ(FILE_PATH_LITERAL("there"), cmd.GetSwitchValueNative("hello"));
ASSERT_EQ("--hello=there --switch", switches.ToString());
}
TEST(Switches, Unparsed) {
Switches switches;
switches.SetUnparsedSwitch("a");
switches.SetUnparsedSwitch("--b");
switches.SetUnparsedSwitch("--c=1");
switches.SetUnparsedSwitch("d=1");
switches.SetUnparsedSwitch("-e=--1=1");
ASSERT_EQ("---e=--1=1 --a --b --c=1 --d=1", switches.ToString());
}
TEST(ParseCapabilities, WithAndroidPackage) { TEST(ParseCapabilities, WithAndroidPackage) {
Capabilities capabilities; Capabilities capabilities;
base::DictionaryValue caps; base::DictionaryValue caps;
...@@ -48,68 +136,23 @@ TEST(ParseCapabilities, LogPath) { ...@@ -48,68 +136,23 @@ TEST(ParseCapabilities, LogPath) {
ASSERT_STREQ("path/to/logfile", capabilities.log_path.c_str()); ASSERT_STREQ("path/to/logfile", capabilities.log_path.c_str());
} }
TEST(ParseCapabilities, NoArgs) { TEST(ParseCapabilities, Args) {
Capabilities capabilities;
base::ListValue args;
ASSERT_TRUE(args.empty());
base::DictionaryValue caps;
caps.Set("chromeOptions.args", args.DeepCopy());
Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.command.GetSwitches().empty());
}
TEST(ParseCapabilities, SingleArgWithoutValue) {
Capabilities capabilities;
base::ListValue args;
args.AppendString("enable-nacl");
ASSERT_EQ(1u, args.GetSize());
base::DictionaryValue caps;
caps.Set("chromeOptions.args", args.DeepCopy());
Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.command.GetSwitches().size());
ASSERT_TRUE(capabilities.command.HasSwitch("enable-nacl"));
}
TEST(ParseCapabilities, SingleArgWithValue) {
Capabilities capabilities;
base::ListValue args;
args.AppendString("load-extension=/test/extension");
ASSERT_EQ(1u, args.GetSize());
base::DictionaryValue caps;
caps.Set("chromeOptions.args", args.DeepCopy());
Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.command.GetSwitches().size());
ASSERT_TRUE(capabilities.command.HasSwitch("load-extension"));
ASSERT_STREQ(
"/test/extension",
capabilities.command.GetSwitchValueASCII("load-extension").c_str());
}
TEST(ParseCapabilities, MultipleArgs) {
Capabilities capabilities; Capabilities capabilities;
base::ListValue args; base::ListValue args;
args.AppendString("arg1"); args.AppendString("arg1");
args.AppendString("arg2=val"); args.AppendString("arg2=val");
args.AppendString("arg3='a space'");
ASSERT_EQ(3u, args.GetSize());
base::DictionaryValue caps; base::DictionaryValue caps;
caps.Set("chromeOptions.args", args.DeepCopy()); caps.Set("chromeOptions.args", args.DeepCopy());
Logger log(Log::kError); Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log); Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_EQ(3u, capabilities.command.GetSwitches().size());
ASSERT_TRUE(capabilities.command.HasSwitch("arg1")); ASSERT_EQ(2u, capabilities.switches.GetSize());
ASSERT_TRUE(capabilities.command.HasSwitch("arg2")); ASSERT_TRUE(capabilities.switches.HasSwitch("arg1"));
ASSERT_STREQ("val", capabilities.command.GetSwitchValueASCII("arg2").c_str()); ASSERT_TRUE(capabilities.switches.HasSwitch("arg2"));
ASSERT_TRUE(capabilities.command.HasSwitch("arg3")); ASSERT_EQ("", capabilities.switches.GetSwitchValue("arg1"));
ASSERT_STREQ("'a space'", ASSERT_EQ("val", capabilities.switches.GetSwitchValue("arg2"));
capabilities.command.GetSwitchValueASCII("arg3").c_str());
} }
TEST(ParseCapabilities, Prefs) { TEST(ParseCapabilities, Prefs) {
...@@ -184,8 +227,8 @@ TEST(ParseCapabilities, DirectProxy) { ...@@ -184,8 +227,8 @@ TEST(ParseCapabilities, DirectProxy) {
Logger log(Log::kError); Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log); Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.command.GetSwitches().size()); ASSERT_EQ(1u, capabilities.switches.GetSize());
ASSERT_TRUE(capabilities.command.HasSwitch("no-proxy-server")); ASSERT_TRUE(capabilities.switches.HasSwitch("no-proxy-server"));
} }
TEST(ParseCapabilities, SystemProxy) { TEST(ParseCapabilities, SystemProxy) {
...@@ -197,7 +240,7 @@ TEST(ParseCapabilities, SystemProxy) { ...@@ -197,7 +240,7 @@ TEST(ParseCapabilities, SystemProxy) {
Logger log(Log::kError); Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log); Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.command.GetSwitches().empty()); ASSERT_EQ(0u, capabilities.switches.GetSize());
} }
TEST(ParseCapabilities, PacProxy) { TEST(ParseCapabilities, PacProxy) {
...@@ -210,10 +253,8 @@ TEST(ParseCapabilities, PacProxy) { ...@@ -210,10 +253,8 @@ TEST(ParseCapabilities, PacProxy) {
Logger log(Log::kError); Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log); Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.command.GetSwitches().size()); ASSERT_EQ(1u, capabilities.switches.GetSize());
ASSERT_STREQ( ASSERT_EQ("test.wpad", capabilities.switches.GetSwitchValue("proxy-pac-url"));
"test.wpad",
capabilities.command.GetSwitchValueASCII("proxy-pac-url").c_str());
} }
TEST(ParseCapabilities, MissingProxyAutoconfigUrl) { TEST(ParseCapabilities, MissingProxyAutoconfigUrl) {
...@@ -237,8 +278,8 @@ TEST(ParseCapabilities, AutodetectProxy) { ...@@ -237,8 +278,8 @@ TEST(ParseCapabilities, AutodetectProxy) {
Logger log(Log::kError); Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log); Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.command.GetSwitches().size()); ASSERT_EQ(1u, capabilities.switches.GetSize());
ASSERT_TRUE(capabilities.command.HasSwitch("proxy-auto-detect")); ASSERT_TRUE(capabilities.switches.HasSwitch("proxy-auto-detect"));
} }
TEST(ParseCapabilities, ManualProxy) { TEST(ParseCapabilities, ManualProxy) {
...@@ -254,13 +295,13 @@ TEST(ParseCapabilities, ManualProxy) { ...@@ -254,13 +295,13 @@ TEST(ParseCapabilities, ManualProxy) {
Logger log(Log::kError); Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log); Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_EQ(2u, capabilities.command.GetSwitches().size()); ASSERT_EQ(2u, capabilities.switches.GetSize());
ASSERT_STREQ( ASSERT_EQ(
"ftp=localhost:9001;http=localhost:8001;https=localhost:10001", "ftp=localhost:9001;http=localhost:8001;https=localhost:10001",
capabilities.command.GetSwitchValueASCII("proxy-server").c_str()); capabilities.switches.GetSwitchValue("proxy-server"));
ASSERT_STREQ( ASSERT_EQ(
"google.com, youtube.com", "google.com, youtube.com",
capabilities.command.GetSwitchValueASCII("proxy-bypass-list").c_str()); capabilities.switches.GetSwitchValue("proxy-bypass-list"));
} }
TEST(ParseCapabilities, MissingSettingForManualProxy) { TEST(ParseCapabilities, MissingSettingForManualProxy) {
...@@ -286,11 +327,11 @@ TEST(ParseCapabilities, IgnoreNullValueForManualProxy) { ...@@ -286,11 +327,11 @@ TEST(ParseCapabilities, IgnoreNullValueForManualProxy) {
Logger log(Log::kError); Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log); Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.command.GetSwitches().size()); ASSERT_EQ(1u, capabilities.switches.GetSize());
ASSERT_TRUE(capabilities.command.HasSwitch("proxy-server")); ASSERT_TRUE(capabilities.switches.HasSwitch("proxy-server"));
ASSERT_STREQ( ASSERT_EQ(
"ftp=localhost:9001", "ftp=localhost:9001",
capabilities.command.GetSwitchValueASCII("proxy-server").c_str()); capabilities.switches.GetSwitchValue("proxy-server"));
} }
TEST(ParseCapabilities, LoggingPrefsOk) { TEST(ParseCapabilities, LoggingPrefsOk) {
...@@ -337,11 +378,11 @@ TEST(ParseCapabilities, ExcludeSwitches) { ...@@ -337,11 +378,11 @@ TEST(ParseCapabilities, ExcludeSwitches) {
TEST(ParseCapabilities, UseExistingBrowser) { TEST(ParseCapabilities, UseExistingBrowser) {
Capabilities capabilities; Capabilities capabilities;
base::DictionaryValue caps; base::DictionaryValue caps;
caps.SetString("chromeOptions.useExistingBrowser", "abc:123"); caps.SetString("chromeOptions.debuggerAddress", "abc:123");
Logger log(Log::kError); Logger log(Log::kError);
Status status = capabilities.Parse(caps, &log); Status status = capabilities.Parse(caps, &log);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.IsExistingBrowser()); ASSERT_TRUE(capabilities.IsExistingBrowser());
ASSERT_EQ("abc", capabilities.use_existing_browser.host()); ASSERT_EQ("abc", capabilities.debugger_address.host());
ASSERT_EQ(123, capabilities.use_existing_browser.port()); ASSERT_EQ(123, capabilities.debugger_address.port());
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/json/string_escape.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
...@@ -118,13 +119,13 @@ Status AdbImpl::SetCommandLineFile(const std::string& device_serial, ...@@ -118,13 +119,13 @@ Status AdbImpl::SetCommandLineFile(const std::string& device_serial,
const std::string& exec_name, const std::string& exec_name,
const std::string& args) { const std::string& args) {
std::string response; std::string response;
if (args.find("'") != std::string::npos) std::string quoted_command =
return Status(kUnknownError, base::GetDoubleQuotedJson(exec_name + " " + args);
"Chrome command line arguments must not contain single quotes");
Status status = ExecuteHostShellCommand( Status status = ExecuteHostShellCommand(
device_serial, device_serial,
"echo '" + exec_name + base::StringPrintf("echo %s > %s; echo $?",
" " + args + "'> " + command_line_file + "; echo $?", quoted_command.c_str(),
command_line_file.c_str()),
&response); &response);
if (!status.IsOk()) if (!status.IsOk())
return status; return status;
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
namespace { namespace {
const char* kCommonSwitches[] = { const char* kCommonSwitches[] = {
"ignore-certificate-errors", "metrics-recording-only"}; "ignore-certificate-errors", "metrics-recording-only"};
Status UnpackAutomationExtension(const base::FilePath& temp_dir, Status UnpackAutomationExtension(const base::FilePath& temp_dir,
base::FilePath* automation_extension) { base::FilePath* automation_extension) {
...@@ -68,66 +68,58 @@ Status UnpackAutomationExtension(const base::FilePath& temp_dir, ...@@ -68,66 +68,58 @@ Status UnpackAutomationExtension(const base::FilePath& temp_dir,
return Status(kOk); return Status(kOk);
} }
void AddSwitches(CommandLine* command,
const char* switches[],
size_t switch_count,
const std::set<std::string>& exclude_switches) {
for (size_t i = 0; i < switch_count; ++i) {
if (exclude_switches.find(switches[i]) == exclude_switches.end())
command->AppendSwitch(switches[i]);
}
}
Status PrepareCommandLine(int port, Status PrepareCommandLine(int port,
const Capabilities& capabilities, const Capabilities& capabilities,
CommandLine* prepared_command, CommandLine* prepared_command,
base::ScopedTempDir* user_data_dir, base::ScopedTempDir* user_data_dir,
base::ScopedTempDir* extension_dir, base::ScopedTempDir* extension_dir,
std::vector<std::string>* extension_bg_pages) { std::vector<std::string>* extension_bg_pages) {
CommandLine command = capabilities.command; base::FilePath program = capabilities.binary;
base::FilePath program = command.GetProgram();
if (program.empty()) { if (program.empty()) {
if (!FindChrome(&program)) if (!FindChrome(&program))
return Status(kUnknownError, "cannot find Chrome binary"); return Status(kUnknownError, "cannot find Chrome binary");
command.SetProgram(program);
} else if (!base::PathExists(program)) { } else if (!base::PathExists(program)) {
return Status(kUnknownError, return Status(kUnknownError,
base::StringPrintf("no chrome binary at %" PRFilePath, base::StringPrintf("no chrome binary at %" PRFilePath,
program.value().c_str())); program.value().c_str()));
} }
CommandLine command(program);
Switches switches;
// TODO(chrisgao): Add "disable-sync" when chrome 30- is not supported.
// For chrome 30-, it leads to crash when opening chrome://settings.
for (size_t i = 0; i < arraysize(kCommonSwitches); ++i)
switches.SetSwitch(kCommonSwitches[i]);
switches.SetSwitch("disable-hang-monitor");
switches.SetSwitch("disable-prompt-on-repost");
switches.SetSwitch("full-memory-crash-report");
switches.SetSwitch("no-first-run");
switches.SetSwitch("disable-background-networking");
switches.SetSwitch("disable-web-resources");
switches.SetSwitch("safebrowsing-disable-auto-update");
switches.SetSwitch("safebrowsing-disable-download-protection");
switches.SetSwitch("disable-client-side-phishing-detection");
switches.SetSwitch("disable-component-update");
switches.SetSwitch("disable-default-apps");
switches.SetSwitch("enable-logging");
switches.SetSwitch("logging-level", "1");
switches.SetSwitch("password-store", "basic");
switches.SetSwitch("use-mock-keychain");
switches.SetSwitch("remote-debugging-port", base::IntToString(port));
for (std::set<std::string>::const_iterator iter =
capabilities.exclude_switches.begin();
iter != capabilities.exclude_switches.end();
++iter) {
switches.RemoveSwitch(*iter);
}
switches.SetFromSwitches(capabilities.switches);
const char* excludable_switches[] = { if (!switches.HasSwitch("user-data-dir")) {
"disable-hang-monitor",
"disable-prompt-on-repost",
"full-memory-crash-report",
"no-first-run",
"disable-background-networking",
// TODO(chrisgao): Add "disable-sync" when chrome 30- is not supported.
// For chrome 30-, it leads to crash when opening chrome://settings.
"disable-web-resources",
"safebrowsing-disable-auto-update",
"safebrowsing-disable-download-protection",
"disable-client-side-phishing-detection",
"disable-component-update",
"disable-default-apps",
};
AddSwitches(&command, excludable_switches, arraysize(excludable_switches),
capabilities.exclude_switches);
AddSwitches(&command, kCommonSwitches, arraysize(kCommonSwitches),
capabilities.exclude_switches);
command.AppendSwitch("enable-logging");
command.AppendSwitchASCII("logging-level", "1");
command.AppendSwitchASCII("password-store", "basic");
command.AppendSwitch("use-mock-keychain");
command.AppendSwitchASCII("remote-debugging-port", base::IntToString(port));
if (!command.HasSwitch("user-data-dir")) {
command.AppendArg("about:blank"); command.AppendArg("about:blank");
if (!user_data_dir->CreateUniqueTempDir()) if (!user_data_dir->CreateUniqueTempDir())
return Status(kUnknownError, "cannot create temp dir for user data dir"); return Status(kUnknownError, "cannot create temp dir for user data dir");
command.AppendSwitchPath("user-data-dir", user_data_dir->path()); switches.SetSwitch("user-data-dir", user_data_dir->path().value());
Status status = internal::PrepareUserDataDir( Status status = internal::PrepareUserDataDir(
user_data_dir->path(), capabilities.prefs.get(), user_data_dir->path(), capabilities.prefs.get(),
capabilities.local_state.get()); capabilities.local_state.get());
...@@ -142,11 +134,11 @@ Status PrepareCommandLine(int port, ...@@ -142,11 +134,11 @@ Status PrepareCommandLine(int port,
Status status = internal::ProcessExtensions(capabilities.extensions, Status status = internal::ProcessExtensions(capabilities.extensions,
extension_dir->path(), extension_dir->path(),
true, true,
&command, &switches,
extension_bg_pages); extension_bg_pages);
if (status.IsError()) if (status.IsError())
return status; return status;
switches.AppendToCommandLine(&command);
*prepared_command = command; *prepared_command = command;
return Status(kOk); return Status(kOk);
} }
...@@ -190,11 +182,11 @@ Status LaunchExistingChromeSession( ...@@ -190,11 +182,11 @@ Status LaunchExistingChromeSession(
Status status(kOk); Status status(kOk);
scoped_ptr<DevToolsHttpClient> devtools_client; scoped_ptr<DevToolsHttpClient> devtools_client;
status = WaitForDevToolsAndCheckVersion( status = WaitForDevToolsAndCheckVersion(
capabilities.use_existing_browser, context_getter, socket_factory, log, capabilities.debugger_address, context_getter, socket_factory, log,
&devtools_client); &devtools_client);
if (status.IsError()) { if (status.IsError()) {
return Status(kUnknownError, "cannot connect to chrome at " + return Status(kUnknownError, "cannot connect to chrome at " +
capabilities.use_existing_browser.ToString(), capabilities.debugger_address.ToString(),
status); status);
} }
chrome->reset(new ChromeExistingImpl(devtools_client.Pass(), chrome->reset(new ChromeExistingImpl(devtools_client.Pass(),
...@@ -323,15 +315,15 @@ Status LaunchAndroidChrome( ...@@ -323,15 +315,15 @@ Status LaunchAndroidChrome(
if (!status.IsOk()) if (!status.IsOk())
return status; return status;
std::string args(capabilities.android_args); Switches switches(capabilities.switches);
for (size_t i = 0; i < arraysize(kCommonSwitches); i++) for (size_t i = 0; i < arraysize(kCommonSwitches); ++i)
args += "--" + std::string(kCommonSwitches[i]) + " "; switches.SetSwitch(kCommonSwitches[i]);
args += "--disable-fre --enable-remote-debugging"; switches.SetSwitch("disable-fre");
switches.SetSwitch("enable-remote-debugging");
status = device->StartApp(capabilities.android_package, status = device->StartApp(capabilities.android_package,
capabilities.android_activity, capabilities.android_activity,
capabilities.android_process, capabilities.android_process,
args, port); switches.ToString(), port);
if (!status.IsOk()) { if (!status.IsOk()) {
device->StopApp(); device->StopApp();
return status; return status;
...@@ -495,10 +487,20 @@ Status ProcessExtension(const std::string& extension, ...@@ -495,10 +487,20 @@ Status ProcessExtension(const std::string& extension,
return Status(kOk); return Status(kOk);
} }
void UpdateExtensionSwitch(Switches* switches,
const char name[],
const base::FilePath::StringType& extension) {
base::FilePath::StringType value = switches->GetSwitchValueNative(name);
if (value.length())
value += FILE_PATH_LITERAL(",");
value += extension;
switches->SetSwitch(name, value);
}
Status ProcessExtensions(const std::vector<std::string>& extensions, Status ProcessExtensions(const std::vector<std::string>& extensions,
const base::FilePath& temp_dir, const base::FilePath& temp_dir,
bool include_automation_extension, bool include_automation_extension,
CommandLine* command, Switches* switches,
std::vector<std::string>* bg_pages) { std::vector<std::string>* bg_pages) {
std::vector<std::string> bg_pages_tmp; std::vector<std::string> bg_pages_tmp;
std::vector<base::FilePath::StringType> extension_paths; std::vector<base::FilePath::StringType> extension_paths;
...@@ -522,9 +524,9 @@ Status ProcessExtensions(const std::vector<std::string>& extensions, ...@@ -522,9 +524,9 @@ Status ProcessExtensions(const std::vector<std::string>& extensions,
Status status = UnpackAutomationExtension(temp_dir, &automation_extension); Status status = UnpackAutomationExtension(temp_dir, &automation_extension);
if (status.IsError()) if (status.IsError())
return status; return status;
if (command->HasSwitch("disable-extensions")) { if (switches->HasSwitch("disable-extensions")) {
command->AppendSwitchNative("load-component-extension", UpdateExtensionSwitch(switches, "load-component-extension",
automation_extension.value()); automation_extension.value());
} else { } else {
extension_paths.push_back(automation_extension.value()); extension_paths.push_back(automation_extension.value());
} }
...@@ -533,7 +535,7 @@ Status ProcessExtensions(const std::vector<std::string>& extensions, ...@@ -533,7 +535,7 @@ Status ProcessExtensions(const std::vector<std::string>& extensions,
if (extension_paths.size()) { if (extension_paths.size()) {
base::FilePath::StringType extension_paths_value = JoinString( base::FilePath::StringType extension_paths_value = JoinString(
extension_paths, FILE_PATH_LITERAL(',')); extension_paths, FILE_PATH_LITERAL(','));
command->AppendSwitchNative("load-extension", extension_paths_value); UpdateExtensionSwitch(switches, "load-extension", extension_paths_value);
} }
bg_pages->swap(bg_pages_tmp); bg_pages->swap(bg_pages_tmp);
return Status(kOk); return Status(kOk);
......
...@@ -41,7 +41,7 @@ namespace internal { ...@@ -41,7 +41,7 @@ namespace internal {
Status ProcessExtensions(const std::vector<std::string>& extensions, Status ProcessExtensions(const std::vector<std::string>& extensions,
const base::FilePath& temp_dir, const base::FilePath& temp_dir,
bool include_automation_extension, bool include_automation_extension,
CommandLine* command, Switches* switches,
std::vector<std::string>* bg_pages); std::vector<std::string>* bg_pages);
Status PrepareUserDataDir( Status PrepareUserDataDir(
const base::FilePath& user_data_dir, const base::FilePath& user_data_dir,
......
...@@ -18,14 +18,14 @@ ...@@ -18,14 +18,14 @@
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
TEST(ProcessExtensions, NoExtension) { TEST(ProcessExtensions, NoExtension) {
CommandLine command(CommandLine::NO_PROGRAM); Switches switches;
std::vector<std::string> extensions; std::vector<std::string> extensions;
base::FilePath extension_dir; base::FilePath extension_dir;
std::vector<std::string> bg_pages; std::vector<std::string> bg_pages;
Status status = internal::ProcessExtensions(extensions, extension_dir, Status status = internal::ProcessExtensions(extensions, extension_dir,
false, &command, &bg_pages); false, &switches, &bg_pages);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_FALSE(command.HasSwitch("load-extension")); ASSERT_FALSE(switches.HasSwitch("load-extension"));
ASSERT_EQ(0u, bg_pages.size()); ASSERT_EQ(0u, bg_pages.size());
} }
...@@ -53,13 +53,13 @@ TEST(ProcessExtensions, SingleExtensionWithBgPage) { ...@@ -53,13 +53,13 @@ TEST(ProcessExtensions, SingleExtensionWithBgPage) {
base::ScopedTempDir extension_dir; base::ScopedTempDir extension_dir;
ASSERT_TRUE(extension_dir.CreateUniqueTempDir()); ASSERT_TRUE(extension_dir.CreateUniqueTempDir());
CommandLine command(CommandLine::NO_PROGRAM); Switches switches;
std::vector<std::string> bg_pages; std::vector<std::string> bg_pages;
Status status = internal::ProcessExtensions(extensions, extension_dir.path(), Status status = internal::ProcessExtensions(extensions, extension_dir.path(),
false, &command, &bg_pages); false, &switches, &bg_pages);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(command.HasSwitch("load-extension")); ASSERT_TRUE(switches.HasSwitch("load-extension"));
base::FilePath temp_ext_path = command.GetSwitchValuePath("load-extension"); base::FilePath temp_ext_path(switches.GetSwitchValueNative("load-extension"));
ASSERT_TRUE(base::PathExists(temp_ext_path)); ASSERT_TRUE(base::PathExists(temp_ext_path));
std::string manifest_txt; std::string manifest_txt;
ASSERT_TRUE(file_util::ReadFileToString( ASSERT_TRUE(file_util::ReadFileToString(
...@@ -91,13 +91,13 @@ TEST(ProcessExtensions, MultipleExtensionsNoBgPages) { ...@@ -91,13 +91,13 @@ TEST(ProcessExtensions, MultipleExtensionsNoBgPages) {
base::ScopedTempDir extension_dir; base::ScopedTempDir extension_dir;
ASSERT_TRUE(extension_dir.CreateUniqueTempDir()); ASSERT_TRUE(extension_dir.CreateUniqueTempDir());
CommandLine command(CommandLine::NO_PROGRAM); Switches switches;
std::vector<std::string> bg_pages; std::vector<std::string> bg_pages;
Status status = internal::ProcessExtensions(extensions, extension_dir.path(), Status status = internal::ProcessExtensions(extensions, extension_dir.path(),
false, &command, &bg_pages); false, &switches, &bg_pages);
ASSERT_TRUE(status.IsOk()); ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(command.HasSwitch("load-extension")); ASSERT_TRUE(switches.HasSwitch("load-extension"));
CommandLine::StringType ext_paths = command.GetSwitchValueNative( CommandLine::StringType ext_paths = switches.GetSwitchValueNative(
"load-extension"); "load-extension");
std::vector<CommandLine::StringType> ext_path_list; std::vector<CommandLine::StringType> ext_path_list;
base::SplitString(ext_paths, FILE_PATH_LITERAL(','), &ext_path_list); base::SplitString(ext_paths, FILE_PATH_LITERAL(','), &ext_path_list);
...@@ -107,6 +107,24 @@ TEST(ProcessExtensions, MultipleExtensionsNoBgPages) { ...@@ -107,6 +107,24 @@ TEST(ProcessExtensions, MultipleExtensionsNoBgPages) {
ASSERT_EQ(0u, bg_pages.size()); ASSERT_EQ(0u, bg_pages.size());
} }
TEST(ProcessExtensions, CommandLineExtensions) {
std::vector<std::string> extensions;
ASSERT_TRUE(AddExtensionForInstall("ext_test_1.crx", &extensions));
base::ScopedTempDir extension_dir;
ASSERT_TRUE(extension_dir.CreateUniqueTempDir());
Switches switches;
switches.SetSwitch("load-extension", "/a");
std::vector<std::string> bg_pages;
Status status = internal::ProcessExtensions(extensions, extension_dir.path(),
false, &switches, &bg_pages);
ASSERT_EQ(kOk, status.code());
base::FilePath::StringType load = switches.GetSwitchValueNative(
"load-extension");
ASSERT_EQ(FILE_PATH_LITERAL("/a,"), load.substr(0, 3));
ASSERT_TRUE(base::PathExists(base::FilePath(load.substr(3))));
}
namespace { namespace {
void AssertEQ(const base::DictionaryValue& dict, const std::string& key, void AssertEQ(const base::DictionaryValue& dict, const std::string& key,
...@@ -122,7 +140,6 @@ TEST(PrepareUserDataDir, CustomPrefs) { ...@@ -122,7 +140,6 @@ TEST(PrepareUserDataDir, CustomPrefs) {
base::ScopedTempDir temp_dir; base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
CommandLine command(CommandLine::NO_PROGRAM);
base::DictionaryValue prefs; base::DictionaryValue prefs;
prefs.SetString("myPrefsKey", "ok"); prefs.SetString("myPrefsKey", "ok");
prefs.SetStringWithoutPathExpansion("pref.sub", "1"); prefs.SetStringWithoutPathExpansion("pref.sub", "1");
......
...@@ -62,7 +62,7 @@ class ChromeDriver(object): ...@@ -62,7 +62,7 @@ class ChromeDriver(object):
def __init__(self, server_url, chrome_binary=None, android_package=None, def __init__(self, server_url, chrome_binary=None, android_package=None,
chrome_switches=None, chrome_extensions=None, chrome_switches=None, chrome_extensions=None,
chrome_log_path=None, chrome_existing_browser=None): chrome_log_path=None, debugger_address=None):
self._executor = command_executor.CommandExecutor(server_url) self._executor = command_executor.CommandExecutor(server_url)
options = {} options = {}
...@@ -83,9 +83,9 @@ class ChromeDriver(object): ...@@ -83,9 +83,9 @@ class ChromeDriver(object):
assert type(chrome_log_path) is str assert type(chrome_log_path) is str
options['logPath'] = chrome_log_path options['logPath'] = chrome_log_path
if chrome_existing_browser: if debugger_address:
assert type(chrome_existing_browser) is str assert type(debugger_address) is str
options['useExistingBrowser'] = chrome_existing_browser options['debuggerAddress'] = debugger_address
params = { params = {
'desiredCapabilities': { 'desiredCapabilities': {
......
...@@ -105,8 +105,11 @@ def _Run(java_tests_src_dir, test_filter, ...@@ -105,8 +105,11 @@ def _Run(java_tests_src_dir, test_filter,
jvm_args = [] jvm_args = []
if debug: if debug:
jvm_args += ['-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,' transport = 'dt_socket'
'address=33081'] if util.IsWindows():
transport = 'dt_shmem'
jvm_args += ['-agentlib:jdwp=transport=%s,server=y,suspend=y,'
'address=33081' % transport]
# Unpack the sources into the test directory and add to the class path # Unpack the sources into the test directory and add to the class path
# for ease of debugging, particularly with jdb. # for ease of debugging, particularly with jdb.
util.Unzip(os.path.join(java_tests_src_dir, 'test-nodeps-srcs.jar'), util.Unzip(os.path.join(java_tests_src_dir, 'test-nodeps-srcs.jar'),
......
...@@ -707,8 +707,7 @@ class ExistingBrowserTest(ChromeDriverBaseTest): ...@@ -707,8 +707,7 @@ class ExistingBrowserTest(ChromeDriverBaseTest):
if process is None: if process is None:
raise RuntimeError('Chrome could not be started with debugging port') raise RuntimeError('Chrome could not be started with debugging port')
try: try:
hostAndPort = '127.0.0.1:%d' % port driver = self.CreateDriver(debugger_address='127.0.0.1:%d' % port)
driver = self.CreateDriver(chrome_existing_browser=hostAndPort)
driver.ExecuteScript('console.info("%s")' % 'connecting at %d!' % port) driver.ExecuteScript('console.info("%s")' % 'connecting at %d!' % port)
driver.Quit() driver.Quit()
finally: finally:
......
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