summaryrefslogtreecommitdiffstats
path: root/chrome/test
diff options
context:
space:
mode:
authorkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-04 20:14:29 +0000
committerkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-04 20:14:29 +0000
commit7f01dde7a68ea76b24aa3c0afd5ca528e2ef9b0c (patch)
treeef16ccfa623aaffd3d193bfd2e5dd7254ed5f9d0 /chrome/test
parent8a48f3f925b5f3b15b69ea6d11568b85987ffc47 (diff)
downloadchromium_src-7f01dde7a68ea76b24aa3c0afd5ca528e2ef9b0c.zip
chromium_src-7f01dde7a68ea76b24aa3c0afd5ca528e2ef9b0c.tar.gz
chromium_src-7f01dde7a68ea76b24aa3c0afd5ca528e2ef9b0c.tar.bz2
[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
Diffstat (limited to 'chrome/test')
-rw-r--r--chrome/test/chromedriver/chrome.h4
-rw-r--r--chrome/test/chromedriver/chrome_impl.cc82
-rw-r--r--chrome/test/chromedriver/chrome_impl.h25
-rw-r--r--chrome/test/chromedriver/chrome_impl_unittest.cc63
-rw-r--r--chrome/test/chromedriver/chrome_launcher_impl.cc16
-rw-r--r--chrome/test/chromedriver/chrome_launcher_impl.h6
-rw-r--r--chrome/test/chromedriver/chromedriver.cc14
-rw-r--r--chrome/test/chromedriver/chromedriver.h1
-rw-r--r--chrome/test/chromedriver/chromedriver.py3
-rw-r--r--chrome/test/chromedriver/chromedriver_unittest.cc6
-rw-r--r--chrome/test/chromedriver/command_executor.h2
-rw-r--r--chrome/test/chromedriver/command_executor_impl.cc24
-rw-r--r--chrome/test/chromedriver/command_executor_impl.h10
-rw-r--r--chrome/test/chromedriver/commands.cc10
-rw-r--r--chrome/test/chromedriver/commands.h6
-rw-r--r--chrome/test/chromedriver/commands_unittest.cc5
-rw-r--r--chrome/test/chromedriver/devtools_client.cc44
-rw-r--r--chrome/test/chromedriver/devtools_client.h40
-rw-r--r--chrome/test/chromedriver/net/net_util.cc50
-rw-r--r--chrome/test/chromedriver/net/net_util.h19
-rw-r--r--chrome/test/chromedriver/net/net_util_unittest.cc141
-rw-r--r--chrome/test/chromedriver/status.cc17
-rw-r--r--chrome/test/chromedriver/status.h7
-rw-r--r--chrome/test/chromedriver/status_unittest.cc36
-rw-r--r--chrome/test/chromedriver/test.py6
25 files changed, 610 insertions, 27 deletions
diff --git a/chrome/test/chromedriver/chrome.h b/chrome/test/chromedriver/chrome.h
index c3df4c4..a0675d0 100644
--- a/chrome/test/chromedriver/chrome.h
+++ b/chrome/test/chromedriver/chrome.h
@@ -5,11 +5,15 @@
#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_H_
#define CHROME_TEST_CHROMEDRIVER_CHROME_H_
+#include <string>
+
class Status;
class Chrome {
public:
virtual ~Chrome() {}
+
+ virtual Status Load(const std::string& url) = 0;
virtual Status Quit() = 0;
};
diff --git a/chrome/test/chromedriver/chrome_impl.cc b/chrome/test/chromedriver/chrome_impl.cc
index 5cac6f3..f074d3c 100644
--- a/chrome/test/chromedriver/chrome_impl.cc
+++ b/chrome/test/chromedriver/chrome_impl.cc
@@ -4,13 +4,42 @@
#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/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 "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,
- base::ScopedTempDir* user_data_dir)
- : process_(process) {
+ URLRequestContextGetter* context_getter,
+ base::ScopedTempDir* user_data_dir,
+ int port)
+ : process_(process),
+ context_getter_(context_getter),
+ port_(port) {
if (user_data_dir->IsValid()) {
CHECK(user_data_dir_.Set(user_data_dir->Take()));
}
@@ -20,8 +49,57 @@ ChromeImpl::~ChromeImpl() {
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() {
if (!base::KillProcess(process_, 0, true))
return Status(kUnknownError, "cannot kill Chrome");
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
diff --git a/chrome/test/chromedriver/chrome_impl.h b/chrome/test/chromedriver/chrome_impl.h
index 1a6d1c8..5f6056e 100644
--- a/chrome/test/chromedriver/chrome_impl.h
+++ b/chrome/test/chromedriver/chrome_impl.h
@@ -5,24 +5,47 @@
#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_IMPL_H_
#define CHROME_TEST_CHROMEDRIVER_CHROME_IMPL_H_
+#include <list>
+#include <string>
+
#include "base/compiler_specific.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 "chrome/test/chromedriver/chrome.h"
+class DevToolsClient;
class Status;
+class URLRequestContextGetter;
class ChromeImpl : public Chrome {
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();
+ Status Init();
+
// Overridden from Chrome:
+ virtual Status Load(const std::string& url) OVERRIDE;
virtual Status Quit() OVERRIDE;
private:
base::ProcessHandle process_;
+ scoped_refptr<URLRequestContextGetter> context_getter_;
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_
diff --git a/chrome/test/chromedriver/chrome_impl_unittest.cc b/chrome/test/chromedriver/chrome_impl_unittest.cc
new file mode 100644
index 0000000..70f0ea4
--- /dev/null
+++ b/chrome/test/chromedriver/chrome_impl_unittest.cc
@@ -0,0 +1,63 @@
+// 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}]");
+}
diff --git a/chrome/test/chromedriver/chrome_launcher_impl.cc b/chrome/test/chromedriver/chrome_launcher_impl.cc
index 3e124fb..ee484ab 100644
--- a/chrome/test/chromedriver/chrome_launcher_impl.cc
+++ b/chrome/test/chromedriver/chrome_launcher_impl.cc
@@ -10,12 +10,15 @@
#include "base/file_path.h"
#include "base/process.h"
#include "base/process_util.h"
+#include "base/string_number_conversions.h"
#include "chrome/test/chromedriver/chrome.h"
#include "chrome/test/chromedriver/chrome_finder.h"
#include "chrome/test/chromedriver/chrome_impl.h"
+#include "chrome/test/chromedriver/net/url_request_context_getter.h"
#include "chrome/test/chromedriver/status.h"
-ChromeLauncherImpl::ChromeLauncherImpl() {}
+ChromeLauncherImpl::ChromeLauncherImpl(URLRequestContextGetter* context_getter)
+ : context_getter_(context_getter) {}
ChromeLauncherImpl::~ChromeLauncherImpl() {}
@@ -28,7 +31,10 @@ Status ChromeLauncherImpl::Launch(
return Status(kUnknownError, "cannot find Chrome binary");
}
+ int port = 33081;
CommandLine command(program);
+ command.AppendSwitchASCII("remote-debugging-port", base::IntToString(port));
+ command.AppendSwitch("no-first-run");
command.AppendSwitch("enable-logging");
command.AppendSwitchASCII("logging-level", "1");
base::ScopedTempDir user_data_dir;
@@ -41,7 +47,11 @@ Status ChromeLauncherImpl::Launch(
base::ProcessHandle process;
if (!base::LaunchProcess(command, options, &process))
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);
}
diff --git a/chrome/test/chromedriver/chrome_launcher_impl.h b/chrome/test/chromedriver/chrome_launcher_impl.h
index ecd7bd1..542017e3b 100644
--- a/chrome/test/chromedriver/chrome_launcher_impl.h
+++ b/chrome/test/chromedriver/chrome_launcher_impl.h
@@ -7,16 +7,18 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/test/chromedriver/chrome_launcher.h"
class Chrome;
class FilePath;
class Status;
+class URLRequestContextGetter;
class ChromeLauncherImpl : public ChromeLauncher {
public:
- ChromeLauncherImpl();
+ explicit ChromeLauncherImpl(URLRequestContextGetter* context_getter);
virtual ~ChromeLauncherImpl();
// Overridden from ChromeLauncher:
@@ -24,6 +26,8 @@ class ChromeLauncherImpl : public ChromeLauncher {
scoped_ptr<Chrome>* chrome) OVERRIDE;
private:
+ scoped_refptr<URLRequestContextGetter> context_getter_;
+
DISALLOW_COPY_AND_ASSIGN(ChromeLauncherImpl);
};
diff --git a/chrome/test/chromedriver/chromedriver.cc b/chrome/test/chromedriver/chromedriver.cc
index 9f66847..70b5746 100644
--- a/chrome/test/chromedriver/chromedriver.cc
+++ b/chrome/test/chromedriver/chromedriver.cc
@@ -6,14 +6,19 @@
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
#include "base/values.h"
#include "chrome/test/chromedriver/command_executor.h"
#include "chrome/test/chromedriver/status.h"
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;
void SetResponse(StatusCode status,
@@ -39,10 +44,19 @@ void SetError(const std::string& error_msg,
void Init(scoped_ptr<CommandExecutor> executor) {
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) {
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;
scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
command, 0, NULL, &error_msg));
diff --git a/chrome/test/chromedriver/chromedriver.h b/chrome/test/chromedriver/chromedriver.h
index 5252da1..3872b003 100644
--- a/chrome/test/chromedriver/chromedriver.h
+++ b/chrome/test/chromedriver/chromedriver.h
@@ -12,6 +12,7 @@
class CommandExecutor;
// Inits the command executor. Must be called before |ExecuteCommand|.
+// This may be called during DLL load on Windows.
void Init(scoped_ptr<CommandExecutor> executor);
// Synchronously executes the given command. Thread safe.
diff --git a/chrome/test/chromedriver/chromedriver.py b/chrome/test/chromedriver/chromedriver.py
index cbe6d3b..c0bfadd 100644
--- a/chrome/test/chromedriver/chromedriver.py
+++ b/chrome/test/chromedriver/chromedriver.py
@@ -68,6 +68,9 @@ class ChromeDriver(object):
def _ExecuteSessionCommand(self, name, params={}):
return self._ExecuteCommand(name, params, self._session_id)
+ def Load(self, url):
+ self._ExecuteSessionCommand('get', {'url': url})
+
def Quit(self):
"""Quits the browser and ends the session."""
self._ExecuteSessionCommand('quit')
diff --git a/chrome/test/chromedriver/chromedriver_unittest.cc b/chrome/test/chromedriver/chromedriver_unittest.cc
index 9c85383..fc8bef6 100644
--- a/chrome/test/chromedriver/chromedriver_unittest.cc
+++ b/chrome/test/chromedriver/chromedriver_unittest.cc
@@ -38,13 +38,13 @@ class DummyExecutor : public CommandExecutor {
public:
virtual ~DummyExecutor() {}
+ virtual void Init() OVERRIDE {}
virtual void ExecuteCommand(const std::string& name,
const base::DictionaryValue& params,
const std::string& session_id,
StatusCode* status,
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 {
EXPECT_TRUE(DidSatisfyExpectations());
}
+ virtual void Init() OVERRIDE {}
+
virtual void ExecuteCommand(const std::string& name,
const base::DictionaryValue& params,
const std::string& session_id,
diff --git a/chrome/test/chromedriver/command_executor.h b/chrome/test/chromedriver/command_executor.h
index db0b0f9..5f8e5fe 100644
--- a/chrome/test/chromedriver/command_executor.h
+++ b/chrome/test/chromedriver/command_executor.h
@@ -22,6 +22,8 @@ class CommandExecutor {
public:
virtual ~CommandExecutor() {}
+ virtual void Init() = 0;
+
// Executes a command synchronously. This function must be thread safe.
virtual void ExecuteCommand(const std::string& name,
const base::DictionaryValue& params,
diff --git a/chrome/test/chromedriver/command_executor_impl.cc b/chrome/test/chromedriver/command_executor_impl.cc
index 57c3a24..4f5b57a 100644
--- a/chrome/test/chromedriver/command_executor_impl.cc
+++ b/chrome/test/chromedriver/command_executor_impl.cc
@@ -6,14 +6,30 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
#include "base/values.h"
+#include "chrome/test/chromedriver/chrome_launcher_impl.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_command.h"
#include "chrome/test/chromedriver/session_map.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(
const SessionCommand&,
const base::DictionaryValue&,
@@ -22,19 +38,19 @@ CommandExecutorImpl::CommandExecutorImpl() {
std::string*)> execute_session_command = base::Bind(
&ExecuteSessionCommand,
&session_map_);
+ command_map_.Set("get", base::Bind(execute_session_command,
+ base::Bind(&ExecuteGet)));
Command quit_command = base::Bind(execute_session_command,
base::Bind(&ExecuteQuit, &session_map_));
command_map_.Set("quit", quit_command);
// Non-session commands.
command_map_.Set("newSession",
- base::Bind(&ExecuteNewSession, &session_map_, &launcher_));
+ base::Bind(&ExecuteNewSession, &session_map_, launcher_.get()));
command_map_.Set("quitAll",
base::Bind(&ExecuteQuitAll, quit_command, &session_map_));
}
-CommandExecutorImpl::~CommandExecutorImpl() {}
-
void CommandExecutorImpl::ExecuteCommand(
const std::string& name,
const base::DictionaryValue& params,
diff --git a/chrome/test/chromedriver/command_executor_impl.h b/chrome/test/chromedriver/command_executor_impl.h
index 125b1e3..092809f 100644
--- a/chrome/test/chromedriver/command_executor_impl.h
+++ b/chrome/test/chromedriver/command_executor_impl.h
@@ -12,7 +12,7 @@
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.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_executor.h"
#include "chrome/test/chromedriver/session_map.h"
@@ -24,12 +24,16 @@ class DictionaryValue;
class Value;
}
+class ChromeLauncherImpl;
+class URLRequestContextGetter;
+
class CommandExecutorImpl : public CommandExecutor {
public:
CommandExecutorImpl();
virtual ~CommandExecutorImpl();
// Overridden from CommandExecutor:
+ virtual void Init() OVERRIDE;
virtual void ExecuteCommand(const std::string& name,
const base::DictionaryValue& params,
const std::string& session_id,
@@ -43,8 +47,10 @@ class CommandExecutorImpl : public CommandExecutor {
CommandExecutorImplTest, CommandThatDoesntSetValueOrSessionId);
FRIEND_TEST_ALL_PREFIXES(CommandExecutorImplTest, CommandThatReturnsError);
+ base::Thread io_thread_;
+ scoped_refptr<URLRequestContextGetter> context_getter_;
SessionMap session_map_;
- ChromeLauncherImpl launcher_;
+ scoped_ptr<ChromeLauncherImpl> launcher_;
SynchronizedMap<std::string, Command> command_map_;
DISALLOW_COPY_AND_ASSIGN(CommandExecutorImpl);
diff --git a/chrome/test/chromedriver/commands.cc b/chrome/test/chromedriver/commands.cc
index 8602aa3..ebf5513 100644
--- a/chrome/test/chromedriver/commands.cc
+++ b/chrome/test/chromedriver/commands.cc
@@ -78,3 +78,13 @@ Status ExecuteQuit(
CHECK(session_map->Remove(session->id));
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);
+}
diff --git a/chrome/test/chromedriver/commands.h b/chrome/test/chromedriver/commands.h
index ac72f1b..3e1a68b 100644
--- a/chrome/test/chromedriver/commands.h
+++ b/chrome/test/chromedriver/commands.h
@@ -46,4 +46,10 @@ Status ExecuteQuit(
const base::DictionaryValue& params,
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_
diff --git a/chrome/test/chromedriver/commands_unittest.cc b/chrome/test/chromedriver/commands_unittest.cc
index d7612d9..eef7136 100644
--- a/chrome/test/chromedriver/commands_unittest.cc
+++ b/chrome/test/chromedriver/commands_unittest.cc
@@ -27,6 +27,9 @@ class StubChrome : public Chrome {
virtual ~StubChrome() {}
// Overridden from Chrome:
+ virtual Status Load(const std::string& url) OVERRIDE {
+ return Status(kOk);
+ }
virtual Status Quit() OVERRIDE {
return Status(kOk);
}
@@ -152,7 +155,7 @@ TEST(CommandsTest, Quit) {
namespace {
-class FailsToQuitChrome : public Chrome {
+class FailsToQuitChrome : public StubChrome {
public:
FailsToQuitChrome() {}
virtual ~FailsToQuitChrome() {}
diff --git a/chrome/test/chromedriver/devtools_client.cc b/chrome/test/chromedriver/devtools_client.cc
new file mode 100644
index 0000000..f78a492
--- /dev/null
+++ b/chrome/test/chromedriver/devtools_client.cc
@@ -0,0 +1,44 @@
+// 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);
+}
diff --git a/chrome/test/chromedriver/devtools_client.h b/chrome/test/chromedriver/devtools_client.h
new file mode 100644
index 0000000..218632f
--- /dev/null
+++ b/chrome/test/chromedriver/devtools_client.h
@@ -0,0 +1,40 @@
+// 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_
diff --git a/chrome/test/chromedriver/net/net_util.cc b/chrome/test/chromedriver/net/net_util.cc
new file mode 100644
index 0000000..c6aebc3
--- /dev/null
+++ b/chrome/test/chromedriver/net/net_util.cc
@@ -0,0 +1,50 @@
+// 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);
+}
diff --git a/chrome/test/chromedriver/net/net_util.h b/chrome/test/chromedriver/net/net_util.h
new file mode 100644
index 0000000..78ab424
--- /dev/null
+++ b/chrome/test/chromedriver/net/net_util.h
@@ -0,0 +1,19 @@
+// 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_
diff --git a/chrome/test/chromedriver/net/net_util_unittest.cc b/chrome/test/chromedriver/net/net_util_unittest.cc
new file mode 100644
index 0000000..7eee142
--- /dev/null
+++ b/chrome/test/chromedriver/net/net_util_unittest.cc
@@ -0,0 +1,141 @@
+// 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());
+}
diff --git a/chrome/test/chromedriver/status.cc b/chrome/test/chromedriver/status.cc
index bd50279..89dc7e9 100644
--- a/chrome/test/chromedriver/status.cc
+++ b/chrome/test/chromedriver/status.cc
@@ -19,6 +19,8 @@ const char* DefaultMessageForStatusCode(StatusCode code) {
return "session not created exception";
case kNoSuchSession:
return "no such session";
+ case kChromeNotReachable:
+ return "chrome not reachable";
default:
return "<unknown>";
}
@@ -34,6 +36,21 @@ Status::Status(StatusCode code, const 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 {
return code_ == kOk;
}
diff --git a/chrome/test/chromedriver/status.h b/chrome/test/chromedriver/status.h
index a52d6e4..7ecb808 100644
--- a/chrome/test/chromedriver/status.h
+++ b/chrome/test/chromedriver/status.h
@@ -13,7 +13,9 @@ enum StatusCode {
kUnknownCommand = 9,
kUnknownError = 13,
kSessionNotCreatedException = 33,
- kNoSuchSession = 100
+ // Chrome-specific status codes.
+ kNoSuchSession = 100,
+ kChromeNotReachable,
};
// Represents a WebDriver status, which may be an error or ok.
@@ -21,6 +23,9 @@ class Status {
public:
explicit Status(StatusCode code);
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 IsError() const;
diff --git a/chrome/test/chromedriver/status_unittest.cc b/chrome/test/chromedriver/status_unittest.cc
index a585afb..9946c07 100644
--- a/chrome/test/chromedriver/status_unittest.cc
+++ b/chrome/test/chromedriver/status_unittest.cc
@@ -14,17 +14,33 @@ TEST(StatusTest, Ok) {
}
TEST(StatusTest, Error) {
- Status ok(kUnknownCommand);
- ASSERT_FALSE(ok.IsOk());
- ASSERT_TRUE(ok.IsError());
- ASSERT_EQ(kUnknownCommand, ok.code());
- ASSERT_STREQ("unknown command", ok.message().c_str());
+ Status error(kUnknownCommand);
+ ASSERT_FALSE(error.IsOk());
+ ASSERT_TRUE(error.IsError());
+ ASSERT_EQ(kUnknownCommand, error.code());
+ ASSERT_STREQ("unknown command", error.message().c_str());
}
TEST(StatusTest, ErrorWithDetails) {
- Status ok(kUnknownError, "something happened");
- ASSERT_FALSE(ok.IsOk());
- ASSERT_TRUE(ok.IsError());
- ASSERT_EQ(kUnknownError, ok.code());
- ASSERT_STREQ("unknown error: something happened", ok.message().c_str());
+ Status error(kUnknownError, "something happened");
+ ASSERT_FALSE(error.IsOk());
+ ASSERT_TRUE(error.IsError());
+ ASSERT_EQ(kUnknownError, error.code());
+ 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());
}
diff --git a/chrome/test/chromedriver/test.py b/chrome/test/chromedriver/test.py
index 6f17e2a..de37fc5 100644
--- a/chrome/test/chromedriver/test.py
+++ b/chrome/test/chromedriver/test.py
@@ -19,6 +19,12 @@ class ChromeDriverTest(unittest.TestCase):
driver = chromedriver.ChromeDriver(_CHROMEDRIVER_LIB, _CHROME_BINARY)
driver.Quit()
+ def testLoadUrl(self):
+ driver = chromedriver.ChromeDriver(_CHROMEDRIVER_LIB)
+ driver.Load('http://www.google.com')
+ driver.Quit()
+
+
if __name__ == '__main__':
if len(sys.argv) != 2 and len(sys.argv) != 3:
print ('Usage: %s <path_to_chromedriver_so> [path_to_chrome_binary]' %