diff options
author | miket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-16 01:18:42 +0000 |
---|---|---|
committer | miket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-16 01:18:42 +0000 |
commit | fc44a4bef55e6c8bfb108bbc9370e405a94b7ab7 (patch) | |
tree | a083b654e34c3451715997a14859e20ac402432d /chrome/browser/extensions/api | |
parent | 2c0e629552d785a5300442964e1b4bb8623e54e5 (diff) | |
download | chromium_src-fc44a4bef55e6c8bfb108bbc9370e405a94b7ab7.zip chromium_src-fc44a4bef55e6c8bfb108bbc9370e405a94b7ab7.tar.gz chromium_src-fc44a4bef55e6c8bfb108bbc9370e405a94b7ab7.tar.bz2 |
Refactor UDP socket API to facilitate testing, then test.
This is the second half of the task started by http://codereview.chromium.org/8896013/. Now it's complete.
BUG=106802
TEST=lots added.
Review URL: http://codereview.chromium.org/8915023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@114738 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions/api')
10 files changed, 459 insertions, 182 deletions
diff --git a/chrome/browser/extensions/api/socket/socket.cc b/chrome/browser/extensions/api/socket/socket.cc new file mode 100644 index 0000000..71cb4a8 --- /dev/null +++ b/chrome/browser/extensions/api/socket/socket.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/socket/socket.h" + +#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" +#include "net/base/io_buffer.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/udp/datagram_socket.h" + +namespace extensions { + +Socket::Socket(net::DatagramClientSocket* datagram_client_socket, + SocketEventNotifier* event_notifier) + : datagram_client_socket_(datagram_client_socket), + event_notifier_(event_notifier), + is_connected_(false) {} + +Socket::~Socket() { + if (is_connected_) { + Close(); + } +} + +bool Socket::Connect(const net::IPEndPoint& ip_end_point) { + is_connected_ = datagram_client_socket_->Connect(ip_end_point) == net::OK; + return is_connected_; +} + +void Socket::Close() { + is_connected_ = false; + datagram_client_socket_->Close(); +} + +void Socket::OnWriteComplete(int result) { + event_notifier_->OnEvent(SOCKET_EVENT_WRITE_COMPLETE, result); +} + +int Socket::Write(const std::string message) { + int length = message.length(); + scoped_refptr<net::StringIOBuffer> io_buffer( + new net::StringIOBuffer(message)); + scoped_refptr<net::DrainableIOBuffer> buffer( + new net::DrainableIOBuffer(io_buffer, length)); + + int bytes_sent = 0; + while (buffer->BytesRemaining()) { + int rv = datagram_client_socket_->Write( + buffer, buffer->BytesRemaining(), + base::Bind(&Socket::OnWriteComplete, base::Unretained(this))); + if (rv <= 0) { + // We pass all errors, including ERROR_IO_PENDING, back to the caller. + return bytes_sent > 0 ? bytes_sent : rv; + } + bytes_sent += rv; + buffer->DidConsume(rv); + } + return bytes_sent; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/socket/socket.h b/chrome/browser/extensions/api/socket/socket.h new file mode 100644 index 0000000..c358cb3 --- /dev/null +++ b/chrome/browser/extensions/api/socket/socket.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_H_ +#define CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_H_ +#pragma once + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" +#include "net/udp/datagram_client_socket.h" + +namespace net { +class IPEndPoint; +} + +namespace extensions { + +// A Socket wraps a low-level socket and includes housekeeping information that +// we need to manage it in the context of an extension. +class Socket { + public: + Socket(net::DatagramClientSocket* datagram_client_socket, + SocketEventNotifier* event_notifier); + virtual ~Socket(); + + // Returns true if successful. + virtual bool Connect(const net::IPEndPoint& ip_end_point); + virtual void Close(); + + // Returns the number of bytes successfully written, or a negative error + // code. Note that ERR_IO_PENDING means that the operation blocked, in which + // case |event_notifier| will eventually be called with the final result + // (again, either a nonnegative number of bytes written, or a negative + // error). + virtual int Write(const std::string message); + + private: + scoped_ptr<net::DatagramClientSocket> datagram_client_socket_; + scoped_ptr<SocketEventNotifier> event_notifier_; + bool is_connected_; + + void OnWriteComplete(int result); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_H_ diff --git a/chrome/browser/extensions/api/socket/socket_api.cc b/chrome/browser/extensions/api/socket/socket_api.cc index 4a54978..597ed39 100644 --- a/chrome/browser/extensions/api/socket/socket_api.cc +++ b/chrome/browser/extensions/api/socket/socket_api.cc @@ -7,10 +7,10 @@ #include "base/bind.h" #include "base/values.h" #include "chrome/browser/extensions/api/socket/socket_api_controller.h" +#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/browser_thread.h" using content::BrowserThread; @@ -81,9 +81,12 @@ bool SocketCreateFunction::Prepare() { } void SocketCreateFunction::Work() { + SocketEventNotifier* event_notifier(new SocketEventNotifier( + profile()->GetExtensionEventRouter(), profile(), extension_id(), + src_id_, source_url())); + int socket_id = controller()->CreateUdp(event_notifier); DictionaryValue* result = new DictionaryValue(); - int socket_id = controller()->CreateUdp(profile(), extension_id(), src_id_, - source_url()); + result->SetInteger(kSocketIdKey, socket_id); result_.reset(result); diff --git a/chrome/browser/extensions/api/socket/socket_api_controller.cc b/chrome/browser/extensions/api/socket/socket_api_controller.cc index 8d00c14..3fec5c4 100644 --- a/chrome/browser/extensions/api/socket/socket_api_controller.cc +++ b/chrome/browser/extensions/api/socket/socket_api_controller.cc @@ -2,167 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/json/json_writer.h" -#include "base/stl_util.h" -#include "base/values.h" #include "chrome/browser/extensions/api/socket/socket_api_controller.h" -#include "chrome/browser/extensions/extension_event_router.h" -#include "chrome/browser/profiles/profile.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" + +#include "chrome/browser/extensions/api/socket/socket.h" +#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" #include "net/base/rand_callback.h" #include "net/udp/datagram_socket.h" #include "net/udp/udp_client_socket.h" -#include "net/udp/udp_socket.h" - -using namespace net; - -namespace events { -const char kOnEvent[] = "experimental.socket.onEvent"; -}; // namespace events namespace extensions { -enum SocketEventType { - SOCKET_EVENT_WRITE_COMPLETE -}; - -const char kEventTypeKey[] = "type"; -const char kEventTypeWriteComplete[] = "writeComplete"; - -const char kSrcIdKey[] = "srcId"; -const char kIsFinalEventKey[] = "isFinalEvent"; - -const char kResultCodeKey[] = "resultCode"; - -std::string SocketEventTypeToString(SocketEventType event_type) { - switch (event_type) { - case SOCKET_EVENT_WRITE_COMPLETE: - return kEventTypeWriteComplete; - default: - NOTREACHED(); - return std::string(); - } -} - -// A Socket wraps a low-level socket and includes housekeeping information that -// we need to manage it in the context of an extension. -class Socket { - public: - Socket(Profile* profile, const std::string& src_extension_id, int src_id, - const GURL& src_url); - ~Socket(); - - bool Connect(const net::IPEndPoint& ip_end_point); - void Close(); - int Write(const std::string message); - - void OnSocketEvent(SocketEventType event_type, int result_code); - - private: - int id_; - - // This group of variables lets us send events back to the creator extension. - Profile* profile_; - std::string src_extension_id_; - int src_id_; - GURL src_url_; - - scoped_ptr<net::UDPClientSocket> udp_client_socket_; - bool is_connected_; - - void OnWriteComplete(int result); -}; - -Socket::Socket(Profile* profile, const std::string& src_extension_id, - int src_id, const GURL& src_url) - : id_(-1), - profile_(profile), - src_extension_id_(src_extension_id), - src_id_(src_id), - src_url_(src_url), - udp_client_socket_(new UDPClientSocket( - DatagramSocket::DEFAULT_BIND, - RandIntCallback(), - NULL, - NetLog::Source())), - is_connected_(false) {} - -Socket::~Socket() { - if (is_connected_) { - Close(); - } -} - -void Socket::OnSocketEvent(SocketEventType event_type, int result_code) { - // Do we have a destination for this event? - if (src_id_ < 0) - return; - - std::string event_type_string = SocketEventTypeToString(event_type); - - ListValue args; - DictionaryValue* event = new DictionaryValue(); - event->SetString(kEventTypeKey, event_type_string); - event->SetInteger(kSrcIdKey, src_id_); - - // TODO(miket): Signal that it's OK to clean up onEvent listeners. This is - // the framework we'll use, but we need to start using it. - event->SetBoolean(kIsFinalEventKey, false); - - if (event_type == SOCKET_EVENT_WRITE_COMPLETE) { - event->SetInteger(kResultCodeKey, result_code); - } - - args.Set(0, event); - std::string json_args; - base::JSONWriter::Write(&args, false, &json_args); - - profile_->GetExtensionEventRouter()->DispatchEventToExtension( - src_extension_id_, - events::kOnEvent, - json_args, - profile_, - src_url_); -} - -bool Socket::Connect(const net::IPEndPoint& ip_end_point) { - is_connected_ = udp_client_socket_->Connect(ip_end_point) == net::OK; - return is_connected_; -} - -void Socket::Close() { - is_connected_ = false; - udp_client_socket_->Close(); -} - -void Socket::OnWriteComplete(int result) { - OnSocketEvent(SOCKET_EVENT_WRITE_COMPLETE, result); -} - -int Socket::Write(const std::string message) { - int length = message.length(); - scoped_refptr<StringIOBuffer> io_buffer(new StringIOBuffer(message)); - scoped_refptr<DrainableIOBuffer> buffer( - new DrainableIOBuffer(io_buffer, length)); - - int bytes_sent = 0; - while (buffer->BytesRemaining()) { - int rv = udp_client_socket_->Write( - buffer, buffer->BytesRemaining(), - base::Bind(&Socket::OnWriteComplete, base::Unretained(this))); - if (rv <= 0) { - // We pass all errors, including ERROR_IO_PENDING, back to the caller. - return bytes_sent > 0 ? bytes_sent : rv; - } - bytes_sent += rv; - buffer->DidConsume(rv); - } - return bytes_sent; -} - -SocketController::SocketController() : next_socket_id_(1) { -} +SocketController::SocketController() : next_socket_id_(1) {} SocketController::~SocketController() {} @@ -175,12 +25,12 @@ Socket* SocketController::GetSocket(int socket_id) { return NULL; } -int SocketController::CreateUdp(Profile* profile, - const std::string& extension_id, - int src_id, - const GURL& src_url) { - linked_ptr<Socket> socket(new Socket(profile, extension_id, src_id, - src_url)); +int SocketController::CreateUdp(SocketEventNotifier* event_notifier) { + linked_ptr<Socket> socket(new Socket( + new net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND, + net::RandIntCallback(), + NULL, + net::NetLog::Source()), event_notifier)); CHECK(socket.get()); socket_map_[next_socket_id_] = socket; return next_socket_id_++; @@ -200,7 +50,7 @@ bool SocketController::DestroyUdp(int socket_id) { // leave experimental without DNS resolution. // // static -bool SocketController::CreateIPEndPoint(const std::string address, int port, +bool SocketController::CreateIPEndPoint(const std::string& address, int port, net::IPEndPoint* ip_end_point) { net::IPAddressNumber ip_number; bool rv = net::ParseIPLiteralToNumber(address, &ip_number); @@ -210,7 +60,7 @@ bool SocketController::CreateIPEndPoint(const std::string address, int port, return true; } -bool SocketController::ConnectUdp(int socket_id, const std::string address, +bool SocketController::ConnectUdp(int socket_id, const std::string& address, int port) { Socket* socket = GetSocket(socket_id); if (!socket) @@ -227,7 +77,7 @@ void SocketController::CloseUdp(int socket_id) { socket->Close(); } -int SocketController::WriteUdp(int socket_id, const std::string message) { +int SocketController::WriteUdp(int socket_id, const std::string& message) { Socket* socket = GetSocket(socket_id); if (!socket) { return -1; diff --git a/chrome/browser/extensions/api/socket/socket_api_controller.h b/chrome/browser/extensions/api/socket/socket_api_controller.h index d2ae263..f35c3dc 100644 --- a/chrome/browser/extensions/api/socket/socket_api_controller.h +++ b/chrome/browser/extensions/api/socket/socket_api_controller.h @@ -11,18 +11,10 @@ #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" -#include "googleurl/src/gurl.h" -#include "net/base/completion_callback.h" class Profile; -namespace base { -class ListValue; -class Value; -} - namespace net { -class UDPClientSocket; class IPEndPoint; } @@ -34,6 +26,7 @@ namespace extensions { extern const char kSrcIdKey[]; class Socket; +class SocketEventNotifier; // SocketController keeps track of a collection of Sockets and provides a // convenient set of methods to manipulate them. @@ -48,21 +41,22 @@ class SocketController { // TODO(miket): aa's suggestion to track lifetime of callbacks associated // with each socket, which will then let us clean up when we go out of scope // rather than requiring that the app developer remember to call Destroy. - int CreateUdp(Profile* profile, const std::string& extension_id, int src_id, - const GURL& src_url); + // + // Takes ownership of |event_notifier|. + int CreateUdp(SocketEventNotifier* event_notifier); bool DestroyUdp(int socket_id); // Connect, Close, Read, and Write map to the equivalent methods in // UDPClientSocket. // // TODO(miket): Implement Read. - bool ConnectUdp(int socket_id, const std::string address, int port); + bool ConnectUdp(int socket_id, const std::string& address, int port); void CloseUdp(int socket_id); - int WriteUdp(int socket_id, const std::string msg); + int WriteUdp(int socket_id, const std::string& message); // Converts a string IP address and integer port into a format that // UDPClientSocket can deal with. Public so test harness can use it. - static bool CreateIPEndPoint(const std::string address, int port, + static bool CreateIPEndPoint(const std::string& address, int port, net::IPEndPoint* ip_end_point); private: diff --git a/chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc b/chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc index b40a1f6..4b0ef51 100644 --- a/chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc +++ b/chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc @@ -8,6 +8,8 @@ #include "base/values.h" #include "chrome/browser/extensions/api/socket/socket_api.h" #include "chrome/browser/extensions/api/socket/socket_api_controller.h" +#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" +#include "chrome/common/extensions/extension.h" namespace extensions { @@ -21,10 +23,13 @@ TEST_F(SocketApiControllerTest, TestSocketControllerLifetime) { // Create some sockets but don't do anything with them. Profile* profile = NULL; - const std::string extension_id("xxxxxxxxx"); + std::string extension_id; + EXPECT_TRUE(Extension::GenerateId("e^(iπ)+1=0", &extension_id)); const GURL url; for (int i = 0; i < 10; ++i) { - int socket_id = controller->CreateUdp(profile, extension_id, -1, url); + SocketEventNotifier* notifier = + new SocketEventNotifier(NULL, profile, extension_id, -1, url); + int socket_id = controller->CreateUdp(notifier); ASSERT_TRUE(socket_id != 0); } @@ -33,7 +38,9 @@ TEST_F(SocketApiControllerTest, TestSocketControllerLifetime) { const int kPort = 38888; const std::string address("127.0.0.1"); for (int i = 0; i < 10; ++i) { - int socket_id = controller->CreateUdp(profile, extension_id, -1, url); + SocketEventNotifier* notifier = + new SocketEventNotifier(NULL, profile, extension_id, -1, url); + int socket_id = controller->CreateUdp(notifier); ASSERT_TRUE(socket_id != 0); ASSERT_TRUE(controller->ConnectUdp(socket_id, address, kPort)); } diff --git a/chrome/browser/extensions/api/socket/socket_event_notifier.cc b/chrome/browser/extensions/api/socket/socket_event_notifier.cc new file mode 100644 index 0000000..ae994e6 --- /dev/null +++ b/chrome/browser/extensions/api/socket/socket_event_notifier.cc @@ -0,0 +1,83 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" + +#include "base/json/json_writer.h" +#include "base/values.h" +#include "chrome/browser/extensions/extension_event_router.h" +#include "chrome/browser/profiles/profile.h" + +namespace events { +const char kOnSocketEvent[] = "experimental.socket.onEvent"; +}; // namespace events + +namespace extensions { + +const char kEventTypeKey[] = "type"; +const char kEventTypeWriteComplete[] = "writeComplete"; + +const char kSrcIdKey[] = "srcId"; +const char kIsFinalEventKey[] = "isFinalEvent"; + +const char kResultCodeKey[] = "resultCode"; + +SocketEventNotifier::SocketEventNotifier(ExtensionEventRouter* router, + Profile* profile, + const std::string& src_extension_id, + int src_id, + const GURL& src_url) + : router_(router), + profile_(profile), + src_extension_id_(src_extension_id), + src_id_(src_id), + src_url_(src_url) {} + +SocketEventNotifier::~SocketEventNotifier() {} + +void SocketEventNotifier::OnEvent(SocketEventType event_type, + int result_code) { + // Do we have a destination for this event? There will be one if a source id + // was injected by the request handler for socket.create in + // schema_generated_bindings.js, which will in turn be the case if the caller + // of socket.create provided an onEvent closure. + if (src_id_ < 0) + return; + + std::string event_type_string = SocketEventTypeToString(event_type); + + ListValue args; + DictionaryValue* event = new DictionaryValue(); + event->SetString(kEventTypeKey, event_type_string); + event->SetInteger(kSrcIdKey, src_id_); + + // TODO(miket): Signal that it's OK to clean up onEvent listeners. This is + // the framework we'll use, but we need to start using it. + event->SetBoolean(kIsFinalEventKey, false); + + if (event_type == SOCKET_EVENT_WRITE_COMPLETE) { + event->SetInteger(kResultCodeKey, result_code); + } + + args.Set(0, event); + std::string json_args; + base::JSONWriter::Write(&args, false, &json_args); + + router_->DispatchEventToExtension(src_extension_id_, events::kOnSocketEvent, + json_args, profile_, src_url_); +} + +// static +std::string SocketEventNotifier::SocketEventTypeToString( + SocketEventType event_type) { + switch (event_type) { + case SOCKET_EVENT_WRITE_COMPLETE: + return kEventTypeWriteComplete; + } + + NOTREACHED(); + return std::string(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/socket/socket_event_notifier.h b/chrome/browser/extensions/api/socket/socket_event_notifier.h new file mode 100644 index 0000000..56db2b8 --- /dev/null +++ b/chrome/browser/extensions/api/socket/socket_event_notifier.h @@ -0,0 +1,54 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_EVENT_NOTIFIER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_EVENT_NOTIFIER_H_ +#pragma once + +#include <string> + +#include "googleurl/src/gurl.h" + +class ExtensionEventRouter; +class Profile; + +namespace events { +extern const char kOnSocketEvent[]; +}; // namespace events + +namespace extensions { + +enum SocketEventType { + SOCKET_EVENT_WRITE_COMPLETE +}; + +extern const char kSrcIdKey[]; + +// Contains the data that a Socket needs to send an event back to the extension +// that instantiated it. +class SocketEventNotifier { + public: + SocketEventNotifier(ExtensionEventRouter* router, + Profile* profile, + const std::string& src_extension_id, int src_id, + const GURL& src_url); + virtual ~SocketEventNotifier(); + + virtual void OnEvent(SocketEventType event_type, int result_code); + + static std::string SocketEventTypeToString(SocketEventType event_type); + + private: + ExtensionEventRouter* router_; + Profile* profile_; + std::string src_extension_id_; + int src_id_; + GURL src_url_; + + DISALLOW_COPY_AND_ASSIGN(SocketEventNotifier); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_EVENT_NOTIFIER_H_ diff --git a/chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc b/chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc new file mode 100644 index 0000000..24e500d --- /dev/null +++ b/chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" + +#include "base/json/json_reader.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chrome/browser/extensions/extension_event_router.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/test/base/testing_profile.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::_; +using testing::SaveArg; + +namespace extensions { + +class SocketEventNotifierTest : public testing::Test { +}; + +class MockExtensionEventRouter : public ExtensionEventRouter { + public: + explicit MockExtensionEventRouter(Profile* profile) : + ExtensionEventRouter(profile) {} + + MOCK_METHOD5(DispatchEventToExtension, void(const std::string& extension_id, + const std::string& event_name, + const std::string& event_args, + Profile* source_profile, + const GURL& event_url)); + + + private: + DISALLOW_COPY_AND_ASSIGN(MockExtensionEventRouter); +}; + +class MockTestingProfile : public TestingProfile { + public: + MockTestingProfile() {} + MOCK_METHOD0(GetExtensionEventRouter, ExtensionEventRouter*()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockTestingProfile); +}; + +TEST_F(SocketEventNotifierTest, TestBasicOperation) { + MockTestingProfile profile; + scoped_ptr<MockExtensionEventRouter> mock_event_router( + new MockExtensionEventRouter(&profile)); + + std::string extension_id; + EXPECT_TRUE(Extension::GenerateId("e^(iπ)+1=0", &extension_id)); + int src_id = 42; + GURL url; + scoped_ptr<SocketEventNotifier> event_notifier( + new SocketEventNotifier(mock_event_router.get(), &profile, + extension_id, src_id, url)); + + std::string event_args; + EXPECT_CALL(*mock_event_router.get(), + DispatchEventToExtension( + extension_id, + events::kOnSocketEvent, + _, + &profile, + url)) + .Times(1) + .WillOnce(SaveArg<2>(&event_args)); + + const int result_code = 888; + event_notifier->OnEvent(SOCKET_EVENT_WRITE_COMPLETE, result_code); + + scoped_ptr<Value> result(base::JSONReader::Read(event_args, true)); + Value* value = result.get(); + ASSERT_TRUE(result.get() != NULL); + ASSERT_EQ(Value::TYPE_LIST, value->GetType()); + ListValue* list = static_cast<ListValue*>(value); + ASSERT_EQ(1u, list->GetSize()); + + DictionaryValue* info; + ASSERT_TRUE(list->GetDictionary(0, &info)); + + int tmp_src_id = 0; + ASSERT_TRUE(info->GetInteger("srcId", &tmp_src_id)); + ASSERT_EQ(src_id, tmp_src_id); + + int tmp_result_code = 0; + ASSERT_TRUE(info->GetInteger("resultCode", &tmp_result_code)); + ASSERT_EQ(result_code, tmp_result_code); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/socket/socket_unittest.cc b/chrome/browser/extensions/api/socket/socket_unittest.cc new file mode 100644 index 0000000..aecd2c7 --- /dev/null +++ b/chrome/browser/extensions/api/socket/socket_unittest.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/socket/socket.h" + +#include "base/memory/scoped_ptr.h" +#include "net/base/completion_callback.h" +#include "net/base/net_errors.h" +#include "net/base/rand_callback.h" +#include "net/udp/udp_client_socket.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::_; +using testing::DoAll; +using testing::Return; +using testing::SaveArg; + +namespace extensions { + +class SocketTest : public testing::Test { +}; + +class MockSocket : public net::UDPClientSocket { + public: + MockSocket() + : net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND, + net::RandIntCallback(), + NULL, + net::NetLog::Source()) {} + + MOCK_METHOD3(Write, int(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback)); + private: + DISALLOW_COPY_AND_ASSIGN(MockSocket); +}; + +class MockSocketEventNotifier : public SocketEventNotifier { + public: + MockSocketEventNotifier() : SocketEventNotifier(NULL, NULL, std::string(), + 0, GURL()) {} + + MOCK_METHOD2(OnEvent, void(SocketEventType event_type, int result_code)); +}; + +TEST_F(SocketTest, TestSocketWrite) { + MockSocket* udp_client_socket = new MockSocket(); + SocketEventNotifier* notifier = new MockSocketEventNotifier(); + + scoped_ptr<Socket> socket(new Socket(udp_client_socket, notifier)); + + EXPECT_CALL(*udp_client_socket, Write(_, _, _)) + .Times(1); + + socket->Write("foo"); +} + +TEST_F(SocketTest, TestSocketBlockedWrite) { + MockSocket* udp_client_socket = new MockSocket(); + MockSocketEventNotifier* notifier = new MockSocketEventNotifier(); + + scoped_ptr<Socket> socket(new Socket(udp_client_socket, notifier)); + + net::CompletionCallback callback; + EXPECT_CALL(*udp_client_socket, Write(_, _, _)) + .Times(1) + .WillOnce(testing::DoAll(SaveArg<2>(&callback), + Return(net::ERR_IO_PENDING))); + + ASSERT_EQ(net::ERR_IO_PENDING, socket->Write("foo")); + + // Good. Original call came back unable to complete. Now pretend the socket + // finished, and confirm that we passed the error back. + EXPECT_CALL(*notifier, OnEvent(SOCKET_EVENT_WRITE_COMPLETE, 42)) + .Times(1); + callback.Run(42); +} + +} // namespace extensions |