diff options
author | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-14 19:08:41 +0000 |
---|---|---|
committer | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-14 19:08:41 +0000 |
commit | 704731bb52650d08ee63e3ab2b4990b6f58ff4eb (patch) | |
tree | 3bff5eeb5bbd9b034fe3876d0a898e9b30bb6531 | |
parent | 2d463b880aefa086893f327cf3e9bafbfd10882a (diff) | |
download | chromium_src-704731bb52650d08ee63e3ab2b4990b6f58ff4eb.zip chromium_src-704731bb52650d08ee63e3ab2b4990b6f58ff4eb.tar.gz chromium_src-704731bb52650d08ee63e3ab2b4990b6f58ff4eb.tar.bz2 |
[chromedriver] Add DevToolsClient::SendCommandAndGetResult and refactor for testability.
Introduces abstract DevToolsClient and SyncWebSocket.
Review URL: https://codereview.chromium.org/11558021
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173184 0039d316-1c4b-4281-b951-d872f2087c98
17 files changed, 685 insertions, 205 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index c2d28ed..acd8579 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -697,12 +697,16 @@ 'test/chromedriver/command_executor_impl.h', 'test/chromedriver/commands.cc', 'test/chromedriver/commands.h', - 'test/chromedriver/devtools_client.cc', 'test/chromedriver/devtools_client.h', + 'test/chromedriver/devtools_client_impl.cc', + 'test/chromedriver/devtools_client_impl.h', 'test/chromedriver/net/net_util.cc', 'test/chromedriver/net/net_util.h', - 'test/chromedriver/net/sync_websocket.cc', 'test/chromedriver/net/sync_websocket.h', + 'test/chromedriver/net/sync_websocket_factory.cc', + 'test/chromedriver/net/sync_websocket_factory.h', + 'test/chromedriver/net/sync_websocket_impl.cc', + 'test/chromedriver/net/sync_websocket_impl.h', 'test/chromedriver/net/url_request_context_getter.cc', 'test/chromedriver/net/url_request_context_getter.h', 'test/chromedriver/net/websocket.cc', @@ -735,6 +739,7 @@ 'test/chromedriver/chromedriver_unittest.cc', 'test/chromedriver/command_executor_impl_unittest.cc', 'test/chromedriver/commands_unittest.cc', + 'test/chromedriver/devtools_client_impl_unittest.cc', 'test/chromedriver/fake_session_accessor.cc', 'test/chromedriver/fake_session_accessor.h', 'test/chromedriver/session_command_unittest.cc', @@ -763,7 +768,7 @@ ], 'sources': [ 'test/chromedriver/net/net_util_unittest.cc', - 'test/chromedriver/net/sync_websocket_unittest.cc', + 'test/chromedriver/net/sync_websocket_impl_unittest.cc', 'test/chromedriver/net/websocket_unittest.cc', ], }, diff --git a/chrome/test/chromedriver/chrome_impl.cc b/chrome/test/chromedriver/chrome_impl.cc index f074d3c..f6f270b 100644 --- a/chrome/test/chromedriver/chrome_impl.cc +++ b/chrome/test/chromedriver/chrome_impl.cc @@ -12,8 +12,9 @@ #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/devtools_client_impl.h" #include "chrome/test/chromedriver/net/net_util.h" +#include "chrome/test/chromedriver/net/sync_websocket_impl.h" #include "chrome/test/chromedriver/net/url_request_context_getter.h" #include "chrome/test/chromedriver/status.h" #include "googleurl/src/gurl.h" @@ -36,10 +37,12 @@ Status FetchPagesInfo(URLRequestContextGetter* context_getter, ChromeImpl::ChromeImpl(base::ProcessHandle process, URLRequestContextGetter* context_getter, base::ScopedTempDir* user_data_dir, - int port) + int port, + const SyncWebSocketFactory& socket_factory) : process_(process), context_getter_(context_getter), - port_(port) { + port_(port), + socket_factory_(socket_factory) { if (user_data_dir->IsValid()) { CHECK(user_data_dir_.Set(user_data_dir->Take())); } @@ -61,7 +64,7 @@ Status ChromeImpl::Init() { } if (debugger_urls.empty()) return Status(kUnknownError, "unable to discover open pages"); - client_.reset(new DevToolsClient(context_getter_, debugger_urls.front())); + client_.reset(new DevToolsClientImpl(socket_factory_, debugger_urls.front())); return Status(kOk); } diff --git a/chrome/test/chromedriver/chrome_impl.h b/chrome/test/chromedriver/chrome_impl.h index 5f6056e..c9c6dcf9 100644 --- a/chrome/test/chromedriver/chrome_impl.h +++ b/chrome/test/chromedriver/chrome_impl.h @@ -14,6 +14,7 @@ #include "base/memory/scoped_ptr.h" #include "base/process.h" #include "chrome/test/chromedriver/chrome.h" +#include "chrome/test/chromedriver/net/sync_websocket_factory.h" class DevToolsClient; class Status; @@ -24,7 +25,8 @@ class ChromeImpl : public Chrome { ChromeImpl(base::ProcessHandle process, URLRequestContextGetter* context_getter, base::ScopedTempDir* user_data_dir, - int port); + int port, + const SyncWebSocketFactory& socket_factory); virtual ~ChromeImpl(); Status Init(); @@ -38,6 +40,7 @@ class ChromeImpl : public Chrome { scoped_refptr<URLRequestContextGetter> context_getter_; base::ScopedTempDir user_data_dir_; int port_; + SyncWebSocketFactory socket_factory_; scoped_ptr<DevToolsClient> client_; }; diff --git a/chrome/test/chromedriver/chrome_launcher_impl.cc b/chrome/test/chromedriver/chrome_launcher_impl.cc index ee484ab..752477c 100644 --- a/chrome/test/chromedriver/chrome_launcher_impl.cc +++ b/chrome/test/chromedriver/chrome_launcher_impl.cc @@ -17,8 +17,11 @@ #include "chrome/test/chromedriver/net/url_request_context_getter.h" #include "chrome/test/chromedriver/status.h" -ChromeLauncherImpl::ChromeLauncherImpl(URLRequestContextGetter* context_getter) - : context_getter_(context_getter) {} +ChromeLauncherImpl::ChromeLauncherImpl( + URLRequestContextGetter* context_getter, + const SyncWebSocketFactory& socket_factory) + : context_getter_(context_getter), + socket_factory_(socket_factory) {} ChromeLauncherImpl::~ChromeLauncherImpl() {} @@ -48,7 +51,7 @@ Status ChromeLauncherImpl::Launch( if (!base::LaunchProcess(command, options, &process)) return Status(kUnknownError, "chrome failed to start"); scoped_ptr<ChromeImpl> chrome_impl(new ChromeImpl( - process, context_getter_, &user_data_dir, port)); + process, context_getter_, &user_data_dir, port, socket_factory_)); Status status = chrome_impl->Init(); if (status.IsError()) return status; diff --git a/chrome/test/chromedriver/chrome_launcher_impl.h b/chrome/test/chromedriver/chrome_launcher_impl.h index 542017e3b..1910ac5 100644 --- a/chrome/test/chromedriver/chrome_launcher_impl.h +++ b/chrome/test/chromedriver/chrome_launcher_impl.h @@ -10,6 +10,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "chrome/test/chromedriver/chrome_launcher.h" +#include "chrome/test/chromedriver/net/sync_websocket_factory.h" class Chrome; class FilePath; @@ -18,7 +19,8 @@ class URLRequestContextGetter; class ChromeLauncherImpl : public ChromeLauncher { public: - explicit ChromeLauncherImpl(URLRequestContextGetter* context_getter); + explicit ChromeLauncherImpl(URLRequestContextGetter* context_getter, + const SyncWebSocketFactory& socket_factory); virtual ~ChromeLauncherImpl(); // Overridden from ChromeLauncher: @@ -27,6 +29,7 @@ class ChromeLauncherImpl : public ChromeLauncher { private: scoped_refptr<URLRequestContextGetter> context_getter_; + SyncWebSocketFactory socket_factory_; DISALLOW_COPY_AND_ASSIGN(ChromeLauncherImpl); }; diff --git a/chrome/test/chromedriver/command_executor_impl.cc b/chrome/test/chromedriver/command_executor_impl.cc index 4f5b57a..9f7965e 100644 --- a/chrome/test/chromedriver/command_executor_impl.cc +++ b/chrome/test/chromedriver/command_executor_impl.cc @@ -11,6 +11,7 @@ #include "base/values.h" #include "chrome/test/chromedriver/chrome_launcher_impl.h" #include "chrome/test/chromedriver/commands.h" +#include "chrome/test/chromedriver/net/sync_websocket_factory.h" #include "chrome/test/chromedriver/net/url_request_context_getter.h" #include "chrome/test/chromedriver/session.h" #include "chrome/test/chromedriver/session_command.h" @@ -27,7 +28,9 @@ void CommandExecutorImpl::Init() { CHECK(io_thread_.StartWithOptions(options)); context_getter_ = new URLRequestContextGetter( io_thread_.message_loop_proxy()); - launcher_.reset(new ChromeLauncherImpl(context_getter_)); + launcher_.reset(new ChromeLauncherImpl( + context_getter_, + CreateSyncWebSocketFactory(context_getter_))); // Session commands. base::Callback<Status( diff --git a/chrome/test/chromedriver/devtools_client.cc b/chrome/test/chromedriver/devtools_client.cc deleted file mode 100644 index f78a492..0000000 --- a/chrome/test/chromedriver/devtools_client.cc +++ /dev/null @@ -1,44 +0,0 @@ -// 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 index 218632f..4edb9de 100644 --- a/chrome/test/chromedriver/devtools_client.h +++ b/chrome/test/chromedriver/devtools_client.h @@ -7,7 +7,6 @@ #include <string> -#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" namespace base { @@ -15,26 +14,18 @@ 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); + virtual ~DevToolsClient() {} + + virtual Status SendCommand(const std::string& method, + const base::DictionaryValue& params) = 0; + virtual Status SendCommandAndGetResult( + const std::string& method, + const base::DictionaryValue& params, + scoped_ptr<base::DictionaryValue>* result) = 0; }; #endif // CHROME_TEST_CHROMEDRIVER_DEVTOOLS_CLIENT_H_ diff --git a/chrome/test/chromedriver/devtools_client_impl.cc b/chrome/test/chromedriver/devtools_client_impl.cc new file mode 100644 index 0000000..746132e --- /dev/null +++ b/chrome/test/chromedriver/devtools_client_impl.cc @@ -0,0 +1,87 @@ +// 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_impl.h" + +#include "base/json/json_reader.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" + +DevToolsClientImpl::DevToolsClientImpl( + const SyncWebSocketFactory& factory, + const std::string& url) + : socket_(factory.Run().Pass()), + url_(url), + connected_(false), + next_id_(1) {} + +DevToolsClientImpl::~DevToolsClientImpl() {} + +Status DevToolsClientImpl::SendCommand( + const std::string& method, + const base::DictionaryValue& params) { + scoped_ptr<base::DictionaryValue> result; + return SendCommandInternal(method, params, &result); +} + +Status DevToolsClientImpl::SendCommandAndGetResult( + const std::string& method, + const base::DictionaryValue& params, + scoped_ptr<base::DictionaryValue>* result) { + scoped_ptr<base::DictionaryValue> intermediate_result; + Status status = SendCommandInternal(method, params, &intermediate_result); + if (status.IsError()) + return status; + if (!intermediate_result) + return Status(kUnknownError, "inspector response missing result"); + result->reset(intermediate_result.release()); + return Status(kOk); +} + +Status DevToolsClientImpl::SendCommandInternal( + const std::string& method, + const base::DictionaryValue& params, + scoped_ptr<base::DictionaryValue>* result) { + if (!connected_) { + if (!socket_->Connect(url_)) + return Status(kUnknownError, "unable to connect to renderer"); + connected_ = true; + } + + base::DictionaryValue command; + command.SetInteger("id", next_id_++); + 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"); + scoped_ptr<base::Value> response_value(base::JSONReader::Read(response)); + base::DictionaryValue* response_dict; + if (!response_value || !response_value->GetAsDictionary(&response_dict)) + return Status(kUnknownError, "missing or invalid inspector response"); + + int command_id; + if (!response_dict->GetInteger("id", &command_id)) + return Status(kUnknownError, "inspector response must have integer 'id'"); + if (command_id != next_id_ - 1) + return Status(kUnknownError, "inspector response ID does not match"); + base::DictionaryValue* unscoped_error; + if (response_dict->GetDictionary("error", &unscoped_error)) { + std::string error_string; + base::JSONWriter::Write(unscoped_error, &error_string); + return Status(kUnknownError, "inspector error: " + error_string); + } + base::DictionaryValue* unscoped_result; + if (response_dict->GetDictionary("result", &unscoped_result)) + result->reset(unscoped_result->DeepCopy()); + return Status(kOk); +} diff --git a/chrome/test/chromedriver/devtools_client_impl.h b/chrome/test/chromedriver/devtools_client_impl.h new file mode 100644 index 0000000..12a349e --- /dev/null +++ b/chrome/test/chromedriver/devtools_client_impl.h @@ -0,0 +1,51 @@ +// 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_IMPL_H_ +#define CHROME_TEST_CHROMEDRIVER_DEVTOOLS_CLIENT_IMPL_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/test/chromedriver/devtools_client.h" +#include "chrome/test/chromedriver/net/sync_websocket_factory.h" +#include "googleurl/src/gurl.h" + +namespace base { +class DictionaryValue; +} + +class Status; +class SyncWebSocket; + +class DevToolsClientImpl : public DevToolsClient { + public: + DevToolsClientImpl(const SyncWebSocketFactory& factory, + const std::string& url); + virtual ~DevToolsClientImpl(); + + // Overridden from DevToolsClient: + virtual Status SendCommand(const std::string& method, + const base::DictionaryValue& params) OVERRIDE; + virtual Status SendCommandAndGetResult( + const std::string& method, + const base::DictionaryValue& params, + scoped_ptr<base::DictionaryValue>* result) OVERRIDE; + + private: + Status SendCommandInternal( + const std::string& method, + const base::DictionaryValue& params, + scoped_ptr<base::DictionaryValue>* result); + scoped_ptr<SyncWebSocket> socket_; + GURL url_; + bool connected_; + int next_id_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsClientImpl); +}; + +#endif // CHROME_TEST_CHROMEDRIVER_DEVTOOLS_CLIENT_IMPL_H_ diff --git a/chrome/test/chromedriver/devtools_client_impl_unittest.cc b/chrome/test/chromedriver/devtools_client_impl_unittest.cc new file mode 100644 index 0000000..c30c871 --- /dev/null +++ b/chrome/test/chromedriver/devtools_client_impl_unittest.cc @@ -0,0 +1,309 @@ +// 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 "base/bind.h" +#include "base/compiler_specific.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chrome/test/chromedriver/devtools_client_impl.h" +#include "chrome/test/chromedriver/net/sync_websocket.h" +#include "chrome/test/chromedriver/net/sync_websocket_factory.h" +#include "chrome/test/chromedriver/status.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class MockSyncWebSocket : public SyncWebSocket { + public: + MockSyncWebSocket() : connected_(false), id_(-1) {} + virtual ~MockSyncWebSocket() {} + + virtual bool Connect(const GURL& url) OVERRIDE { + EXPECT_STREQ("http://url/", url.possibly_invalid_spec().c_str()); + connected_ = true; + return true; + } + + virtual bool Send(const std::string& message) OVERRIDE { + EXPECT_TRUE(connected_); + scoped_ptr<base::Value> value(base::JSONReader::Read(message)); + base::DictionaryValue* dict = NULL; + EXPECT_TRUE(value->GetAsDictionary(&dict)); + if (!dict) + return false; + EXPECT_TRUE(dict->GetInteger("id", &id_)); + std::string method; + EXPECT_TRUE(dict->GetString("method", &method)); + EXPECT_STREQ("method", method.c_str()); + base::DictionaryValue* params = NULL; + EXPECT_TRUE(dict->GetDictionary("params", ¶ms)); + if (!params) + return false; + int param = -1; + EXPECT_TRUE(params->GetInteger("param", ¶m)); + EXPECT_EQ(1, param); + return true; + } + + virtual bool ReceiveNextMessage(std::string* message) OVERRIDE { + base::DictionaryValue response; + response.SetInteger("id", id_); + base::DictionaryValue result; + result.SetInteger("param", 1); + response.Set("result", result.DeepCopy()); + base::JSONWriter::Write(&response, message); + return true; + } + + private: + bool connected_; + int id_; +}; + +template <typename T> +scoped_ptr<SyncWebSocket> CreateMockSyncWebSocket() { + return scoped_ptr<SyncWebSocket>(new T()); +} + +} // namespace + +TEST(DevToolsClientImpl, SendCommand) { + SyncWebSocketFactory factory = + base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>); + DevToolsClientImpl client(factory, "http://url"); + base::DictionaryValue params; + params.SetInteger("param", 1); + ASSERT_EQ(kOk, client.SendCommand("method", params).code()); +} + +TEST(DevToolsClientImpl, SendCommandAndGetResult) { + SyncWebSocketFactory factory = + base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket>); + DevToolsClientImpl client(factory, "http://url"); + base::DictionaryValue params; + params.SetInteger("param", 1); + scoped_ptr<base::DictionaryValue> result; + Status status = client.SendCommandAndGetResult("method", params, &result); + ASSERT_EQ(kOk, status.code()); + std::string json; + base::JSONWriter::Write(result.get(), &json); + ASSERT_STREQ("{\"param\":1}", json.c_str()); +} + +namespace { + +class MockSyncWebSocket2 : public SyncWebSocket { + public: + MockSyncWebSocket2() {} + virtual ~MockSyncWebSocket2() {} + + virtual bool Connect(const GURL& url) OVERRIDE { + return false; + } + + virtual bool Send(const std::string& message) OVERRIDE { + EXPECT_TRUE(false); + return false; + } + + virtual bool ReceiveNextMessage(std::string* message) OVERRIDE { + EXPECT_TRUE(false); + return false; + } +}; + +} // namespace + +TEST(DevToolsClientImpl, SendCommandConnectFails) { + SyncWebSocketFactory factory = + base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket2>); + DevToolsClientImpl client(factory, "http://url"); + base::DictionaryValue params; + ASSERT_TRUE(client.SendCommand("method", params).IsError()); +} + +namespace { + +class MockSyncWebSocket3 : public SyncWebSocket { + public: + MockSyncWebSocket3() {} + virtual ~MockSyncWebSocket3() {} + + virtual bool Connect(const GURL& url) OVERRIDE { + return true; + } + + virtual bool Send(const std::string& message) OVERRIDE { + return false; + } + + virtual bool ReceiveNextMessage(std::string* message) OVERRIDE { + EXPECT_TRUE(false); + return false; + } +}; + +} // namespace + +TEST(DevToolsClientImpl, SendCommandSendFails) { + SyncWebSocketFactory factory = + base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket3>); + DevToolsClientImpl client(factory, "http://url"); + base::DictionaryValue params; + ASSERT_TRUE(client.SendCommand("method", params).IsError()); +} + +namespace { + +class MockSyncWebSocket4 : public SyncWebSocket { + public: + MockSyncWebSocket4() {} + virtual ~MockSyncWebSocket4() {} + + virtual bool Connect(const GURL& url) OVERRIDE { + return true; + } + + virtual bool Send(const std::string& message) OVERRIDE { + return true; + } + + virtual bool ReceiveNextMessage(std::string* message) OVERRIDE { + return false; + } +}; + +} // namespace + +TEST(DevToolsClientImpl, SendCommandReceiveNextMessageFails) { + SyncWebSocketFactory factory = + base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket4>); + DevToolsClientImpl client(factory, "http://url"); + base::DictionaryValue params; + ASSERT_TRUE(client.SendCommand("method", params).IsError()); +} + +namespace { + +class MockSyncWebSocket5 : public SyncWebSocket { + public: + explicit MockSyncWebSocket5(const std::list<std::string> messages) + : messages_(messages) {} + virtual ~MockSyncWebSocket5() {} + + virtual bool Connect(const GURL& url) OVERRIDE { + return true; + } + + virtual bool Send(const std::string& message) OVERRIDE { + return true; + } + + virtual bool ReceiveNextMessage(std::string* message) OVERRIDE { + if (messages_.empty()) + return false; + *message = messages_.front(); + messages_.pop_front(); + return true; + } + + private: + std::list<std::string> messages_; +}; + +scoped_ptr<SyncWebSocket> CreateMockSyncWebSocket5( + const std::list<std::string>& messages) { + return scoped_ptr<SyncWebSocket>(new MockSyncWebSocket5(messages)); +} + +} // namespace + +TEST(DevToolsClientImpl, SendCommandBadResponses) { + std::list<std::string> msgs; + msgs.push_back(""); + msgs.push_back("{}"); + msgs.push_back("{\"id\":false}"); + msgs.push_back("{\"id\":1000}"); + msgs.push_back("{\"id\":5,\"error\":{\"key\":\"whoops\"}}"); + SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket5, msgs); + DevToolsClientImpl client(factory, "http://url"); + base::DictionaryValue params; + for (size_t i = 0; i < msgs.size() - 1; ++i) { + ASSERT_TRUE(client.SendCommand("method", params).IsError()); + } + Status status = client.SendCommand("method", params); + ASSERT_TRUE(status.IsError()); + ASSERT_NE(std::string::npos, status.message().find("whoops")); +} + +TEST(DevToolsClientImpl, SendCommandAndGetResultBadResults) { + std::list<std::string> msgs; + msgs.push_back("{\"id\":1}"); + msgs.push_back("{\"id\":2,\"result\":1}"); + msgs.push_back("{\"id\":3,\"error\":{\"key\":\"whoops\"},\"result\":{}}"); + SyncWebSocketFactory factory = base::Bind(&CreateMockSyncWebSocket5, msgs); + DevToolsClientImpl client(factory, "http://url"); + scoped_ptr<base::DictionaryValue> result; + base::DictionaryValue params; + for (size_t i = 0; i < msgs.size() - 1; ++i) { + Status status = client.SendCommandAndGetResult("method", params, &result); + ASSERT_TRUE(status.IsError()); + ASSERT_FALSE(result); + } + Status status = client.SendCommand("method", params); + ASSERT_TRUE(status.IsError()); + ASSERT_NE(std::string::npos, status.message().find("whoops")); +} + +namespace { + +class MockSyncWebSocket6 : public SyncWebSocket { + public: + MockSyncWebSocket6() : connected_(false), id_(-1) {} + virtual ~MockSyncWebSocket6() {} + + virtual bool Connect(const GURL& url) OVERRIDE { + EXPECT_FALSE(connected_); + connected_ = true; + return true; + } + + virtual bool Send(const std::string& message) OVERRIDE { + scoped_ptr<base::Value> value(base::JSONReader::Read(message)); + base::DictionaryValue* dict = NULL; + EXPECT_TRUE(value->GetAsDictionary(&dict)); + if (!dict) + return false; + EXPECT_TRUE(dict->GetInteger("id", &id_)); + return true; + } + + virtual bool ReceiveNextMessage(std::string* message) OVERRIDE { + base::DictionaryValue response; + response.SetInteger("id", id_); + base::JSONWriter::Write(&response, message); + return true; + } + + private: + bool connected_; + int id_; +}; + +} // namespace + +TEST(DevToolsClientImpl, SendCommandOnlyConnectsOnce) { + SyncWebSocketFactory factory = + base::Bind(&CreateMockSyncWebSocket<MockSyncWebSocket6>); + DevToolsClientImpl client(factory, "http://url"); + base::DictionaryValue params; + ASSERT_TRUE(client.SendCommand("method", params).IsOk()); + ASSERT_TRUE(client.SendCommand("method", params).IsOk()); +} diff --git a/chrome/test/chromedriver/net/sync_websocket.h b/chrome/test/chromedriver/net/sync_websocket.h index ad5cf5d..060ea4d 100644 --- a/chrome/test/chromedriver/net/sync_websocket.h +++ b/chrome/test/chromedriver/net/sync_websocket.h @@ -5,107 +5,24 @@ #ifndef CHROME_TEST_CHROMEDRIVER_NET_SYNC_WEBSOCKET_H_ #define CHROME_TEST_CHROMEDRIVER_NET_SYNC_WEBSOCKET_H_ -#include <list> #include <string> -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/condition_variable.h" -#include "base/synchronization/lock.h" -#include "chrome/test/chromedriver/net/websocket.h" -#include "net/base/completion_callback.h" -#include "net/socket_stream/socket_stream.h" - -namespace base { -class WaitableEvent; -} - -namespace net { -class URLRequestContextGetter; -} - class GURL; // Proxy for using a WebSocket running on a background thread synchronously. class SyncWebSocket { public: - explicit SyncWebSocket(net::URLRequestContextGetter* context_getter); - virtual ~SyncWebSocket(); + virtual ~SyncWebSocket() {} // Connects to the WebSocket server. Returns true on success. - bool Connect(const GURL& url); + virtual bool Connect(const GURL& url) = 0; // Sends message. Returns true on success. - bool Send(const std::string& message); + virtual bool Send(const std::string& message) = 0; // Receives next message. Blocks until at least one message is received or // the socket is closed. Returns true on success and modifies |message|. - bool ReceiveNextMessage(std::string* message); - - private: - struct CoreTraits; - class Core : public WebSocketListener, - public base::RefCountedThreadSafe<Core, CoreTraits> { - public: - explicit Core(net::URLRequestContextGetter* context_getter); - - bool Connect(const GURL& url); - - bool Send(const std::string& message); - - bool ReceiveNextMessage(std::string* message); - - // Overriden from WebSocketListener: - virtual void OnMessageReceived(const std::string& message) OVERRIDE; - virtual void OnClose() OVERRIDE; - - private: - friend class base::RefCountedThreadSafe<Core, CoreTraits>; - friend class base::DeleteHelper<Core>; - friend struct CoreTraits; - - virtual ~Core(); - - void ConnectOnIO(const GURL& url, - bool* success, - base::WaitableEvent* event); - void OnConnectCompletedOnIO(bool* connected, - base::WaitableEvent* event, - int error); - void SendOnIO(const std::string& message, - bool* result, - base::WaitableEvent* event); - - // OnDestruct is meant to ensure deletion on the IO thread. - void OnDestruct() const; - - scoped_refptr<net::URLRequestContextGetter> context_getter_; - - // Only accessed on IO thread. - scoped_ptr<WebSocket> socket_; - - base::Lock lock_; - - // Protected by |lock_|. - bool closed_; - - // Protected by |lock_|. - std::list<std::string> received_queue_; - - // Protected by |lock_|. - // Signaled when the socket closes or a message is received. - base::ConditionVariable on_update_event_; - }; - - scoped_refptr<Core> core_; -}; - -struct SyncWebSocket::CoreTraits { - static void Destruct(const SyncWebSocket::Core* core) { - core->OnDestruct(); - } + virtual bool ReceiveNextMessage(std::string* message) = 0; }; #endif // CHROME_TEST_CHROMEDRIVER_NET_SYNC_WEBSOCKET_H_ diff --git a/chrome/test/chromedriver/net/sync_websocket_factory.cc b/chrome/test/chromedriver/net/sync_websocket_factory.cc new file mode 100644 index 0000000..8759cbb --- /dev/null +++ b/chrome/test/chromedriver/net/sync_websocket_factory.cc @@ -0,0 +1,24 @@ +// 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/net/sync_websocket_factory.h" + +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "chrome/test/chromedriver/net/sync_websocket_impl.h" +#include "chrome/test/chromedriver/net/url_request_context_getter.h" + +namespace { + +scoped_ptr<SyncWebSocket> CreateSyncWebSocket( + scoped_refptr<URLRequestContextGetter> context_getter) { + return scoped_ptr<SyncWebSocket>(new SyncWebSocketImpl(context_getter)); +} + +} // namespace + +SyncWebSocketFactory CreateSyncWebSocketFactory( + URLRequestContextGetter* getter) { + return base::Bind(&CreateSyncWebSocket, make_scoped_refptr(getter)); +} diff --git a/chrome/test/chromedriver/net/sync_websocket_factory.h b/chrome/test/chromedriver/net/sync_websocket_factory.h new file mode 100644 index 0000000..9b9f0de --- /dev/null +++ b/chrome/test/chromedriver/net/sync_websocket_factory.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_SYNC_WEBSOCKET_FACTORY_H_ +#define CHROME_TEST_CHROMEDRIVER_NET_SYNC_WEBSOCKET_FACTORY_H_ + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" + +class SyncWebSocket; +class URLRequestContextGetter; + +typedef base::Callback<scoped_ptr<SyncWebSocket>()> SyncWebSocketFactory; + +SyncWebSocketFactory CreateSyncWebSocketFactory( + URLRequestContextGetter* getter); + +#endif // CHROME_TEST_CHROMEDRIVER_NET_SYNC_WEBSOCKET_FACTORY_H_ diff --git a/chrome/test/chromedriver/net/sync_websocket.cc b/chrome/test/chromedriver/net/sync_websocket_impl.cc index dadf16c..e1150cb 100644 --- a/chrome/test/chromedriver/net/sync_websocket.cc +++ b/chrome/test/chromedriver/net/sync_websocket_impl.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/test/chromedriver/net/sync_websocket.h" +#include "chrome/test/chromedriver/net/sync_websocket_impl.h" #include "base/bind.h" #include "base/callback.h" @@ -13,52 +13,52 @@ #include "net/base/net_errors.h" #include "net/url_request/url_request_context_getter.h" -SyncWebSocket::SyncWebSocket( +SyncWebSocketImpl::SyncWebSocketImpl( net::URLRequestContextGetter* context_getter) : core_(new Core(context_getter)) {} -SyncWebSocket::~SyncWebSocket() {} +SyncWebSocketImpl::~SyncWebSocketImpl() {} -bool SyncWebSocket::Connect(const GURL& url) { +bool SyncWebSocketImpl::Connect(const GURL& url) { return core_->Connect(url); } -bool SyncWebSocket::Send(const std::string& message) { +bool SyncWebSocketImpl::Send(const std::string& message) { return core_->Send(message); } -bool SyncWebSocket::ReceiveNextMessage(std::string* message) { +bool SyncWebSocketImpl::ReceiveNextMessage(std::string* message) { return core_->ReceiveNextMessage(message); } -SyncWebSocket::Core::Core(net::URLRequestContextGetter* context_getter) +SyncWebSocketImpl::Core::Core(net::URLRequestContextGetter* context_getter) : context_getter_(context_getter), closed_(false), on_update_event_(&lock_) {} -bool SyncWebSocket::Core::Connect(const GURL& url) { +bool SyncWebSocketImpl::Core::Connect(const GURL& url) { bool success = false; base::WaitableEvent event(false, false); context_getter_->GetNetworkTaskRunner()->PostTask( FROM_HERE, - base::Bind(&SyncWebSocket::Core::ConnectOnIO, + base::Bind(&SyncWebSocketImpl::Core::ConnectOnIO, this, url, &success, &event)); event.Wait(); return success; } -bool SyncWebSocket::Core::Send(const std::string& message) { +bool SyncWebSocketImpl::Core::Send(const std::string& message) { bool success = false; base::WaitableEvent event(false, false); context_getter_->GetNetworkTaskRunner()->PostTask( FROM_HERE, - base::Bind(&SyncWebSocket::Core::SendOnIO, + base::Bind(&SyncWebSocketImpl::Core::SendOnIO, this, message, &success, &event)); event.Wait(); return success; } -bool SyncWebSocket::Core::ReceiveNextMessage(std::string* message) { +bool SyncWebSocketImpl::Core::ReceiveNextMessage(std::string* message) { base::AutoLock lock(lock_); while (received_queue_.empty() && !closed_) on_update_event_.Wait(); if (closed_) @@ -68,31 +68,31 @@ bool SyncWebSocket::Core::ReceiveNextMessage(std::string* message) { return true; } -void SyncWebSocket::Core::OnMessageReceived(const std::string& message) { +void SyncWebSocketImpl::Core::OnMessageReceived(const std::string& message) { base::AutoLock lock(lock_); received_queue_.push_back(message); on_update_event_.Signal(); } -void SyncWebSocket::Core::OnClose() { +void SyncWebSocketImpl::Core::OnClose() { base::AutoLock lock(lock_); closed_ = true; on_update_event_.Signal(); } -SyncWebSocket::Core::~Core() { } +SyncWebSocketImpl::Core::~Core() { } -void SyncWebSocket::Core::ConnectOnIO( +void SyncWebSocketImpl::Core::ConnectOnIO( const GURL& url, bool* success, base::WaitableEvent* event) { socket_.reset(new WebSocket(context_getter_, url, this)); socket_->Connect(base::Bind( - &SyncWebSocket::Core::OnConnectCompletedOnIO, + &SyncWebSocketImpl::Core::OnConnectCompletedOnIO, this, success, event)); } -void SyncWebSocket::Core::OnConnectCompletedOnIO( +void SyncWebSocketImpl::Core::OnConnectCompletedOnIO( bool* success, base::WaitableEvent* event, int error) { @@ -100,7 +100,7 @@ void SyncWebSocket::Core::OnConnectCompletedOnIO( event->Signal(); } -void SyncWebSocket::Core::SendOnIO( +void SyncWebSocketImpl::Core::SendOnIO( const std::string& message, bool* success, base::WaitableEvent* event) { @@ -108,7 +108,7 @@ void SyncWebSocket::Core::SendOnIO( event->Signal(); } -void SyncWebSocket::Core::OnDestruct() const { +void SyncWebSocketImpl::Core::OnDestruct() const { scoped_refptr<base::SingleThreadTaskRunner> network_task_runner = context_getter_->GetNetworkTaskRunner(); if (network_task_runner->BelongsToCurrentThread()) diff --git a/chrome/test/chromedriver/net/sync_websocket_impl.h b/chrome/test/chromedriver/net/sync_websocket_impl.h new file mode 100644 index 0000000..cca8857 --- /dev/null +++ b/chrome/test/chromedriver/net/sync_websocket_impl.h @@ -0,0 +1,106 @@ +// 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_SYNC_WEBSOCKET_IMPL_H_ +#define CHROME_TEST_CHROMEDRIVER_NET_SYNC_WEBSOCKET_IMPL_H_ + +#include <list> +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/condition_variable.h" +#include "base/synchronization/lock.h" +#include "chrome/test/chromedriver/net/sync_websocket.h" +#include "chrome/test/chromedriver/net/websocket.h" +#include "net/base/completion_callback.h" +#include "net/socket_stream/socket_stream.h" + +namespace base { +class WaitableEvent; +} + +namespace net { +class URLRequestContextGetter; +} + +class GURL; + +class SyncWebSocketImpl : public SyncWebSocket { + public: + explicit SyncWebSocketImpl(net::URLRequestContextGetter* context_getter); + virtual ~SyncWebSocketImpl(); + + // Overridden from SyncWebSocket: + virtual bool Connect(const GURL& url) OVERRIDE; + virtual bool Send(const std::string& message) OVERRIDE; + virtual bool ReceiveNextMessage(std::string* message) OVERRIDE; + + private: + struct CoreTraits; + class Core : public WebSocketListener, + public base::RefCountedThreadSafe<Core, CoreTraits> { + public: + explicit Core(net::URLRequestContextGetter* context_getter); + + bool Connect(const GURL& url); + + bool Send(const std::string& message); + + bool ReceiveNextMessage(std::string* message); + + // Overriden from WebSocketListener: + virtual void OnMessageReceived(const std::string& message) OVERRIDE; + virtual void OnClose() OVERRIDE; + + private: + friend class base::RefCountedThreadSafe<Core, CoreTraits>; + friend class base::DeleteHelper<Core>; + friend struct CoreTraits; + + virtual ~Core(); + + void ConnectOnIO(const GURL& url, + bool* success, + base::WaitableEvent* event); + void OnConnectCompletedOnIO(bool* connected, + base::WaitableEvent* event, + int error); + void SendOnIO(const std::string& message, + bool* result, + base::WaitableEvent* event); + + // OnDestruct is meant to ensure deletion on the IO thread. + void OnDestruct() const; + + scoped_refptr<net::URLRequestContextGetter> context_getter_; + + // Only accessed on IO thread. + scoped_ptr<WebSocket> socket_; + + base::Lock lock_; + + // Protected by |lock_|. + bool closed_; + + // Protected by |lock_|. + std::list<std::string> received_queue_; + + // Protected by |lock_|. + // Signaled when the socket closes or a message is received. + base::ConditionVariable on_update_event_; + }; + + scoped_refptr<Core> core_; +}; + +struct SyncWebSocketImpl::CoreTraits { + static void Destruct(const SyncWebSocketImpl::Core* core) { + core->OnDestruct(); + } +}; + +#endif // CHROME_TEST_CHROMEDRIVER_NET_SYNC_WEBSOCKET_IMPL_H_ diff --git a/chrome/test/chromedriver/net/sync_websocket_unittest.cc b/chrome/test/chromedriver/net/sync_websocket_impl_unittest.cc index 5bb2b53..928d9fe1 100644 --- a/chrome/test/chromedriver/net/sync_websocket_unittest.cc +++ b/chrome/test/chromedriver/net/sync_websocket_impl_unittest.cc @@ -14,7 +14,7 @@ #include "base/stringprintf.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" -#include "chrome/test/chromedriver/net/sync_websocket.h" +#include "chrome/test/chromedriver/net/sync_websocket_impl.h" #include "chrome/test/chromedriver/net/url_request_context_getter.h" #include "googleurl/src/gurl.h" #include "net/base/ip_endpoint.h" @@ -27,10 +27,10 @@ namespace { -class SyncWebSocketTest : public testing::Test, - public net::HttpServer::Delegate { +class SyncWebSocketImplTest : public testing::Test, + public net::HttpServer::Delegate { public: - SyncWebSocketTest() + SyncWebSocketImplTest() : io_thread_("io"), close_on_receive_(false) { base::Thread::Options options(MessageLoop::TYPE_IO, 0); @@ -40,16 +40,16 @@ class SyncWebSocketTest : public testing::Test, base::WaitableEvent event(false, false); io_thread_.message_loop_proxy()->PostTask( FROM_HERE, - base::Bind(&SyncWebSocketTest::InitOnIO, + base::Bind(&SyncWebSocketImplTest::InitOnIO, base::Unretained(this), &event)); event.Wait(); } - virtual ~SyncWebSocketTest() { + virtual ~SyncWebSocketImplTest() { base::WaitableEvent event(false, false); io_thread_.message_loop_proxy()->PostTask( FROM_HERE, - base::Bind(&SyncWebSocketTest::DestroyServerOnIO, + base::Bind(&SyncWebSocketImplTest::DestroyServerOnIO, base::Unretained(this), &event)); event.Wait(); } @@ -100,22 +100,22 @@ class SyncWebSocketTest : public testing::Test, } // namespace -TEST_F(SyncWebSocketTest, CreateDestroy) { - SyncWebSocket sock(context_getter_); +TEST_F(SyncWebSocketImplTest, CreateDestroy) { + SyncWebSocketImpl sock(context_getter_); } -TEST_F(SyncWebSocketTest, Connect) { - SyncWebSocket sock(context_getter_); +TEST_F(SyncWebSocketImplTest, Connect) { + SyncWebSocketImpl sock(context_getter_); ASSERT_TRUE(sock.Connect(server_url_)); } -TEST_F(SyncWebSocketTest, ConnectFail) { - SyncWebSocket sock(context_getter_); +TEST_F(SyncWebSocketImplTest, ConnectFail) { + SyncWebSocketImpl sock(context_getter_); ASSERT_FALSE(sock.Connect(GURL("ws://127.0.0.1:33333"))); } -TEST_F(SyncWebSocketTest, SendReceive) { - SyncWebSocket sock(context_getter_); +TEST_F(SyncWebSocketImplTest, SendReceive) { + SyncWebSocketImpl sock(context_getter_); ASSERT_TRUE(sock.Connect(server_url_)); ASSERT_TRUE(sock.Send("hi")); std::string message; @@ -123,8 +123,8 @@ TEST_F(SyncWebSocketTest, SendReceive) { ASSERT_STREQ("hi", message.c_str()); } -TEST_F(SyncWebSocketTest, SendReceiveLarge) { - SyncWebSocket sock(context_getter_); +TEST_F(SyncWebSocketImplTest, SendReceiveLarge) { + SyncWebSocketImpl sock(context_getter_); ASSERT_TRUE(sock.Connect(server_url_)); // Sends/receives 200kb. For some reason pushing this above 240kb on my // machine results in receiving no data back from the http server. @@ -136,8 +136,8 @@ TEST_F(SyncWebSocketTest, SendReceiveLarge) { ASSERT_EQ(wrote_message, message); } -TEST_F(SyncWebSocketTest, SendReceiveMany) { - SyncWebSocket sock(context_getter_); +TEST_F(SyncWebSocketImplTest, SendReceiveMany) { + SyncWebSocketImpl sock(context_getter_); ASSERT_TRUE(sock.Connect(server_url_)); ASSERT_TRUE(sock.Send("1")); ASSERT_TRUE(sock.Send("2")); @@ -151,9 +151,9 @@ TEST_F(SyncWebSocketTest, SendReceiveMany) { ASSERT_STREQ("3", message.c_str()); } -TEST_F(SyncWebSocketTest, CloseOnReceive) { +TEST_F(SyncWebSocketImplTest, CloseOnReceive) { close_on_receive_ = true; - SyncWebSocket sock(context_getter_); + SyncWebSocketImpl sock(context_getter_); ASSERT_TRUE(sock.Connect(server_url_)); ASSERT_TRUE(sock.Send("1")); std::string message; @@ -161,13 +161,13 @@ TEST_F(SyncWebSocketTest, CloseOnReceive) { ASSERT_STREQ("", message.c_str()); } -TEST_F(SyncWebSocketTest, CloseOnSend) { - SyncWebSocket sock(context_getter_); +TEST_F(SyncWebSocketImplTest, CloseOnSend) { + SyncWebSocketImpl sock(context_getter_); ASSERT_TRUE(sock.Connect(server_url_)); base::WaitableEvent event(false, false); io_thread_.message_loop_proxy()->PostTask( FROM_HERE, - base::Bind(&SyncWebSocketTest::DestroyServerOnIO, + base::Bind(&SyncWebSocketImplTest::DestroyServerOnIO, base::Unretained(this), &event)); event.Wait(); ASSERT_FALSE(sock.Send("1")); |