Commit 7f01dde7 authored by kkania@chromium.org's avatar kkania@chromium.org

[chromedriver] Implement connecting to devtools and loading a page.

BUG=none


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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@171035 0039d316-1c4b-4281-b951-d872f2087c98
parent 8a48f3f9
...@@ -685,6 +685,10 @@ ...@@ -685,6 +685,10 @@
'test/chromedriver/command_executor_impl.h', 'test/chromedriver/command_executor_impl.h',
'test/chromedriver/commands.cc', 'test/chromedriver/commands.cc',
'test/chromedriver/commands.h', 'test/chromedriver/commands.h',
'test/chromedriver/devtools_client.cc',
'test/chromedriver/devtools_client.h',
'test/chromedriver/net/net_util.cc',
'test/chromedriver/net/net_util.h',
'test/chromedriver/net/sync_websocket.cc', 'test/chromedriver/net/sync_websocket.cc',
'test/chromedriver/net/sync_websocket.h', 'test/chromedriver/net/sync_websocket.h',
'test/chromedriver/net/url_request_context_getter.cc', 'test/chromedriver/net/url_request_context_getter.cc',
...@@ -715,6 +719,7 @@ ...@@ -715,6 +719,7 @@
], ],
'sources': [ 'sources': [
'test/chromedriver/chrome_finder_unittest.cc', 'test/chromedriver/chrome_finder_unittest.cc',
'test/chromedriver/chrome_impl_unittest.cc',
'test/chromedriver/chromedriver_unittest.cc', 'test/chromedriver/chromedriver_unittest.cc',
'test/chromedriver/command_executor_impl_unittest.cc', 'test/chromedriver/command_executor_impl_unittest.cc',
'test/chromedriver/commands_unittest.cc', 'test/chromedriver/commands_unittest.cc',
...@@ -745,6 +750,7 @@ ...@@ -745,6 +750,7 @@
'..,' '..,'
], ],
'sources': [ 'sources': [
'test/chromedriver/net/net_util_unittest.cc',
'test/chromedriver/net/sync_websocket_unittest.cc', 'test/chromedriver/net/sync_websocket_unittest.cc',
'test/chromedriver/net/websocket_unittest.cc', 'test/chromedriver/net/websocket_unittest.cc',
], ],
......
...@@ -5,11 +5,15 @@ ...@@ -5,11 +5,15 @@
#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_H_ #ifndef CHROME_TEST_CHROMEDRIVER_CHROME_H_
#define CHROME_TEST_CHROMEDRIVER_CHROME_H_ #define CHROME_TEST_CHROMEDRIVER_CHROME_H_
#include <string>
class Status; class Status;
class Chrome { class Chrome {
public: public:
virtual ~Chrome() {} virtual ~Chrome() {}
virtual Status Load(const std::string& url) = 0;
virtual Status Quit() = 0; virtual Status Quit() = 0;
}; };
......
...@@ -4,13 +4,42 @@ ...@@ -4,13 +4,42 @@
#include "chrome/test/chromedriver/chrome_impl.h" #include "chrome/test/chromedriver/chrome_impl.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/process_util.h" #include "base/process_util.h"
#include "base/stringprintf.h"
#include "base/threading/platform_thread.h"
#include "base/time.h"
#include "base/values.h"
#include "chrome/test/chromedriver/devtools_client.h"
#include "chrome/test/chromedriver/net/net_util.h"
#include "chrome/test/chromedriver/net/url_request_context_getter.h"
#include "chrome/test/chromedriver/status.h" #include "chrome/test/chromedriver/status.h"
#include "googleurl/src/gurl.h"
namespace {
Status FetchPagesInfo(URLRequestContextGetter* context_getter,
int port,
std::list<std::string>* debugger_urls) {
std::string url = base::StringPrintf(
"http://127.0.0.1:%d/json", port);
std::string data;
if (!FetchUrl(GURL(url), context_getter, &data))
return Status(kChromeNotReachable);
return internal::ParsePagesInfo(data, debugger_urls);
}
} // namespace
ChromeImpl::ChromeImpl(base::ProcessHandle process, ChromeImpl::ChromeImpl(base::ProcessHandle process,
base::ScopedTempDir* user_data_dir) URLRequestContextGetter* context_getter,
: process_(process) { base::ScopedTempDir* user_data_dir,
int port)
: process_(process),
context_getter_(context_getter),
port_(port) {
if (user_data_dir->IsValid()) { if (user_data_dir->IsValid()) {
CHECK(user_data_dir_.Set(user_data_dir->Take())); CHECK(user_data_dir_.Set(user_data_dir->Take()));
} }
...@@ -20,8 +49,57 @@ ChromeImpl::~ChromeImpl() { ...@@ -20,8 +49,57 @@ ChromeImpl::~ChromeImpl() {
base::CloseProcessHandle(process_); base::CloseProcessHandle(process_);
} }
Status ChromeImpl::Init() {
base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
std::list<std::string> debugger_urls;
while (base::Time::Now() < deadline) {
FetchPagesInfo(context_getter_, port_, &debugger_urls);
if (debugger_urls.empty())
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
else
break;
}
if (debugger_urls.empty())
return Status(kUnknownError, "unable to discover open pages");
client_.reset(new DevToolsClient(context_getter_, debugger_urls.front()));
return Status(kOk);
}
Status ChromeImpl::Load(const std::string& url) {
base::DictionaryValue params;
params.SetString("url", url);
return client_->SendCommand("Page.navigate", params);
}
Status ChromeImpl::Quit() { Status ChromeImpl::Quit() {
if (!base::KillProcess(process_, 0, true)) if (!base::KillProcess(process_, 0, true))
return Status(kUnknownError, "cannot kill Chrome"); return Status(kUnknownError, "cannot kill Chrome");
return Status(kOk); return Status(kOk);
} }
namespace internal {
Status ParsePagesInfo(const std::string& data,
std::list<std::string>* debugger_urls) {
scoped_ptr<base::Value> value(base::JSONReader::Read(data));
if (!value.get())
return Status(kUnknownError, "DevTools returned invalid JSON");
base::ListValue* list;
if (!value->GetAsList(&list))
return Status(kUnknownError, "DevTools did not return list");
std::list<std::string> internal_urls;
for (size_t i = 0; i < list->GetSize(); ++i) {
base::DictionaryValue* info;
if (!list->GetDictionary(i, &info))
return Status(kUnknownError, "DevTools contains non-dictionary item");
std::string debugger_url;
if (!info->GetString("webSocketDebuggerUrl", &debugger_url))
return Status(kUnknownError, "DevTools did not include debugger URL");
internal_urls.push_back(debugger_url);
}
debugger_urls->swap(internal_urls);
return Status(kOk);
}
} // namespace internal
...@@ -5,24 +5,47 @@ ...@@ -5,24 +5,47 @@
#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_IMPL_H_ #ifndef CHROME_TEST_CHROMEDRIVER_CHROME_IMPL_H_
#define CHROME_TEST_CHROMEDRIVER_CHROME_IMPL_H_ #define CHROME_TEST_CHROMEDRIVER_CHROME_IMPL_H_
#include <list>
#include <string>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/process.h" #include "base/process.h"
#include "chrome/test/chromedriver/chrome.h" #include "chrome/test/chromedriver/chrome.h"
class DevToolsClient;
class Status; class Status;
class URLRequestContextGetter;
class ChromeImpl : public Chrome { class ChromeImpl : public Chrome {
public: public:
ChromeImpl(base::ProcessHandle process, base::ScopedTempDir* user_data_dir); ChromeImpl(base::ProcessHandle process,
URLRequestContextGetter* context_getter,
base::ScopedTempDir* user_data_dir,
int port);
virtual ~ChromeImpl(); virtual ~ChromeImpl();
Status Init();
// Overridden from Chrome: // Overridden from Chrome:
virtual Status Load(const std::string& url) OVERRIDE;
virtual Status Quit() OVERRIDE; virtual Status Quit() OVERRIDE;
private: private:
base::ProcessHandle process_; base::ProcessHandle process_;
scoped_refptr<URLRequestContextGetter> context_getter_;
base::ScopedTempDir user_data_dir_; base::ScopedTempDir user_data_dir_;
int port_;
scoped_ptr<DevToolsClient> client_;
}; };
namespace internal {
Status ParsePagesInfo(const std::string& data,
std::list<std::string>* debugger_urls);
} // namespace internal
#endif // CHROME_TEST_CHROMEDRIVER_CHROME_IMPL_H_ #endif // CHROME_TEST_CHROMEDRIVER_CHROME_IMPL_H_
// Copyright (c) 2012 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 <list>
#include <string>
#include "chrome/test/chromedriver/chrome_impl.h"
#include "chrome/test/chromedriver/status.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(ParsePagesInfo, Normal) {
std::list<std::string> urls;
Status status = internal::ParsePagesInfo(
"[{\"webSocketDebuggerUrl\": \"http://debugurl\"}]",
&urls);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, urls.size());
ASSERT_EQ("http://debugurl", urls.front());
}
TEST(ParsePagesInfo, Multiple) {
std::list<std::string> urls;
Status status = internal::ParsePagesInfo(
"[{\"webSocketDebuggerUrl\": \"http://debugurl\"},"
" {\"webSocketDebuggerUrl\": \"http://debugurl2\"}]",
&urls);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(2u, urls.size());
ASSERT_EQ("http://debugurl", urls.front());
ASSERT_EQ("http://debugurl2", urls.back());
}
namespace {
void AssertFails(const std::string& data) {
std::list<std::string> urls;
Status status = internal::ParsePagesInfo(data, &urls);
ASSERT_FALSE(status.IsOk());
ASSERT_EQ(0u, urls.size());
}
} // namespace
TEST(ParsePagesInfo, InvalidJSON) {
AssertFails("[");
}
TEST(ParsePagesInfo, NonList) {
AssertFails("{}");
}
TEST(ParsePagesInfo, NonDictionary) {
AssertFails("[1]");
}
TEST(ParsePagesInfo, NoDebuggerUrl) {
AssertFails("[{\"hi\": 1}]");
}
TEST(ParsePagesInfo, InvalidDebuggerUrl) {
AssertFails("[{\"webSocketDebuggerUrl\": 1}]");
}
...@@ -10,12 +10,15 @@ ...@@ -10,12 +10,15 @@
#include "base/file_path.h" #include "base/file_path.h"
#include "base/process.h" #include "base/process.h"
#include "base/process_util.h" #include "base/process_util.h"
#include "base/string_number_conversions.h"
#include "chrome/test/chromedriver/chrome.h" #include "chrome/test/chromedriver/chrome.h"
#include "chrome/test/chromedriver/chrome_finder.h" #include "chrome/test/chromedriver/chrome_finder.h"
#include "chrome/test/chromedriver/chrome_impl.h" #include "chrome/test/chromedriver/chrome_impl.h"
#include "chrome/test/chromedriver/net/url_request_context_getter.h"
#include "chrome/test/chromedriver/status.h" #include "chrome/test/chromedriver/status.h"
ChromeLauncherImpl::ChromeLauncherImpl() {} ChromeLauncherImpl::ChromeLauncherImpl(URLRequestContextGetter* context_getter)
: context_getter_(context_getter) {}
ChromeLauncherImpl::~ChromeLauncherImpl() {} ChromeLauncherImpl::~ChromeLauncherImpl() {}
...@@ -28,7 +31,10 @@ Status ChromeLauncherImpl::Launch( ...@@ -28,7 +31,10 @@ Status ChromeLauncherImpl::Launch(
return Status(kUnknownError, "cannot find Chrome binary"); return Status(kUnknownError, "cannot find Chrome binary");
} }
int port = 33081;
CommandLine command(program); CommandLine command(program);
command.AppendSwitchASCII("remote-debugging-port", base::IntToString(port));
command.AppendSwitch("no-first-run");
command.AppendSwitch("enable-logging"); command.AppendSwitch("enable-logging");
command.AppendSwitchASCII("logging-level", "1"); command.AppendSwitchASCII("logging-level", "1");
base::ScopedTempDir user_data_dir; base::ScopedTempDir user_data_dir;
...@@ -41,7 +47,11 @@ Status ChromeLauncherImpl::Launch( ...@@ -41,7 +47,11 @@ Status ChromeLauncherImpl::Launch(
base::ProcessHandle process; base::ProcessHandle process;
if (!base::LaunchProcess(command, options, &process)) if (!base::LaunchProcess(command, options, &process))
return Status(kUnknownError, "chrome failed to start"); return Status(kUnknownError, "chrome failed to start");
chrome->reset(new ChromeImpl(process, &user_data_dir)); scoped_ptr<ChromeImpl> chrome_impl(new ChromeImpl(
process, context_getter_, &user_data_dir, port));
Status status = chrome_impl->Init();
if (status.IsError())
return status;
chrome->reset(chrome_impl.release());
return Status(kOk); return Status(kOk);
} }
...@@ -7,16 +7,18 @@ ...@@ -7,16 +7,18 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "chrome/test/chromedriver/chrome_launcher.h" #include "chrome/test/chromedriver/chrome_launcher.h"
class Chrome; class Chrome;
class FilePath; class FilePath;
class Status; class Status;
class URLRequestContextGetter;
class ChromeLauncherImpl : public ChromeLauncher { class ChromeLauncherImpl : public ChromeLauncher {
public: public:
ChromeLauncherImpl(); explicit ChromeLauncherImpl(URLRequestContextGetter* context_getter);
virtual ~ChromeLauncherImpl(); virtual ~ChromeLauncherImpl();
// Overridden from ChromeLauncher: // Overridden from ChromeLauncher:
...@@ -24,6 +26,8 @@ class ChromeLauncherImpl : public ChromeLauncher { ...@@ -24,6 +26,8 @@ class ChromeLauncherImpl : public ChromeLauncher {
scoped_ptr<Chrome>* chrome) OVERRIDE; scoped_ptr<Chrome>* chrome) OVERRIDE;
private: private:
scoped_refptr<URLRequestContextGetter> context_getter_;
DISALLOW_COPY_AND_ASSIGN(ChromeLauncherImpl); DISALLOW_COPY_AND_ASSIGN(ChromeLauncherImpl);
}; };
......
...@@ -6,14 +6,19 @@ ...@@ -6,14 +6,19 @@
#include "base/json/json_reader.h" #include "base/json/json_reader.h"
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/test/chromedriver/command_executor.h" #include "chrome/test/chromedriver/command_executor.h"
#include "chrome/test/chromedriver/status.h" #include "chrome/test/chromedriver/status.h"
namespace { namespace {
// Guards |g_executor_initialized|.
base::LazyInstance<base::Lock> g_lazy_lock = LAZY_INSTANCE_INITIALIZER;
bool g_executor_initialized = false;
CommandExecutor* g_command_executor = NULL; CommandExecutor* g_command_executor = NULL;
void SetResponse(StatusCode status, void SetResponse(StatusCode status,
...@@ -39,10 +44,19 @@ void SetError(const std::string& error_msg, ...@@ -39,10 +44,19 @@ void SetError(const std::string& error_msg,
void Init(scoped_ptr<CommandExecutor> executor) { void Init(scoped_ptr<CommandExecutor> executor) {
g_command_executor = executor.release(); g_command_executor = executor.release();
// We do not call CommandExecutor::Init here because you can't do some things
// (e.g., creating threads) during DLL loading on Windows.
} }
void ExecuteCommand(const std::string& command, std::string* response) { void ExecuteCommand(const std::string& command, std::string* response) {
CHECK(g_command_executor); CHECK(g_command_executor);
{
base::AutoLock(g_lazy_lock.Get());
if (!g_executor_initialized) {
g_command_executor->Init();
g_executor_initialized = true;
}
}
std::string error_msg; std::string error_msg;
scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError( scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
command, 0, NULL, &error_msg)); command, 0, NULL, &error_msg));
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
class CommandExecutor; class CommandExecutor;
// Inits the command executor. Must be called before |ExecuteCommand|. // Inits the command executor. Must be called before |ExecuteCommand|.
// This may be called during DLL load on Windows.
void Init(scoped_ptr<CommandExecutor> executor); void Init(scoped_ptr<CommandExecutor> executor);
// Synchronously executes the given command. Thread safe. // Synchronously executes the given command. Thread safe.
......
...@@ -68,6 +68,9 @@ class ChromeDriver(object): ...@@ -68,6 +68,9 @@ class ChromeDriver(object):
def _ExecuteSessionCommand(self, name, params={}): def _ExecuteSessionCommand(self, name, params={}):
return self._ExecuteCommand(name, params, self._session_id) return self._ExecuteCommand(name, params, self._session_id)
def Load(self, url):
self._ExecuteSessionCommand('get', {'url': url})
def Quit(self): def Quit(self):
"""Quits the browser and ends the session.""" """Quits the browser and ends the session."""
self._ExecuteSessionCommand('quit') self._ExecuteSessionCommand('quit')
...@@ -38,13 +38,13 @@ class DummyExecutor : public CommandExecutor { ...@@ -38,13 +38,13 @@ class DummyExecutor : public CommandExecutor {
public: public:
virtual ~DummyExecutor() {} virtual ~DummyExecutor() {}
virtual void Init() OVERRIDE {}
virtual void ExecuteCommand(const std::string& name, virtual void ExecuteCommand(const std::string& name,
const base::DictionaryValue& params, const base::DictionaryValue& params,
const std::string& session_id, const std::string& session_id,
StatusCode* status, StatusCode* status,
scoped_ptr<base::Value>* value, scoped_ptr<base::Value>* value,
std::string* out_session_id) OVERRIDE { std::string* out_session_id) OVERRIDE {}
}
}; };
...@@ -80,6 +80,8 @@ class ExecutorMock : public CommandExecutor { ...@@ -80,6 +80,8 @@ class ExecutorMock : public CommandExecutor {
EXPECT_TRUE(DidSatisfyExpectations()); EXPECT_TRUE(DidSatisfyExpectations());
} }
virtual void Init() OVERRIDE {}
virtual void ExecuteCommand(const std::string& name, virtual void ExecuteCommand(const std::string& name,
const base::DictionaryValue& params, const base::DictionaryValue& params,
const std::string& session_id, const std::string& session_id,
......
...@@ -22,6 +22,8 @@ class CommandExecutor { ...@@ -22,6 +22,8 @@ class CommandExecutor {
public: public:
virtual ~CommandExecutor() {} virtual ~CommandExecutor() {}
virtual void Init() = 0;
// Executes a command synchronously. This function must be thread safe. // Executes a command synchronously. This function must be thread safe.
virtual void ExecuteCommand(const std::string& name, virtual void ExecuteCommand(const std::string& name,
const base::DictionaryValue& params, const base::DictionaryValue& params,
......
...@@ -6,14 +6,30 @@ ...@@ -6,14 +6,30 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/test/chromedriver/chrome_launcher_impl.h"
#include "chrome/test/chromedriver/commands.h" #include "chrome/test/chromedriver/commands.h"
#include "chrome/test/chromedriver/net/url_request_context_getter.h"
#include "chrome/test/chromedriver/session.h" #include "chrome/test/chromedriver/session.h"
#include "chrome/test/chromedriver/session_command.h" #include "chrome/test/chromedriver/session_command.h"
#include "chrome/test/chromedriver/session_map.h" #include "chrome/test/chromedriver/session_map.h"
#include "chrome/test/chromedriver/status.h" #include "chrome/test/chromedriver/status.h"
CommandExecutorImpl::CommandExecutorImpl() { CommandExecutorImpl::CommandExecutorImpl()
: io_thread_("ChromeDriver IO") {}
CommandExecutorImpl::~CommandExecutorImpl() {}
void CommandExecutorImpl::Init() {
base::Thread::Options options(MessageLoop::TYPE_IO, 0);
CHECK(io_thread_.StartWithOptions(options));
context_getter_ = new URLRequestContextGetter(
io_thread_.message_loop_proxy());
launcher_.reset(new ChromeLauncherImpl(context_getter_));
// Session commands.
base::Callback<Status( base::Callback<Status(
const SessionCommand&, const SessionCommand&,
const base::DictionaryValue&, const base::DictionaryValue&,
...@@ -22,19 +38,19 @@ CommandExecutorImpl::CommandExecutorImpl() { ...@@ -22,19 +38,19 @@ CommandExecutorImpl::CommandExecutorImpl() {
std::string*)> execute_session_command = base::Bind( std::string*)> execute_session_command = base::Bind(
&ExecuteSessionCommand, &ExecuteSessionCommand,
&session_map_); &session_map_);
command_map_.Set("get", base::Bind(execute_session_command,
base::Bind(&ExecuteGet)));
Command quit_command = base::Bind(execute_session_command, Command quit_command = base::Bind(execute_session_command,
base::Bind(&ExecuteQuit, &session_map_)); base::Bind(&ExecuteQuit, &session_map_));
command_map_.Set("quit", quit_command); command_map_.Set("quit", quit_command);
// Non-session commands. // Non-session commands.
command_map_.Set("newSession", command_map_.Set("newSession",
base::Bind(&ExecuteNewSession, &session_map_, &launcher_)); base::Bind(&ExecuteNewSession, &session_map_, launcher_.get()));
command_map_.Set("quitAll", command_map_.Set("quitAll",
base::Bind(&ExecuteQuitAll, quit_command, &session_map_)); base::Bind(&ExecuteQuitAll, quit_command, &session_map_));
} }
CommandExecutorImpl::~CommandExecutorImpl() {}
void CommandExecutorImpl::ExecuteCommand( void CommandExecutorImpl::ExecuteCommand(
const std::string& name, const std::string& name,
const base::DictionaryValue& params, const base::DictionaryValue& params,
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/gtest_prod_util.h" #include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "chrome/test/chromedriver/chrome_launcher_impl.h" #include "base/threading/thread.h"
#include "chrome/test/chromedriver/command.h" #include "chrome/test/chromedriver/command.h"
#include "chrome/test/chromedriver/command_executor.h" #include "chrome/test/chromedriver/command_executor.h"
#include "chrome/test/chromedriver/session_map.h" #include "chrome/test/chromedriver/session_map.h"
...@@ -24,12 +24,16 @@ class DictionaryValue; ...@@ -24,12 +24,16 @@ class DictionaryValue;
class Value; class Value;
} }
class ChromeLauncherImpl;
class URLRequestContextGetter;
class CommandExecutorImpl : public CommandExecutor { class CommandExecutorImpl : public CommandExecutor {
public: public:
CommandExecutorImpl(); CommandExecutorImpl();
virtual ~CommandExecutorImpl(); virtual ~CommandExecutorImpl();
// Overridden from CommandExecutor: // Overridden from CommandExecutor:
virtual void Init() OVERRIDE;
virtual void ExecuteCommand(const std::string& name, virtual void ExecuteCommand(const std::string& name,
const base::DictionaryValue& params, const base::DictionaryValue& params,
const std::string& session_id, const std::string& session_id,
...@@ -43,8 +47,10 @@ class CommandExecutorImpl : public CommandExecutor { ...@@ -43,8 +47,10 @@ class CommandExecutorImpl : public CommandExecutor {
CommandExecutorImplTest, CommandThatDoesntSetValueOrSessionId); CommandExecutorImplTest, CommandThatDoesntSetValueOrSessionId);
FRIEND_TEST_ALL_PREFIXES(CommandExecutorImplTest, CommandThatReturnsError); FRIEND_TEST_ALL_PREFIXES(CommandExecutorImplTest, CommandThatReturnsError);
base::Thread io_thread_;
scoped_refptr<URLRequestContextGetter> context_getter_;
SessionMap session_map_; SessionMap session_map_;
ChromeLauncherImpl launcher_; scoped_ptr<ChromeLauncherImpl> launcher_;
SynchronizedMap<std::string, Command> command_map_; SynchronizedMap<std::string, Command> command_map_;
DISALLOW_COPY_AND_ASSIGN(CommandExecutorImpl); DISALLOW_COPY_AND_ASSIGN(CommandExecutorImpl);
......
...@@ -78,3 +78,13 @@ Status ExecuteQuit( ...@@ -78,3 +78,13 @@ Status ExecuteQuit(
CHECK(session_map->Remove(session->id)); CHECK(session_map->Remove(session->id));
return session->chrome->Quit(); return session->chrome->Quit();
} }
Status ExecuteGet(
Session* session,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value) {
std::string url;
if (!params.GetString("url", &url))
return Status(kUnknownError, "'url' must be a string");
return session->chrome->Load(url);
}
...@@ -46,4 +46,10 @@ Status ExecuteQuit( ...@@ -46,4 +46,10 @@ Status ExecuteQuit(
const base::DictionaryValue& params, const base::DictionaryValue& params,
scoped_ptr<base::Value>* value); scoped_ptr<base::Value>* value);
// Loads a URL.
Status ExecuteGet(
Session* session,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value);
#endif // CHROME_TEST_CHROMEDRIVER_COMMANDS_H_ #endif // CHROME_TEST_CHROMEDRIVER_COMMANDS_H_
...@@ -27,6 +27,9 @@ class StubChrome : public Chrome { ...@@ -27,6 +27,9 @@ class StubChrome : public Chrome {
virtual ~StubChrome() {} virtual ~StubChrome() {}
// Overridden from Chrome: // Overridden from Chrome:
virtual Status Load(const std::string& url) OVERRIDE {
return Status(kOk);
}
virtual Status Quit() OVERRIDE { virtual Status Quit() OVERRIDE {
return Status(kOk); return Status(kOk);
} }
...@@ -152,7 +155,7 @@ TEST(CommandsTest, Quit) { ...@@ -152,7 +155,7 @@ TEST(CommandsTest, Quit) {
namespace { namespace {
class FailsToQuitChrome : public Chrome { class FailsToQuitChrome : public StubChrome {
public: public:
FailsToQuitChrome() {} FailsToQuitChrome() {}
virtual ~FailsToQuitChrome() {} virtual ~FailsToQuitChrome() {}
......
// Copyright (c) 2012 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/chromedriver/devtools_client.h"
#include "base/json/json_writer.h"
#include "base/values.h"
#include "chrome/test/chromedriver/net/sync_websocket.h"
#include "chrome/test/chromedriver/net/url_request_context_getter.h"
#include "chrome/test/chromedriver/status.h"
#include "googleurl/src/gurl.h"
DevToolsClient::DevToolsClient(
URLRequestContextGetter* context_getter,
const std::string& url)
: context_getter_(context_getter),
url_(url),
socket_(new SyncWebSocket(context_getter)),
connected_(false) {}
DevToolsClient::~DevToolsClient() {}
Status DevToolsClient::SendCommand(
const std::string& method,
const base::DictionaryValue& params) {
if (!connected_) {
if (!socket_->Connect(GURL(url_)))
return Status(kUnknownError, "unable to connect to renderer");
connected_ = true;
}
base::DictionaryValue command;
command.SetInteger("id", 1);
command.SetString("method", method);
command.Set("params", params.DeepCopy());
std::string message;
base::JSONWriter::Write(&command, &message);
if (!socket_->Send(message))
return Status(kUnknownError, "unable to send message to renderer");
std::string response;
if (!socket_->ReceiveNextMessage(&response))
return Status(kUnknownError, "unable to receive message from renderer");
return Status(kOk);
}
// Copyright (c) 2012 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.
#ifndef CHROME_TEST_CHROMEDRIVER_DEVTOOLS_CLIENT_H_
#define CHROME_TEST_CHROMEDRIVER_DEVTOOLS_CLIENT_H_
#include <string>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
namespace base {
class DictionaryValue;
}
class Status;
class SyncWebSocket;
class URLRequestContextGetter;
// A DevTools client of a single DevTools debugger.
class DevToolsClient {
public:
DevToolsClient(URLRequestContextGetter* context_getter,
const std::string& url);
~DevToolsClient();
Status SendCommand(const std::string& method,
const base::DictionaryValue& params);
private:
scoped_refptr<URLRequestContextGetter> context_getter_;
std::string url_;
scoped_ptr<SyncWebSocket> socket_;
bool connected_;
DISALLOW_COPY_AND_ASSIGN(DevToolsClient);
};
#endif // CHROME_TEST_CHROMEDRIVER_DEVTOOLS_CLIENT_H_
// Copyright (c) 2012 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 "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "chrome/test/chromedriver/net/url_request_context_getter.h"
#include "googleurl/src/gurl.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
namespace {
class SyncUrlFetcher : public net::URLFetcherDelegate {
public:
SyncUrlFetcher() {}
virtual ~SyncUrlFetcher() {}
bool Fetch(const GURL& url,
URLRequestContextGetter* getter,
std::string* response) {
MessageLoop loop;
scoped_ptr<net::URLFetcher> fetcher_(
net::URLFetcher::Create(url, net::URLFetcher::GET, this));
fetcher_->SetRequestContext(getter);
response_ = response;
fetcher_->Start();
loop.Run();
return success_;
}
virtual void OnURLFetchComplete(const net::URLFetcher* source) {
success_ = (source->GetResponseCode() == 200);
if (success_)
success_ = source->GetResponseAsString(response_);
MessageLoop::current()->Quit();
}
private:
bool success_;
std::string* response_;
};
} // namespace
bool FetchUrl(const GURL& url,
URLRequestContextGetter* getter,
std::string* response) {
return SyncUrlFetcher().Fetch(url, getter, response);
}
// Copyright (c) 2012 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.
#ifndef CHROME_TEST_CHROMEDRIVER_NET_NET_UTIL_H_
#define CHROME_TEST_CHROMEDRIVER_NET_NET_UTIL_H_
#include <string>
class GURL;
class URLRequestContextGetter;
// Synchronously fetches data from a GET HTTP request to the given URL.
// Returns true if response is 200 OK and sets response body to |response|.
bool FetchUrl(const GURL& url,
URLRequestContextGetter* getter,
std::string* response);
#endif // CHROME_TEST_CHROMEDRIVER_NET_NET_UTIL_H_
// Copyright (c) 2012 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 <string>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
#include "base/single_thread_task_runner.h"
#include "base/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "chrome/test/chromedriver/net/net_util.h"
#include "chrome/test/chromedriver/net/url_request_context_getter.h"
#include "googleurl/src/gurl.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/tcp_listen_socket.h"
#include "net/server/http_server.h"
#include "net/server/http_server_request_info.h"
#include "net/url_request/url_request_context_getter.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class FetchUrlTest : public testing::Test,
public net::HttpServer::Delegate {
public:
FetchUrlTest()
: io_thread_("io"),
response_(kSendHello) {
base::Thread::Options options(MessageLoop::TYPE_IO, 0);
CHECK(io_thread_.StartWithOptions(options));
context_getter_ = new URLRequestContextGetter(
io_thread_.message_loop_proxy());
base::WaitableEvent event(false, false);
io_thread_.message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&FetchUrlTest::InitOnIO,
base::Unretained(this), &event));
event.Wait();
}
virtual ~FetchUrlTest() {
base::WaitableEvent event(false, false);
io_thread_.message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&FetchUrlTest::DestroyServerOnIO,
base::Unretained(this), &event));
event.Wait();
}
void InitOnIO(base::WaitableEvent* event) {
net::TCPListenSocketFactory factory("127.0.0.1", 0);
server_ = new net::HttpServer(factory, this);
net::IPEndPoint address;
CHECK_EQ(net::OK, server_->GetLocalAddress(&address));
server_url_ = GURL(
base::StringPrintf("http://127.0.0.1:%d", address.port()));
event->Signal();
}
void DestroyServerOnIO(base::WaitableEvent* event) {
server_ = NULL;
event->Signal();
}
// Overridden from net::HttpServer::Delegate:
virtual void OnHttpRequest(int connection_id,
const net::HttpServerRequestInfo& info) OVERRIDE {
switch (response_) {
case kSendHello:
server_->Send200(connection_id, "hello", "text/plain");
break;
case kSend404:
server_->Send404(connection_id);
break;
case kClose:
// net::HttpServer doesn't allow us to close connection during callback.
MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&net::HttpServer::Close, server_, connection_id));
break;
default:
break;
}
}
virtual void OnWebSocketRequest(
int connection_id,
const net::HttpServerRequestInfo& info) OVERRIDE {}
virtual void OnWebSocketMessage(int connection_id,
const std::string& data) OVERRIDE {}
virtual void OnClose(int connection_id) OVERRIDE {}
protected:
enum ServerResponse {
kSendHello = 0,
kSend404,
kClose,
};
base::Thread io_thread_;
ServerResponse response_;
scoped_refptr<net::HttpServer> server_;
scoped_refptr<URLRequestContextGetter> context_getter_;
GURL server_url_;
};
} // namespace
TEST_F(FetchUrlTest, Http200) {
std::string response("stuff");
ASSERT_TRUE(FetchUrl(server_url_, context_getter_, &response));
ASSERT_STREQ("hello", response.c_str());
}
TEST_F(FetchUrlTest, HttpNon200) {
response_ = kSend404;
std::string response("stuff");
ASSERT_FALSE(FetchUrl(server_url_, context_getter_, &response));
ASSERT_STREQ("stuff", response.c_str());
}
TEST_F(FetchUrlTest, ConnectionClose) {
response_ = kClose;
std::string response("stuff");
ASSERT_FALSE(FetchUrl(server_url_, context_getter_, &response));
ASSERT_STREQ("stuff", response.c_str());
}
TEST_F(FetchUrlTest, NoServer) {
std::string response("stuff");
GURL bogus_url("http://localhost:33333");
ASSERT_FALSE(FetchUrl(bogus_url, context_getter_, &response));
ASSERT_STREQ("stuff", response.c_str());
}
...@@ -19,6 +19,8 @@ const char* DefaultMessageForStatusCode(StatusCode code) { ...@@ -19,6 +19,8 @@ const char* DefaultMessageForStatusCode(StatusCode code) {
return "session not created exception"; return "session not created exception";
case kNoSuchSession: case kNoSuchSession:
return "no such session"; return "no such session";
case kChromeNotReachable:
return "chrome not reachable";
default: default:
return "<unknown>"; return "<unknown>";
} }
...@@ -34,6 +36,21 @@ Status::Status(StatusCode code, const std::string& details) ...@@ -34,6 +36,21 @@ Status::Status(StatusCode code, const std::string& details)
msg_(DefaultMessageForStatusCode(code) + std::string(": ") + details) { msg_(DefaultMessageForStatusCode(code) + std::string(": ") + details) {
} }
Status::Status(StatusCode code, const Status& cause)
: code_(code),
msg_(DefaultMessageForStatusCode(code) + std::string("\nfrom ") +
cause.message()) {}
Status::Status(StatusCode code,
const std::string& details,
const Status& cause)
: code_(code),
msg_(DefaultMessageForStatusCode(code) + std::string(": ") + details +
"\nfrom " + cause.message()) {
}
Status::~Status() {}
bool Status::IsOk() const { bool Status::IsOk() const {
return code_ == kOk; return code_ == kOk;
} }
......
...@@ -13,7 +13,9 @@ enum StatusCode { ...@@ -13,7 +13,9 @@ enum StatusCode {
kUnknownCommand = 9, kUnknownCommand = 9,
kUnknownError = 13, kUnknownError = 13,
kSessionNotCreatedException = 33, kSessionNotCreatedException = 33,
kNoSuchSession = 100 // Chrome-specific status codes.
kNoSuchSession = 100,
kChromeNotReachable,
}; };
// Represents a WebDriver status, which may be an error or ok. // Represents a WebDriver status, which may be an error or ok.
...@@ -21,6 +23,9 @@ class Status { ...@@ -21,6 +23,9 @@ class Status {
public: public:
explicit Status(StatusCode code); explicit Status(StatusCode code);
Status(StatusCode code, const std::string& details); Status(StatusCode code, const std::string& details);
Status(StatusCode code, const Status& cause);
Status(StatusCode code, const std::string& details, const Status& cause);
~Status();
bool IsOk() const; bool IsOk() const;
bool IsError() const; bool IsError() const;
......
...@@ -14,17 +14,33 @@ TEST(StatusTest, Ok) { ...@@ -14,17 +14,33 @@ TEST(StatusTest, Ok) {
} }
TEST(StatusTest, Error) { TEST(StatusTest, Error) {
Status ok(kUnknownCommand); Status error(kUnknownCommand);
ASSERT_FALSE(ok.IsOk()); ASSERT_FALSE(error.IsOk());
ASSERT_TRUE(ok.IsError()); ASSERT_TRUE(error.IsError());
ASSERT_EQ(kUnknownCommand, ok.code()); ASSERT_EQ(kUnknownCommand, error.code());
ASSERT_STREQ("unknown command", ok.message().c_str()); ASSERT_STREQ("unknown command", error.message().c_str());
} }
TEST(StatusTest, ErrorWithDetails) { TEST(StatusTest, ErrorWithDetails) {
Status ok(kUnknownError, "something happened"); Status error(kUnknownError, "something happened");
ASSERT_FALSE(ok.IsOk()); ASSERT_FALSE(error.IsOk());
ASSERT_TRUE(ok.IsError()); ASSERT_TRUE(error.IsError());
ASSERT_EQ(kUnknownError, ok.code()); ASSERT_EQ(kUnknownError, error.code());
ASSERT_STREQ("unknown error: something happened", ok.message().c_str()); ASSERT_STREQ("unknown error: something happened", error.message().c_str());
}
TEST(StatusTest, ErrorWithCause) {
Status error(
kUnknownCommand, "quit",
Status(
kUnknownError, "something happened",
Status(kSessionNotCreatedException)));
ASSERT_FALSE(error.IsOk());
ASSERT_TRUE(error.IsError());
ASSERT_EQ(kUnknownCommand, error.code());
ASSERT_STREQ(
"unknown command: quit\n"
"from unknown error: something happened\n"
"from session not created exception",
error.message().c_str());
} }
...@@ -19,6 +19,12 @@ class ChromeDriverTest(unittest.TestCase): ...@@ -19,6 +19,12 @@ class ChromeDriverTest(unittest.TestCase):
driver = chromedriver.ChromeDriver(_CHROMEDRIVER_LIB, _CHROME_BINARY) driver = chromedriver.ChromeDriver(_CHROMEDRIVER_LIB, _CHROME_BINARY)
driver.Quit() driver.Quit()
def testLoadUrl(self):
driver = chromedriver.ChromeDriver(_CHROMEDRIVER_LIB)
driver.Load('http://www.google.com')
driver.Quit()
if __name__ == '__main__': if __name__ == '__main__':
if len(sys.argv) != 2 and len(sys.argv) != 3: if len(sys.argv) != 2 and len(sys.argv) != 3:
print ('Usage: %s <path_to_chromedriver_so> [path_to_chrome_binary]' % print ('Usage: %s <path_to_chromedriver_so> [path_to_chrome_binary]' %
......
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