Index: net/socket_stream/socket_stream_unittest.cc |
diff --git a/net/socket_stream/socket_stream_unittest.cc b/net/socket_stream/socket_stream_unittest.cc |
index b0734fc5fb5b0bd7d98e04fca93560e40458993e..aed80f28ba2891c8ce191377dd0ed9e7e94dcd6f 100644 |
--- a/net/socket_stream/socket_stream_unittest.cc |
+++ b/net/socket_stream/socket_stream_unittest.cc |
@@ -181,6 +181,54 @@ class SocketStreamEventRecorder : public SocketStream::Delegate { |
DISALLOW_COPY_AND_ASSIGN(SocketStreamEventRecorder); |
}; |
+// This is used for the test OnErrorDetachDelegate. |
+class SelfDeletingDelegate : public SocketStream::Delegate { |
+ public: |
+ // |callback| must cause the test message loop to exit when called. |
+ explicit SelfDeletingDelegate(const CompletionCallback& callback) |
+ : socket_stream_(), callback_(callback) {} |
+ |
+ virtual ~SelfDeletingDelegate() {} |
+ |
+ // Call DetachDelegate(), delete |this|, then run the callback. |
+ virtual void OnError(const SocketStream* socket, int error) OVERRIDE { |
+ // callback_ will be deleted when we delete |this|, so copy it to call it |
+ // afterwards. |
+ CompletionCallback callback = callback_; |
+ socket_stream_->DetachDelegate(); |
+ delete this; |
+ callback.Run(OK); |
+ } |
+ |
+ // This can't be passed in the constructor because this object needs to be |
+ // created before SocketStream. |
+ void set_socket_stream(const scoped_refptr<SocketStream>& socket_stream) { |
+ socket_stream_ = socket_stream; |
+ EXPECT_EQ(socket_stream_->delegate(), this); |
+ } |
+ |
+ virtual void OnConnected(SocketStream* socket, int max_pending_send_allowed) |
+ OVERRIDE { |
+ ADD_FAILURE() << "OnConnected() should not be called"; |
+ } |
+ virtual void OnSentData(SocketStream* socket, int amount_sent) OVERRIDE { |
+ ADD_FAILURE() << "OnSentData() should not be called"; |
+ } |
+ virtual void OnReceivedData(SocketStream* socket, const char* data, int len) |
+ OVERRIDE { |
+ ADD_FAILURE() << "OnReceivedData() should not be called"; |
+ } |
+ virtual void OnClose(SocketStream* socket) OVERRIDE { |
+ ADD_FAILURE() << "OnClose() should not be called"; |
+ } |
+ |
+ private: |
+ scoped_refptr<SocketStream> socket_stream_; |
+ const CompletionCallback callback_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SelfDeletingDelegate); |
+}; |
+ |
class TestURLRequestContextWithProxy : public TestURLRequestContext { |
public: |
explicit TestURLRequestContextWithProxy(const std::string& proxy) |
@@ -846,4 +894,34 @@ TEST_F(SocketStreamTest, BeforeConnectFailed) { |
EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[1].event_type); |
} |
+// Check that a connect failure, followed by the delegate calling DetachDelegate |
+// and deleting itself in the OnError callback, is handled correctly. |
+TEST_F(SocketStreamTest, OnErrorDetachDelegate) { |
+ MockClientSocketFactory mock_socket_factory; |
+ TestCompletionCallback test_callback; |
+ |
+ // SelfDeletingDelegate is self-owning; we just need a pointer to it to |
+ // connect it and the SocketStream. |
+ SelfDeletingDelegate* delegate = |
+ new SelfDeletingDelegate(test_callback.callback()); |
+ MockConnect mock_connect(ASYNC, ERR_CONNECTION_REFUSED); |
+ StaticSocketDataProvider data; |
+ data.set_connect_data(mock_connect); |
+ mock_socket_factory.AddSocketDataProvider(&data); |
+ |
+ TestURLRequestContext context; |
+ scoped_refptr<SocketStream> socket_stream( |
+ new SocketStream(GURL("ws://localhost:9998/echo"), delegate)); |
+ socket_stream->set_context(&context); |
+ socket_stream->SetClientSocketFactory(&mock_socket_factory); |
+ delegate->set_socket_stream(socket_stream); |
+ // The delegate pointer will become invalid during the test. Set it to NULL to |
+ // avoid holding a dangling pointer. |
+ delegate = NULL; |
+ |
+ socket_stream->Connect(); |
+ |
+ EXPECT_EQ(OK, test_callback.WaitForResult()); |
+} |
+ |
} // namespace net |