summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/api
diff options
context:
space:
mode:
authormiket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-16 01:18:42 +0000
committermiket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-16 01:18:42 +0000
commitfc44a4bef55e6c8bfb108bbc9370e405a94b7ab7 (patch)
treea083b654e34c3451715997a14859e20ac402432d /chrome/browser/extensions/api
parent2c0e629552d785a5300442964e1b4bb8623e54e5 (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/extensions/api/socket/socket.cc63
-rw-r--r--chrome/browser/extensions/api/socket/socket.h50
-rw-r--r--chrome/browser/extensions/api/socket/socket_api.cc9
-rw-r--r--chrome/browser/extensions/api/socket/socket_api_controller.cc176
-rw-r--r--chrome/browser/extensions/api/socket/socket_api_controller.h20
-rw-r--r--chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc13
-rw-r--r--chrome/browser/extensions/api/socket/socket_event_notifier.cc83
-rw-r--r--chrome/browser/extensions/api/socket/socket_event_notifier.h54
-rw-r--r--chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc94
-rw-r--r--chrome/browser/extensions/api/socket/socket_unittest.cc79
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