Index: net/socket/client_socket_pool_base_unittest.cc |
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc |
index b1c1a99e1ed7edbd3fb8bfb724b776e6d173aa5f..d7c0f0def9cf9ef87a4e1c3ff107e5341c20ac33 100644 |
--- a/net/socket/client_socket_pool_base_unittest.cc |
+++ b/net/socket/client_socket_pool_base_unittest.cc |
@@ -4,6 +4,8 @@ |
#include "net/socket/client_socket_pool_base.h" |
+#include <vector> |
+ |
#include "base/bind.h" |
#include "base/bind_helpers.h" |
#include "base/callback.h" |
@@ -28,8 +30,12 @@ |
#include "net/socket/socket_test_util.h" |
#include "net/socket/ssl_host_info.h" |
#include "net/socket/stream_socket.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
#include "testing/gtest/include/gtest/gtest.h" |
+using ::testing::Invoke; |
+using ::testing::Return; |
+ |
namespace net { |
namespace { |
@@ -40,10 +46,18 @@ const net::RequestPriority kDefaultPriority = MEDIUM; |
class TestSocketParams : public base::RefCounted<TestSocketParams> { |
public: |
- bool ignore_limits() { return false; } |
+ TestSocketParams() : ignore_limits_(false) {} |
+ |
+ void set_ignore_limits(bool ignore_limits) { |
+ ignore_limits_ = ignore_limits; |
+ } |
+ bool ignore_limits() { return ignore_limits_; } |
+ |
private: |
friend class base::RefCounted<TestSocketParams>; |
~TestSocketParams() {} |
+ |
+ bool ignore_limits_; |
}; |
typedef ClientSocketPoolBase<TestSocketParams> TestClientSocketPoolBase; |
@@ -355,12 +369,18 @@ class TestConnectJobFactory |
public: |
explicit TestConnectJobFactory(MockClientSocketFactory* client_socket_factory) |
: job_type_(TestConnectJob::kMockJob), |
+ job_types_(NULL), |
client_socket_factory_(client_socket_factory) {} |
virtual ~TestConnectJobFactory() {} |
void set_job_type(TestConnectJob::JobType job_type) { job_type_ = job_type; } |
+ void set_job_types(std::vector<TestConnectJob::JobType>* job_types) { |
+ job_types_ = job_types; |
+ CHECK(!job_types_->empty()); |
+ } |
+ |
void set_timeout_duration(base::TimeDelta timeout_duration) { |
timeout_duration_ = timeout_duration; |
} |
@@ -371,7 +391,12 @@ class TestConnectJobFactory |
const std::string& group_name, |
const TestClientSocketPoolBase::Request& request, |
ConnectJob::Delegate* delegate) const { |
- return new TestConnectJob(job_type_, |
+ TestConnectJob::JobType job_type = job_type_; |
mmenke
2012/03/19 18:43:35
nit: Maybe an EXPECT_TRUE(!job_types_ || !job_typ
Ryan Hamilton
2012/03/19 19:04:15
Done.
|
+ if (job_types_ && !job_types_->empty()) { |
+ job_type = job_types_->back(); |
+ job_types_->pop_back(); |
mmenke
2012/03/19 18:43:35
Think going front to back is much clearer. Wasn't
Ryan Hamilton
2012/03/19 19:04:15
Done.
|
+ } |
+ return new TestConnectJob(job_type, |
group_name, |
request, |
timeout_duration_, |
@@ -386,6 +411,7 @@ class TestConnectJobFactory |
private: |
TestConnectJob::JobType job_type_; |
+ std::vector<TestConnectJob::JobType>* job_types_; |
base::TimeDelta timeout_duration_; |
MockClientSocketFactory* const client_socket_factory_; |
@@ -447,6 +473,10 @@ class TestClientSocketPool : public ClientSocketPool { |
base_.Flush(); |
} |
+ virtual bool IsStalled() const OVERRIDE { |
+ return base_.IsStalled(); |
+ } |
+ |
virtual void CloseIdleSockets() OVERRIDE { |
base_.CloseIdleSockets(); |
} |
@@ -466,6 +496,14 @@ class TestClientSocketPool : public ClientSocketPool { |
return base_.GetLoadState(group_name, handle); |
} |
+ virtual void AddLayeredPool(LayeredPool* pool) OVERRIDE { |
+ base_.AddLayeredPool(pool); |
+ } |
+ |
+ virtual void RemoveLayeredPool(LayeredPool* pool) OVERRIDE { |
+ base_.RemoveLayeredPool(pool); |
+ } |
+ |
virtual DictionaryValue* GetInfoAsValue( |
const std::string& name, |
const std::string& type, |
@@ -499,6 +537,10 @@ class TestClientSocketPool : public ClientSocketPool { |
void EnableConnectBackupJobs() { base_.EnableConnectBackupJobs(); } |
+ bool CloseOneIdleConnectionInLayeredPool() { |
+ return base_.CloseOneIdleConnectionInLayeredPool(); |
+ } |
+ |
private: |
TestClientSocketPoolBase base_; |
@@ -1164,6 +1206,7 @@ TEST_F(ClientSocketPoolBaseTest, WaitForStalledSocketAtSocketLimit) { |
ClientSocketHandle stalled_handle; |
TestCompletionCallback callback; |
{ |
+ EXPECT_FALSE(pool_->IsStalled()); |
ClientSocketHandle handles[kDefaultMaxSockets]; |
for (int i = 0; i < kDefaultMaxSockets; ++i) { |
TestCompletionCallback callback; |
@@ -1178,6 +1221,7 @@ TEST_F(ClientSocketPoolBaseTest, WaitForStalledSocketAtSocketLimit) { |
EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count()); |
EXPECT_EQ(0, pool_->IdleSocketCount()); |
+ EXPECT_FALSE(pool_->IsStalled()); |
// Now we will hit the socket limit. |
EXPECT_EQ(ERR_IO_PENDING, stalled_handle.Init("foo", |
@@ -1186,6 +1230,7 @@ TEST_F(ClientSocketPoolBaseTest, WaitForStalledSocketAtSocketLimit) { |
callback.callback(), |
pool_.get(), |
BoundNetLog())); |
+ EXPECT_TRUE(pool_->IsStalled()); |
// Dropping out of scope will close all handles and return them to idle. |
} |
@@ -2005,9 +2050,9 @@ TEST_F(ClientSocketPoolBaseTest, DisableCleanupTimer) { |
EXPECT_EQ(1, handle2.socket()->Write(NULL, 1, CompletionCallback())); |
handle2.Reset(); |
- // The idle socket timeout value was set to 10 milliseconds. Wait 20 |
+ // The idle socket timeout value was set to 10 milliseconds. Wait 100 |
// milliseconds so the sockets timeout. |
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); |
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); |
MessageLoop::current()->RunAllPending(); |
ASSERT_EQ(2, pool_->IdleSocketCount()); |
@@ -3039,6 +3084,7 @@ TEST_F(ClientSocketPoolBaseTest, RequestSocketsHitMaxSocketLimit) { |
ASSERT_TRUE(pool_->HasGroup("a")); |
EXPECT_EQ(kDefaultMaxSockets - 1, pool_->NumConnectJobsInGroup("a")); |
+ EXPECT_FALSE(pool_->IsStalled()); |
ASSERT_FALSE(pool_->HasGroup("b")); |
@@ -3047,6 +3093,7 @@ TEST_F(ClientSocketPoolBaseTest, RequestSocketsHitMaxSocketLimit) { |
ASSERT_TRUE(pool_->HasGroup("b")); |
EXPECT_EQ(1, pool_->NumConnectJobsInGroup("b")); |
+ EXPECT_FALSE(pool_->IsStalled()); |
} |
TEST_F(ClientSocketPoolBaseTest, RequestSocketsCountIdleSockets) { |
@@ -3365,6 +3412,170 @@ TEST_F(ClientSocketPoolBaseTest, PreconnectWithBackupJob) { |
EXPECT_EQ(1, pool_->NumActiveSocketsInGroup("a")); |
} |
+class MockLayeredPool : public LayeredPool { |
+ public: |
+ MockLayeredPool(TestClientSocketPool* pool, |
+ const std::string& group_name) |
+ : pool_(pool), |
+ params_(new TestSocketParams), |
+ group_name_(group_name), |
+ can_release_connection_(true) { |
+ pool_->AddLayeredPool(this); |
+ } |
+ |
+ ~MockLayeredPool() { |
+ pool_->RemoveLayeredPool(this); |
+ } |
+ |
+ int RequestSocket(TestClientSocketPool* pool) { |
+ return handle_.Init(group_name_, params_, kDefaultPriority, |
+ callback_.callback(), pool, BoundNetLog()); |
+ } |
+ |
+ int RequestSocketWithoutLimits(TestClientSocketPool* pool) { |
+ params_->set_ignore_limits(true); |
+ return handle_.Init(group_name_, params_, kDefaultPriority, |
+ callback_.callback(), pool, BoundNetLog()); |
+ } |
+ |
+ bool ReleaseOneConnection() { |
+ if (!handle_.is_initialized() || !can_release_connection_) { |
+ return false; |
+ } |
+ handle_.socket()->Disconnect(); |
+ handle_.Reset(); |
+ return true; |
+ } |
+ |
+ void set_can_release_connection(bool can_release_connection) { |
+ can_release_connection_ = can_release_connection; |
+ } |
+ |
+ MOCK_METHOD0(CloseOneIdleConnection, bool()); |
+ |
+ private: |
+ TestClientSocketPool* const pool_; |
+ scoped_refptr<TestSocketParams> params_; |
+ ClientSocketHandle handle_; |
+ TestCompletionCallback callback_; |
+ const std::string group_name_; |
+ bool can_release_connection_; |
+}; |
+ |
+TEST_F(ClientSocketPoolBaseTest, FailToCloseIdleSocketsNotHeldByLayeredPool) { |
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup); |
+ connect_job_factory_->set_job_type(TestConnectJob::kMockJob); |
+ |
+ MockLayeredPool mock_layered_pool(pool_.get(), "foo"); |
+ EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection()) |
+ .WillOnce(Return(false)); |
mmenke
2012/03/19 18:43:35
nit: To make this test more closely match the nex
Ryan Hamilton
2012/03/19 19:04:15
Done.
|
+ EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get())); |
+ EXPECT_FALSE(pool_->CloseOneIdleConnectionInLayeredPool()); |
+} |
+ |
+TEST_F(ClientSocketPoolBaseTest, ForciblyCloseIdleSocketsHeldByLayeredPool) { |
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup); |
+ connect_job_factory_->set_job_type(TestConnectJob::kMockJob); |
+ |
+ MockLayeredPool mock_layered_pool(pool_.get(), "foo"); |
+ EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get())); |
+ EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection()) |
+ .WillOnce(Invoke(&mock_layered_pool, |
+ &MockLayeredPool::ReleaseOneConnection)); |
+ EXPECT_TRUE(pool_->CloseOneIdleConnectionInLayeredPool()); |
+} |
+ |
+// This test exercises the codepath which caused http://crbug.com/109876 |
+TEST_F(ClientSocketPoolBaseTest, |
+ CloseIdleSocketsHeldByLayeredPoolInSameGroupWhenNeeded) { |
+ CreatePool(2, 2); |
+ std::vector<TestConnectJob::JobType> job_types; |
+ job_types.push_back(TestConnectJob::kMockJob); |
+ job_types.push_back(TestConnectJob::kMockFailingJob); |
+ job_types.push_back(TestConnectJob::kMockJob); |
+ job_types.push_back(TestConnectJob::kMockJob); |
+ connect_job_factory_->set_job_types(&job_types); |
+ |
+ ClientSocketHandle handle1; |
+ TestCompletionCallback callback1; |
+ EXPECT_EQ(OK, handle1.Init("group1", |
+ params_, |
+ kDefaultPriority, |
+ callback1.callback(), |
+ pool_.get(), |
+ BoundNetLog())); |
mmenke
2012/03/19 18:43:35
nit: Fix indent.
Ryan Hamilton
2012/03/19 19:04:15
Done.
|
+ |
+ MockLayeredPool mock_layered_pool(pool_.get(), "group2"); |
+ EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get())); |
+ EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection()) |
+ .WillRepeatedly(Invoke(&mock_layered_pool, |
+ &MockLayeredPool::ReleaseOneConnection)); |
mmenke
2012/03/19 18:43:35
nit: -1 indent.
Ryan Hamilton
2012/03/19 19:04:15
Done.
|
+ mock_layered_pool.set_can_release_connection(false); |
+ |
+ ClientSocketHandle handle2; |
mmenke
2012/03/19 18:43:35
Suggest you name these handle3 and handle4, so the
mmenke
2012/03/19 18:43:35
Suggest you add a comment here saying that this co
Ryan Hamilton
2012/03/19 19:04:15
Done. Good idea.
Ryan Hamilton
2012/03/19 19:04:15
Done.
|
+ TestCompletionCallback callback2; |
+ EXPECT_EQ(ERR_IO_PENDING, handle2.Init("group2", |
+ params_, |
+ HIGHEST, |
mmenke
2012/03/19 18:43:35
I don't think this or the next request has to be a
Ryan Hamilton
2012/03/19 19:04:15
Done. Yes, you're right. This was a leftover fro
|
+ callback2.callback(), |
+ pool_.get(), |
+ BoundNetLog())); |
+ |
+ mock_layered_pool.set_can_release_connection(true); |
+ ClientSocketHandle handle3; |
+ TestCompletionCallback callback3; |
+ EXPECT_EQ(OK, handle3.Init("group2", |
+ params_, |
+ HIGHEST, |
+ callback3.callback(), |
+ pool_.get(), |
+ BoundNetLog())); |
mmenke
2012/03/19 18:43:35
nit: Fix indent.
Ryan Hamilton
2012/03/19 19:04:15
Done.
|
+} |
+ |
+TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketsHeldByLayeredPoolWhenNeeded) { |
+ CreatePool(1, 1); |
+ connect_job_factory_->set_job_type(TestConnectJob::kMockJob); |
+ |
+ MockLayeredPool mock_layered_pool(pool_.get(), "foo"); |
+ EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get())); |
+ EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection()) |
+ .WillOnce(Invoke(&mock_layered_pool, |
+ &MockLayeredPool::ReleaseOneConnection)); |
+ ClientSocketHandle handle; |
+ TestCompletionCallback callback; |
+ EXPECT_EQ(OK, handle.Init("a", |
+ params_, |
+ kDefaultPriority, |
+ callback.callback(), |
+ pool_.get(), |
+ BoundNetLog())); |
+} |
+ |
+TEST_F(ClientSocketPoolBaseTest, |
+ CloseMultipleIdleSocketsHeldByLayeredPoolWhenNeeded) { |
+ CreatePool(1, 1); |
+ connect_job_factory_->set_job_type(TestConnectJob::kMockJob); |
+ |
+ MockLayeredPool mock_layered_pool1(pool_.get(), "foo"); |
+ EXPECT_EQ(OK, mock_layered_pool1.RequestSocket(pool_.get())); |
+ EXPECT_CALL(mock_layered_pool1, CloseOneIdleConnection()) |
+ .WillRepeatedly(Invoke(&mock_layered_pool1, |
+ &MockLayeredPool::ReleaseOneConnection)); |
+ MockLayeredPool mock_layered_pool2(pool_.get(), "bar"); |
+ EXPECT_EQ(OK, mock_layered_pool2.RequestSocketWithoutLimits(pool_.get())); |
+ EXPECT_CALL(mock_layered_pool2, CloseOneIdleConnection()) |
+ .WillRepeatedly(Invoke(&mock_layered_pool2, |
+ &MockLayeredPool::ReleaseOneConnection)); |
+ ClientSocketHandle handle; |
+ TestCompletionCallback callback; |
+ EXPECT_EQ(OK, handle.Init("a", |
+ params_, |
+ kDefaultPriority, |
+ callback.callback(), |
+ pool_.get(), |
+ BoundNetLog())); |
+} |
+ |
} // namespace |
} // namespace net |