// Copyright (c) 2009 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 "net/socket/client_socket_pool_base.h"

#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "base/platform_thread.h"
#include "base/scoped_vector.h"
#include "net/base/load_log.h"
#include "net/base/load_log_unittest.h"
#include "net/base/net_errors.h"
#include "net/base/request_priority.h"
#include "net/base/test_completion_callback.h"
#include "net/socket/client_socket.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/socket_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

const int kDefaultMaxSockets = 4;
const int kDefaultMaxSocketsPerGroup = 2;
const net::RequestPriority kDefaultPriority = MEDIUM;

typedef ClientSocketPoolBase<const void*> TestClientSocketPoolBase;

class MockClientSocket : public ClientSocket {
 public:
  MockClientSocket() : connected_(false) {}

  // Socket methods:
  virtual int Read(
      IOBuffer* /* buf */, int /* len */, CompletionCallback* /* callback */) {
    return ERR_UNEXPECTED;
  }

  virtual int Write(
      IOBuffer* /* buf */, int /* len */, CompletionCallback* /* callback */) {
    return ERR_UNEXPECTED;
  }
  virtual bool SetReceiveBufferSize(int32 size) { return true; };
  virtual bool SetSendBufferSize(int32 size) { return true; };

  // ClientSocket methods:

  virtual int Connect(CompletionCallback* callback, LoadLog* load_log) {
    connected_ = true;
    return OK;
  }

  virtual void Disconnect() { connected_ = false; }
  virtual bool IsConnected() const { return connected_; }
  virtual bool IsConnectedAndIdle() const { return connected_; }

  virtual int GetPeerName(struct sockaddr* /* name */,
                          socklen_t* /* namelen */) {
    return ERR_UNEXPECTED;
  }

 private:
  bool connected_;

  DISALLOW_COPY_AND_ASSIGN(MockClientSocket);
};

class TestConnectJob;

class MockClientSocketFactory : public ClientSocketFactory {
 public:
  MockClientSocketFactory() : allocation_count_(0) {}

  virtual ClientSocket* CreateTCPClientSocket(const AddressList& addresses) {
    allocation_count_++;
    return NULL;
  }

  virtual SSLClientSocket* CreateSSLClientSocket(
      ClientSocket* transport_socket,
      const std::string& hostname,
      const SSLConfig& ssl_config) {
    NOTIMPLEMENTED();
    return NULL;
  }

  void WaitForSignal(TestConnectJob* job) { waiting_jobs_.push_back(job); }
  void SignalJobs();

  int allocation_count() const { return allocation_count_; }

 private:
  int allocation_count_;
  std::vector<TestConnectJob*> waiting_jobs_;
};

class TestConnectJob : public ConnectJob {
 public:
  enum JobType {
    kMockJob,
    kMockFailingJob,
    kMockPendingJob,
    kMockPendingFailingJob,
    kMockWaitingJob,
    kMockAdvancingLoadStateJob,
  };

  TestConnectJob(JobType job_type,
                 const std::string& group_name,
                 const TestClientSocketPoolBase::Request& request,
                 base::TimeDelta timeout_duration,
                 ConnectJob::Delegate* delegate,
                 MockClientSocketFactory* client_socket_factory,
                 LoadLog* load_log)
      : ConnectJob(group_name, timeout_duration, delegate, load_log),
        job_type_(job_type),
        client_socket_factory_(client_socket_factory),
        method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
        load_state_(LOAD_STATE_IDLE) {}

  void Signal() {
    DoConnect(waiting_success_, true /* async */);
  }

  virtual LoadState GetLoadState() const { return load_state_; }

 private:
  // ConnectJob methods:

  virtual int ConnectInternal() {
    AddressList ignored;
    client_socket_factory_->CreateTCPClientSocket(ignored);
    set_socket(new MockClientSocket());
    switch (job_type_) {
      case kMockJob:
        return DoConnect(true /* successful */, false /* sync */);
      case kMockFailingJob:
        return DoConnect(false /* error */, false /* sync */);
      case kMockPendingJob:
        set_load_state(LOAD_STATE_CONNECTING);

        // Depending on execution timings, posting a delayed task can result
        // in the task getting executed the at the earliest possible
        // opportunity or only after returning once from the message loop and
        // then a second call into the message loop. In order to make behavior
        // more deterministic, we change the default delay to 2ms. This should
        // always require us to wait for the second call into the message loop.
        //
        // N.B. The correct fix for this and similar timing problems is to
        // abstract time for the purpose of unittests. Unfortunately, we have
        // a lot of third-party components that directly call the various
        // time functions, so this change would be rather invasive.
        MessageLoop::current()->PostDelayedTask(
            FROM_HERE,
            method_factory_.NewRunnableMethod(
                &TestConnectJob::DoConnect,
                true /* successful */,
                true /* async */),
            2);
        return ERR_IO_PENDING;
      case kMockPendingFailingJob:
        set_load_state(LOAD_STATE_CONNECTING);
        MessageLoop::current()->PostDelayedTask(
            FROM_HERE,
            method_factory_.NewRunnableMethod(
                &TestConnectJob::DoConnect,
                false /* error */,
                true  /* async */),
            2);
        return ERR_IO_PENDING;
      case kMockWaitingJob:
        client_socket_factory_->WaitForSignal(this);
        waiting_success_ = true;
        return ERR_IO_PENDING;
      case kMockAdvancingLoadStateJob:
        MessageLoop::current()->PostDelayedTask(
            FROM_HERE,
            method_factory_.NewRunnableMethod(
                &TestConnectJob::AdvanceLoadState, load_state_),
            2);
        return ERR_IO_PENDING;
      default:
        NOTREACHED();
        set_socket(NULL);
        return ERR_FAILED;
    }
  }

  void set_load_state(LoadState load_state) { load_state_ = load_state; }

  int DoConnect(bool succeed, bool was_async) {
    int result = ERR_CONNECTION_FAILED;
    if (succeed) {
      result = OK;
      socket()->Connect(NULL, NULL);
    } else {
      set_socket(NULL);
    }

    if (was_async)
      NotifyDelegateOfCompletion(result);
    return result;
  }

  void AdvanceLoadState(LoadState state) {
    int tmp = state;
    tmp++;
    state = static_cast<LoadState>(tmp);
    set_load_state(state);
    // Post a delayed task so RunAllPending() won't run it.
    MessageLoop::current()->PostDelayedTask(
        FROM_HERE,
        method_factory_.NewRunnableMethod(&TestConnectJob::AdvanceLoadState,
                                          state),
        1 /* 1ms delay */);
  }

  bool waiting_success_;
  const JobType job_type_;
  MockClientSocketFactory* const client_socket_factory_;
  ScopedRunnableMethodFactory<TestConnectJob> method_factory_;
  LoadState load_state_;

  DISALLOW_COPY_AND_ASSIGN(TestConnectJob);
};

class TestConnectJobFactory
    : public TestClientSocketPoolBase::ConnectJobFactory {
 public:
  explicit TestConnectJobFactory(MockClientSocketFactory* client_socket_factory)
      : job_type_(TestConnectJob::kMockJob),
        client_socket_factory_(client_socket_factory) {}

  virtual ~TestConnectJobFactory() {}

  void set_job_type(TestConnectJob::JobType job_type) { job_type_ = job_type; }

  void set_timeout_duration(base::TimeDelta timeout_duration) {
    timeout_duration_ = timeout_duration;
  }

  // ConnectJobFactory methods:

  virtual ConnectJob* NewConnectJob(
      const std::string& group_name,
      const TestClientSocketPoolBase::Request& request,
      ConnectJob::Delegate* delegate,
      LoadLog* load_log) const {
    return new TestConnectJob(job_type_,
                              group_name,
                              request,
                              timeout_duration_,
                              delegate,
                              client_socket_factory_,
                              load_log);
  }

 private:
  TestConnectJob::JobType job_type_;
  base::TimeDelta timeout_duration_;
  MockClientSocketFactory* const client_socket_factory_;

  DISALLOW_COPY_AND_ASSIGN(TestConnectJobFactory);
};

class TestClientSocketPool : public ClientSocketPool {
 public:
  TestClientSocketPool(
      int max_sockets,
      int max_sockets_per_group,
      base::TimeDelta unused_idle_socket_timeout,
      base::TimeDelta used_idle_socket_timeout,
      TestClientSocketPoolBase::ConnectJobFactory* connect_job_factory)
      : base_(max_sockets, max_sockets_per_group,
              unused_idle_socket_timeout, used_idle_socket_timeout,
              connect_job_factory, NULL) {}

  virtual int RequestSocket(
      const std::string& group_name,
      const void* params,
      net::RequestPriority priority,
      ClientSocketHandle* handle,
      CompletionCallback* callback,
      LoadLog* load_log) {
    return base_.RequestSocket(
        group_name, params, priority, handle, callback, load_log);
  }

  virtual void CancelRequest(
      const std::string& group_name,
      const ClientSocketHandle* handle) {
    base_.CancelRequest(group_name, handle);
  }

  virtual void ReleaseSocket(
      const std::string& group_name,
      ClientSocket* socket) {
    base_.ReleaseSocket(group_name, socket);
  }

  virtual void CloseIdleSockets() {
    base_.CloseIdleSockets();
  }

  virtual int IdleSocketCount() const { return base_.idle_socket_count(); }

  virtual int IdleSocketCountInGroup(const std::string& group_name) const {
    return base_.IdleSocketCountInGroup(group_name);
  }

  virtual LoadState GetLoadState(const std::string& group_name,
                                 const ClientSocketHandle* handle) const {
    return base_.GetLoadState(group_name, handle);
  }

  const TestClientSocketPoolBase* base() const { return &base_; }

  int NumConnectJobsInGroup(const std::string& group_name) const {
    return base_.NumConnectJobsInGroup(group_name);
  }

  void CleanupTimedOutIdleSockets() { base_.CleanupIdleSockets(false); }

 private:
  ~TestClientSocketPool() {}

  TestClientSocketPoolBase base_;

  DISALLOW_COPY_AND_ASSIGN(TestClientSocketPool);
};

}  // namespace

REGISTER_SOCKET_PARAMS_FOR_POOL(TestClientSocketPool, const void*);

namespace {

void MockClientSocketFactory::SignalJobs() {
  for (std::vector<TestConnectJob*>::iterator it = waiting_jobs_.begin();
       it != waiting_jobs_.end(); ++it) {
    (*it)->Signal();
  }
  waiting_jobs_.clear();
}

class TestConnectJobDelegate : public ConnectJob::Delegate {
 public:
  TestConnectJobDelegate()
      : have_result_(false), waiting_for_result_(false), result_(OK) {}
  virtual ~TestConnectJobDelegate() {}

  virtual void OnConnectJobComplete(int result, ConnectJob* job) {
    result_ = result;
    scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
    // socket.get() should be NULL iff result != OK
    EXPECT_EQ(socket.get() == NULL, result != OK);
    delete job;
    have_result_ = true;
    if (waiting_for_result_)
      MessageLoop::current()->Quit();
  }

  int WaitForResult() {
    DCHECK(!waiting_for_result_);
    while (!have_result_) {
      waiting_for_result_ = true;
      MessageLoop::current()->Run();
      waiting_for_result_ = false;
    }
    have_result_ = false;  // auto-reset for next callback
    return result_;
  }

 private:
  bool have_result_;
  bool waiting_for_result_;
  int result_;
};

class ClientSocketPoolBaseTest : public ClientSocketPoolTest {
 protected:
  ClientSocketPoolBaseTest() {}

  void CreatePool(int max_sockets, int max_sockets_per_group) {
    CreatePoolWithIdleTimeouts(
        max_sockets,
        max_sockets_per_group,
        base::TimeDelta::FromSeconds(kUnusedIdleSocketTimeout),
        base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout));
  }

  void CreatePoolWithIdleTimeouts(
      int max_sockets, int max_sockets_per_group,
      base::TimeDelta unused_idle_socket_timeout,
      base::TimeDelta used_idle_socket_timeout) {
    DCHECK(!pool_.get());
    connect_job_factory_ = new TestConnectJobFactory(&client_socket_factory_);
    pool_ = new TestClientSocketPool(max_sockets,
                                     max_sockets_per_group,
                                     unused_idle_socket_timeout,
                                     used_idle_socket_timeout,
                                     connect_job_factory_);
  }

  int StartRequest(const std::string& group_name,
                   net::RequestPriority priority) {
    return StartRequestUsingPool<TestClientSocketPool, const void*>(
        pool_.get(), group_name, priority, NULL);
  }

  virtual void TearDown() {
    // We post all of our delayed tasks with a 2ms delay. I.e. they don't
    // actually become pending until 2ms after they have been created. In order
    // to flush all tasks, we need to wait so that we know there are no
    // soon-to-be-pending tasks waiting.
    PlatformThread::Sleep(10);
    MessageLoop::current()->RunAllPending();

    // Need to delete |pool_| before we turn late binding back off. We also need
    // to delete |requests_| because the pool is reference counted and requests
    // keep reference to it.
    // TODO(willchan): Remove this part when late binding becomes the default.
    pool_ = NULL;
    requests_.reset();

    ClientSocketPoolTest::TearDown();
  }

  MockClientSocketFactory client_socket_factory_;
  TestConnectJobFactory* connect_job_factory_;
  scoped_refptr<TestClientSocketPool> pool_;
};

// Helper function which explicitly specifies the template parameters, since
// the compiler will infer (in this case, incorrectly) that NULL is of type int.
int InitHandle(ClientSocketHandle* handle,
               const std::string& group_name,
               net::RequestPriority priority,
               CompletionCallback* callback,
               TestClientSocketPool* pool,
               LoadLog* load_log) {
  return handle->Init<const void*, TestClientSocketPool>(
      group_name, NULL, priority, callback, pool, load_log);
}

// Even though a timeout is specified, it doesn't time out on a synchronous
// completion.
TEST_F(ClientSocketPoolBaseTest, ConnectJob_NoTimeoutOnSynchronousCompletion) {
  TestConnectJobDelegate delegate;
  ClientSocketHandle ignored;
  TestClientSocketPoolBase::Request request(
      &ignored, NULL, kDefaultPriority, NULL, NULL);
  scoped_ptr<TestConnectJob> job(
      new TestConnectJob(TestConnectJob::kMockJob,
                         "a",
                         request,
                         base::TimeDelta::FromMicroseconds(1),
                         &delegate,
                         &client_socket_factory_,
                         NULL));
  EXPECT_EQ(OK, job->Connect());
}

TEST_F(ClientSocketPoolBaseTest, ConnectJob_TimedOut) {
  TestConnectJobDelegate delegate;
  ClientSocketHandle ignored;
  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
  TestClientSocketPoolBase::Request request(
      &ignored, NULL, kDefaultPriority, NULL, NULL);
  // Deleted by TestConnectJobDelegate.
  TestConnectJob* job =
      new TestConnectJob(TestConnectJob::kMockPendingJob,
                         "a",
                         request,
                         base::TimeDelta::FromMicroseconds(1),
                         &delegate,
                         &client_socket_factory_,
                         log);
  ASSERT_EQ(ERR_IO_PENDING, job->Connect());
  PlatformThread::Sleep(1);
  EXPECT_EQ(ERR_TIMED_OUT, delegate.WaitForResult());

  EXPECT_EQ(3u, log->entries().size());
  EXPECT_TRUE(LogContainsBeginEvent(
      *log, 0, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEvent(
      *log, 1, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT,
      LoadLog::PHASE_NONE));
  EXPECT_TRUE(LogContainsEndEvent(
      *log, 2, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
}

TEST_F(ClientSocketPoolBaseTest, BasicSynchronous) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  TestCompletionCallback callback;
  ClientSocketHandle handle;
  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
  EXPECT_EQ(OK, InitHandle(&handle, "a", kDefaultPriority,
                           &callback, pool_.get(), log));
  EXPECT_TRUE(handle.is_initialized());
  EXPECT_TRUE(handle.socket());
  handle.Reset();

  EXPECT_EQ(4u, log->entries().size());
  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKET_POOL));
  EXPECT_TRUE(LogContainsBeginEvent(
      *log, 1, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEndEvent(
      *log, 2, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEndEvent(*log, 3, LoadLog::TYPE_SOCKET_POOL));
}

TEST_F(ClientSocketPoolBaseTest, InitConnectionFailure) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
  TestSocketRequest req(&request_order_, &completion_count_);
  EXPECT_EQ(ERR_CONNECTION_FAILED,
            InitHandle(req.handle(), "a", kDefaultPriority, &req,
                       pool_.get(), log));

  EXPECT_EQ(4u, log->entries().size());
  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKET_POOL));
  EXPECT_TRUE(LogContainsBeginEvent(
      *log, 1, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEndEvent(
      *log, 2, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEndEvent(*log, 3, LoadLog::TYPE_SOCKET_POOL));
}

TEST_F(ClientSocketPoolBaseTest, TotalLimit) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  // TODO(eroman): Check that the LoadLog contains this event.

  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("c", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("d", kDefaultPriority));

  EXPECT_EQ(static_cast<int>(requests_.size()),
            client_socket_factory_.allocation_count());
  EXPECT_EQ(requests_.size() - kDefaultMaxSockets, completion_count_);

  EXPECT_EQ(ERR_IO_PENDING, StartRequest("e", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("f", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("g", kDefaultPriority));

  ReleaseAllConnections(KEEP_ALIVE);

  EXPECT_EQ(static_cast<int>(requests_.size()),
            client_socket_factory_.allocation_count());
  EXPECT_EQ(requests_.size() - kDefaultMaxSockets, completion_count_);

  EXPECT_EQ(1, GetOrderOfRequest(1));
  EXPECT_EQ(2, GetOrderOfRequest(2));
  EXPECT_EQ(3, GetOrderOfRequest(3));
  EXPECT_EQ(4, GetOrderOfRequest(4));
  EXPECT_EQ(5, GetOrderOfRequest(5));
  EXPECT_EQ(6, GetOrderOfRequest(6));
  EXPECT_EQ(7, GetOrderOfRequest(7));

  // Make sure we test order of all requests made.
  EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(8));
}

TEST_F(ClientSocketPoolBaseTest, TotalLimitReachedNewGroup) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  // TODO(eroman): Check that the LoadLog contains this event.

  // Reach all limits: max total sockets, and max sockets per group.
  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));

  EXPECT_EQ(static_cast<int>(requests_.size()),
            client_socket_factory_.allocation_count());
  EXPECT_EQ(requests_.size() - kDefaultMaxSockets, completion_count_);

  // Now create a new group and verify that we don't starve it.
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kDefaultPriority));

  ReleaseAllConnections(KEEP_ALIVE);

  EXPECT_EQ(static_cast<int>(requests_.size()),
            client_socket_factory_.allocation_count());
  EXPECT_EQ(requests_.size() - kDefaultMaxSockets, completion_count_);

  EXPECT_EQ(1, GetOrderOfRequest(1));
  EXPECT_EQ(2, GetOrderOfRequest(2));
  EXPECT_EQ(3, GetOrderOfRequest(3));
  EXPECT_EQ(4, GetOrderOfRequest(4));
  EXPECT_EQ(5, GetOrderOfRequest(5));

  // Make sure we test order of all requests made.
  EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(6));
}

TEST_F(ClientSocketPoolBaseTest, TotalLimitRespectsPriority) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  EXPECT_EQ(OK, StartRequest("b", LOWEST));
  EXPECT_EQ(OK, StartRequest("a", MEDIUM));
  EXPECT_EQ(OK, StartRequest("b", HIGHEST));
  EXPECT_EQ(OK, StartRequest("a", LOWEST));

  EXPECT_EQ(static_cast<int>(requests_.size()),
            client_socket_factory_.allocation_count());

  EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", LOWEST));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", MEDIUM));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("b", HIGHEST));

  ReleaseAllConnections(KEEP_ALIVE);

  // We're re-using one socket for group "a", and one for "b".
  EXPECT_EQ(static_cast<int>(requests_.size()) - 2,
            client_socket_factory_.allocation_count());
  EXPECT_EQ(requests_.size() - kDefaultMaxSockets, completion_count_);

  // First 4 requests don't have to wait, and finish in order.
  EXPECT_EQ(1, GetOrderOfRequest(1));
  EXPECT_EQ(2, GetOrderOfRequest(2));
  EXPECT_EQ(3, GetOrderOfRequest(3));
  EXPECT_EQ(4, GetOrderOfRequest(4));

  // Request ("b", HIGHEST) has the highest priority, then ("a", MEDIUM),
  // and then ("c", LOWEST).
  EXPECT_EQ(7, GetOrderOfRequest(5));
  EXPECT_EQ(6, GetOrderOfRequest(6));
  EXPECT_EQ(5, GetOrderOfRequest(7));

  // Make sure we test order of all requests made.
  EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(8));
}

TEST_F(ClientSocketPoolBaseTest, TotalLimitRespectsGroupLimit) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  EXPECT_EQ(OK, StartRequest("a", LOWEST));
  EXPECT_EQ(OK, StartRequest("a", LOW));
  EXPECT_EQ(OK, StartRequest("b", HIGHEST));
  EXPECT_EQ(OK, StartRequest("b", MEDIUM));

  EXPECT_EQ(static_cast<int>(requests_.size()),
            client_socket_factory_.allocation_count());

  EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", MEDIUM));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOW));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("b", HIGHEST));

  ReleaseAllConnections(KEEP_ALIVE);

  // We're re-using one socket for group "a", and one for "b".
  EXPECT_EQ(static_cast<int>(requests_.size()) - 2,
            client_socket_factory_.allocation_count());
  EXPECT_EQ(requests_.size() - kDefaultMaxSockets, completion_count_);

  // First 4 requests don't have to wait, and finish in order.
  EXPECT_EQ(1, GetOrderOfRequest(1));
  EXPECT_EQ(2, GetOrderOfRequest(2));
  EXPECT_EQ(3, GetOrderOfRequest(3));
  EXPECT_EQ(4, GetOrderOfRequest(4));

  // Request ("b", 7) has the highest priority, but we can't make new socket for
  // group "b", because it has reached the per-group limit. Then we make
  // socket for ("c", 6), because it has higher priority than ("a", 4),
  // and we still can't make a socket for group "b".
  EXPECT_EQ(5, GetOrderOfRequest(5));
  EXPECT_EQ(6, GetOrderOfRequest(6));
  EXPECT_EQ(7, GetOrderOfRequest(7));

  // Make sure we test order of all requests made.
  EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(8));
}

// Make sure that we count connecting sockets against the total limit.
TEST_F(ClientSocketPoolBaseTest, TotalLimitCountsConnectingSockets) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("c", kDefaultPriority));

  // Create one asynchronous request.
  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("d", kDefaultPriority));

  // We post all of our delayed tasks with a 2ms delay. I.e. they don't
  // actually become pending until 2ms after they have been created. In order
  // to flush all tasks, we need to wait so that we know there are no
  // soon-to-be-pending tasks waiting.
  PlatformThread::Sleep(10);
  MessageLoop::current()->RunAllPending();

  // The next synchronous request should wait for its turn.
  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("e", kDefaultPriority));

  ReleaseAllConnections(KEEP_ALIVE);

  EXPECT_EQ(static_cast<int>(requests_.size()),
            client_socket_factory_.allocation_count());

  EXPECT_EQ(1, GetOrderOfRequest(1));
  EXPECT_EQ(2, GetOrderOfRequest(2));
  EXPECT_EQ(3, GetOrderOfRequest(3));
  EXPECT_EQ(4, GetOrderOfRequest(4));
  EXPECT_EQ(5, GetOrderOfRequest(5));

  // Make sure we test order of all requests made.
  EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(6));
}

// Inside ClientSocketPoolBase we have a may_have_stalled_group flag,
// which tells it to use more expensive, but accurate, group selection
// algorithm. Make sure it doesn't get stuck in the "on" state.
TEST_F(ClientSocketPoolBaseTest, MayHaveStalledGroupReset) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  EXPECT_FALSE(pool_->base()->may_have_stalled_group());

  // Reach group socket limit.
  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_FALSE(pool_->base()->may_have_stalled_group());

  // Reach total limit, but don't request more sockets.
  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
  EXPECT_FALSE(pool_->base()->may_have_stalled_group());

  // Request one more socket while we are at the maximum sockets limit.
  // This should flip the may_have_stalled_group flag.
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kDefaultPriority));
  EXPECT_TRUE(pool_->base()->may_have_stalled_group());

  // After releasing first connection for "a", we're still at the
  // maximum sockets limit, but every group's pending queue is empty,
  // so we reset the flag.
  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
  EXPECT_FALSE(pool_->base()->may_have_stalled_group());

  // Requesting additional socket while at the total limit should
  // flip the flag back to "on".
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kDefaultPriority));
  EXPECT_TRUE(pool_->base()->may_have_stalled_group());

  // We'll request one more socket to verify that we don't reset the flag
  // too eagerly.
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("d", kDefaultPriority));
  EXPECT_TRUE(pool_->base()->may_have_stalled_group());

  // We're at the maximum socket limit, and still have one request pending
  // for "d". Flag should be "on".
  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
  EXPECT_TRUE(pool_->base()->may_have_stalled_group());

  // Now every group's pending queue should be empty again.
  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
  EXPECT_FALSE(pool_->base()->may_have_stalled_group());

  ReleaseAllConnections(KEEP_ALIVE);
  EXPECT_FALSE(pool_->base()->may_have_stalled_group());
}

TEST_F(ClientSocketPoolBaseTest, PendingRequests) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", MEDIUM));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", HIGHEST));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOW));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));

  ReleaseAllConnections(KEEP_ALIVE);

  EXPECT_EQ(kDefaultMaxSocketsPerGroup,
            client_socket_factory_.allocation_count());
  EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup, completion_count_);

  EXPECT_EQ(1, GetOrderOfRequest(1));
  EXPECT_EQ(2, GetOrderOfRequest(2));
  EXPECT_EQ(6, GetOrderOfRequest(3));
  EXPECT_EQ(4, GetOrderOfRequest(4));
  EXPECT_EQ(3, GetOrderOfRequest(5));
  EXPECT_EQ(5, GetOrderOfRequest(6));
  EXPECT_EQ(7, GetOrderOfRequest(7));

  // Make sure we test order of all requests made.
  EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(8));
}

TEST_F(ClientSocketPoolBaseTest, PendingRequests_NoKeepAlive) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", MEDIUM));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", HIGHEST));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOW));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));

  ReleaseAllConnections(NO_KEEP_ALIVE);

  for (size_t i = kDefaultMaxSocketsPerGroup; i < requests_.size(); ++i)
    EXPECT_EQ(OK, requests_[i]->WaitForResult());

  EXPECT_EQ(static_cast<int>(requests_.size()),
            client_socket_factory_.allocation_count());
  EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup, completion_count_);
}

// This test will start up a RequestSocket() and then immediately Cancel() it.
// The pending connect job will be cancelled and should not call back into
// ClientSocketPoolBase.
TEST_F(ClientSocketPoolBaseTest, CancelRequestClearGroup) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
  TestSocketRequest req(&request_order_, &completion_count_);
  EXPECT_EQ(ERR_IO_PENDING,
            InitHandle(req.handle(), "a", kDefaultPriority, &req,
                       pool_.get(), NULL));
  req.handle()->Reset();
}

TEST_F(ClientSocketPoolBaseTest, ConnectCancelConnect) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
  ClientSocketHandle handle;
  TestCompletionCallback callback;
  TestSocketRequest req(&request_order_, &completion_count_);

  EXPECT_EQ(ERR_IO_PENDING,
            InitHandle(&handle, "a", kDefaultPriority, &callback,
                       pool_.get(), NULL));

  handle.Reset();

  TestCompletionCallback callback2;
  EXPECT_EQ(ERR_IO_PENDING,
            InitHandle(&handle, "a", kDefaultPriority, &callback2,
                       pool_.get(), NULL));

  EXPECT_EQ(OK, callback2.WaitForResult());
  EXPECT_FALSE(callback.have_result());

  handle.Reset();
}

TEST_F(ClientSocketPoolBaseTest, CancelRequest) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", MEDIUM));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", HIGHEST));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOW));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));

  // Cancel a request.
  size_t index_to_cancel = kDefaultMaxSocketsPerGroup + 2;
  EXPECT_FALSE(requests_[index_to_cancel]->handle()->is_initialized());
  requests_[index_to_cancel]->handle()->Reset();

  ReleaseAllConnections(KEEP_ALIVE);

  EXPECT_EQ(kDefaultMaxSocketsPerGroup,
            client_socket_factory_.allocation_count());
  EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup - 1,
            completion_count_);

  EXPECT_EQ(1, GetOrderOfRequest(1));
  EXPECT_EQ(2, GetOrderOfRequest(2));
  EXPECT_EQ(5, GetOrderOfRequest(3));
  EXPECT_EQ(3, GetOrderOfRequest(4));
  EXPECT_EQ(kRequestNotFound, GetOrderOfRequest(5));  // Canceled request.
  EXPECT_EQ(4, GetOrderOfRequest(6));
  EXPECT_EQ(6, GetOrderOfRequest(7));

  // Make sure we test order of all requests made.
  EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(8));
}

class RequestSocketCallback : public CallbackRunner< Tuple1<int> > {
 public:
  RequestSocketCallback(ClientSocketHandle* handle,
                        TestClientSocketPool* pool,
                        TestConnectJobFactory* test_connect_job_factory,
                        TestConnectJob::JobType next_job_type)
      : handle_(handle),
        pool_(pool),
        within_callback_(false),
        test_connect_job_factory_(test_connect_job_factory),
        next_job_type_(next_job_type) {}

  virtual void RunWithParams(const Tuple1<int>& params) {
    callback_.RunWithParams(params);
    ASSERT_EQ(OK, params.a);

    if (!within_callback_) {
      test_connect_job_factory_->set_job_type(next_job_type_);

      // Don't allow reuse of the socket.  Disconnect it and then release it and
      // run through the MessageLoop once to get it completely released.
      handle_->socket()->Disconnect();
      handle_->Reset();
      {
        MessageLoop::ScopedNestableTaskAllower nestable(
            MessageLoop::current());
        MessageLoop::current()->RunAllPending();
      }
      within_callback_ = true;
      TestCompletionCallback next_job_callback;
      int rv = InitHandle(
          handle_, "a", kDefaultPriority, &next_job_callback, pool_.get(),
          NULL);
      switch (next_job_type_) {
        case TestConnectJob::kMockJob:
          EXPECT_EQ(OK, rv);
          break;
        case TestConnectJob::kMockPendingJob:
          EXPECT_EQ(ERR_IO_PENDING, rv);

          // For pending jobs, wait for new socket to be created. This makes
          // sure there are no more pending operations nor any unclosed sockets
          // when the test finishes.
          // We need to give it a little bit of time to run, so that all the
          // operations that happen on timers (e.g. cleanup of idle
          // connections) can execute.
          {
            MessageLoop::ScopedNestableTaskAllower nestable(
                MessageLoop::current());
            PlatformThread::Sleep(10);
            EXPECT_EQ(OK, next_job_callback.WaitForResult());
          }
          break;
        default:
          FAIL() << "Unexpected job type: " << next_job_type_;
          break;
      }
    }
  }

  int WaitForResult() {
    return callback_.WaitForResult();
  }

 private:
  ClientSocketHandle* const handle_;
  const scoped_refptr<TestClientSocketPool> pool_;
  bool within_callback_;
  TestConnectJobFactory* const test_connect_job_factory_;
  TestConnectJob::JobType next_job_type_;
  TestCompletionCallback callback_;
};

TEST_F(ClientSocketPoolBaseTest, RequestPendingJobTwice) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
  ClientSocketHandle handle;
  RequestSocketCallback callback(
      &handle, pool_.get(), connect_job_factory_,
      TestConnectJob::kMockPendingJob);
  int rv = InitHandle(&handle, "a", kDefaultPriority, &callback,
                      pool_.get(), NULL);
  ASSERT_EQ(ERR_IO_PENDING, rv);

  EXPECT_EQ(OK, callback.WaitForResult());
}

TEST_F(ClientSocketPoolBaseTest, RequestPendingJobThenSynchronous) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
  ClientSocketHandle handle;
  RequestSocketCallback callback(
      &handle, pool_.get(), connect_job_factory_, TestConnectJob::kMockJob);
  int rv = InitHandle(&handle, "a", kDefaultPriority, &callback,
                      pool_.get(), NULL);
  ASSERT_EQ(ERR_IO_PENDING, rv);

  EXPECT_EQ(OK, callback.WaitForResult());
}

// Make sure that pending requests get serviced after active requests get
// cancelled.
TEST_F(ClientSocketPoolBaseTest, CancelActiveRequestWithPendingRequests) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);

  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));

  // Now, kDefaultMaxSocketsPerGroup requests should be active.
  // Let's cancel them.
  for (int i = 0; i < kDefaultMaxSocketsPerGroup; ++i) {
    ASSERT_FALSE(requests_[i]->handle()->is_initialized());
    requests_[i]->handle()->Reset();
  }

  // Let's wait for the rest to complete now.
  for (size_t i = kDefaultMaxSocketsPerGroup; i < requests_.size(); ++i) {
    EXPECT_EQ(OK, requests_[i]->WaitForResult());
    requests_[i]->handle()->Reset();
  }

  EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup, completion_count_);
}

// Make sure that pending requests get serviced after active requests fail.
TEST_F(ClientSocketPoolBaseTest, FailingActiveRequestWithPendingRequests) {
  const size_t kMaxSockets = 5;
  CreatePool(kMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);

  const size_t kNumberOfRequests = 2 * kDefaultMaxSocketsPerGroup + 1;
  ASSERT_LE(kNumberOfRequests, kMaxSockets);  // Otherwise the test will hang.

  // Queue up all the requests
  for (size_t i = 0; i < kNumberOfRequests; ++i)
    EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));

  for (size_t i = 0; i < kNumberOfRequests; ++i)
    EXPECT_EQ(ERR_CONNECTION_FAILED, requests_[i]->WaitForResult());
}

TEST_F(ClientSocketPoolBaseTest, CancelActiveRequestThenRequestSocket) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);

  TestSocketRequest req(&request_order_, &completion_count_);
  int rv = InitHandle(req.handle(), "a", kDefaultPriority, &req,
                      pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);

  // Cancel the active request.
  req.handle()->Reset();

  rv = InitHandle(req.handle(), "a", kDefaultPriority, &req,
                  pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);
  EXPECT_EQ(OK, req.WaitForResult());

  EXPECT_FALSE(req.handle()->is_reused());
  EXPECT_EQ(1U, completion_count_);
  EXPECT_EQ(2, client_socket_factory_.allocation_count());
}

// Regression test for http://crbug.com/17985.
TEST_F(ClientSocketPoolBaseTest, GroupWithPendingRequestsIsNotEmpty) {
  const int kMaxSockets = 3;
  const int kMaxSocketsPerGroup = 2;
  CreatePool(kMaxSockets, kMaxSocketsPerGroup);

  const RequestPriority kHighPriority = HIGHEST;

  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));

  // This is going to be a pending request in an otherwise empty group.
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));

  // Reach the maximum socket limit.
  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));

  // Create a stalled group with high priorities.
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kHighPriority));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kHighPriority));
  EXPECT_TRUE(pool_->base()->may_have_stalled_group());

  // Release the first two sockets from "a", which will make room
  // for requests from "c". After that "a" will have no active sockets
  // and one pending request.
  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));

  // Closing idle sockets should not get us into trouble, but in the bug
  // we were hitting a CHECK here.
  EXPECT_EQ(2, pool_->IdleSocketCountInGroup("a"));
  pool_->CloseIdleSockets();
  EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
}

class ClientSocketPoolBaseTest_LateBinding : public ClientSocketPoolBaseTest {
 protected:
  virtual void SetUp() {
    ClientSocketPoolBaseTest::SetUp();
  }
};

TEST_F(ClientSocketPoolBaseTest, BasicAsynchronous) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
  TestSocketRequest req(&request_order_, &completion_count_);
  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
  int rv = InitHandle(req.handle(), "a", LOWEST, &req, pool_.get(), log);
  EXPECT_EQ(ERR_IO_PENDING, rv);
  EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req.handle()));
  EXPECT_EQ(OK, req.WaitForResult());
  EXPECT_TRUE(req.handle()->is_initialized());
  EXPECT_TRUE(req.handle()->socket());
  req.handle()->Reset();

  EXPECT_EQ(6u, log->entries().size());
  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKET_POOL));
  EXPECT_TRUE(LogContainsBeginEvent(
      *log, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
  EXPECT_TRUE(LogContainsEndEvent(
      *log, 2, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
  EXPECT_TRUE(LogContainsBeginEvent(
      *log, 3, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEndEvent(
      *log, 4, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEndEvent(
      *log, 5, LoadLog::TYPE_SOCKET_POOL));
}

TEST_F(ClientSocketPoolBaseTest,
       InitConnectionAsynchronousFailure) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
  TestSocketRequest req(&request_order_, &completion_count_);
  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
  EXPECT_EQ(ERR_IO_PENDING,
            InitHandle(req.handle(), "a", kDefaultPriority, &req,
                       pool_.get(), log));
  EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req.handle()));
  EXPECT_EQ(ERR_CONNECTION_FAILED, req.WaitForResult());

  EXPECT_EQ(6u, log->entries().size());
  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKET_POOL));
  EXPECT_TRUE(LogContainsBeginEvent(
      *log, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
  EXPECT_TRUE(LogContainsEndEvent(
      *log, 2, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
  EXPECT_TRUE(LogContainsBeginEvent(
      *log, 3, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEndEvent(
      *log, 4, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEndEvent(*log, 5, LoadLog::TYPE_SOCKET_POOL));
}

TEST_F(ClientSocketPoolBaseTest, TwoRequestsCancelOne) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
  TestSocketRequest req(&request_order_, &completion_count_);
  TestSocketRequest req2(&request_order_, &completion_count_);

  scoped_refptr<LoadLog> log1(new LoadLog(LoadLog::kUnbounded));
  EXPECT_EQ(ERR_IO_PENDING,
            InitHandle(req.handle(), "a", kDefaultPriority, &req,
                       pool_.get(), log1));
  scoped_refptr<LoadLog> log2(new LoadLog(LoadLog::kUnbounded));
  EXPECT_EQ(ERR_IO_PENDING,
            InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
                       pool_.get(), log2));

  req.handle()->Reset();

  EXPECT_EQ(5u, log1->entries().size());
  EXPECT_TRUE(LogContainsBeginEvent(*log1, 0, LoadLog::TYPE_SOCKET_POOL));
  EXPECT_TRUE(LogContainsBeginEvent(
      *log1, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
  EXPECT_TRUE(LogContainsEndEvent(
      *log1, 2, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
  EXPECT_TRUE(LogContainsEvent(
      *log1, 3, LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE));
  EXPECT_TRUE(LogContainsEndEvent(*log1, 4, LoadLog::TYPE_SOCKET_POOL));

  // At this point, request 2 is just waiting for the connect job to finish.
  EXPECT_EQ(2u, log2->entries().size());
  EXPECT_TRUE(LogContainsBeginEvent(*log2, 0, LoadLog::TYPE_SOCKET_POOL));
  EXPECT_TRUE(LogContainsBeginEvent(
      *log2, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));

  EXPECT_EQ(OK, req2.WaitForResult());
  req2.handle()->Reset();

  // Now request 2 has actually finished.
  EXPECT_EQ(6u, log2->entries().size());
  EXPECT_TRUE(LogContainsBeginEvent(*log2, 0, LoadLog::TYPE_SOCKET_POOL));
  EXPECT_TRUE(LogContainsBeginEvent(
      *log2, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
  EXPECT_TRUE(LogContainsEndEvent(
      *log1, 2, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
  EXPECT_TRUE(LogContainsBeginEvent(
      *log2, 3, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEndEvent(
      *log2, 4, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
  EXPECT_TRUE(LogContainsEndEvent(*log2, 5, LoadLog::TYPE_SOCKET_POOL));

}

TEST_F(ClientSocketPoolBaseTest, CancelRequestLimitsJobs) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);

  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOW));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", MEDIUM));
  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", HIGHEST));

  EXPECT_EQ(kDefaultMaxSocketsPerGroup, pool_->NumConnectJobsInGroup("a"));
  requests_[2]->handle()->Reset();
  requests_[3]->handle()->Reset();
  EXPECT_EQ(kDefaultMaxSocketsPerGroup, pool_->NumConnectJobsInGroup("a"));

  requests_[1]->handle()->Reset();
  EXPECT_EQ(kDefaultMaxSocketsPerGroup, pool_->NumConnectJobsInGroup("a"));

  requests_[0]->handle()->Reset();
  EXPECT_EQ(kDefaultMaxSocketsPerGroup - 1, pool_->NumConnectJobsInGroup("a"));
}

// When requests and ConnectJobs are not coupled, the request will get serviced
// by whatever comes first.
TEST_F(ClientSocketPoolBaseTest, ReleaseSockets) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);

  // Start job 1 (async OK)
  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);

  TestSocketRequest req1(&request_order_, &completion_count_);
  int rv = InitHandle(req1.handle(), "a", kDefaultPriority,
                      &req1, pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);
  EXPECT_EQ(OK, req1.WaitForResult());

  // Job 1 finished OK.  Start job 2 (also async OK).  Request 3 is pending
  // without a job.
  connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);

  TestSocketRequest req2(&request_order_, &completion_count_);
  rv = InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
                  pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);
  TestSocketRequest req3(&request_order_, &completion_count_);
  rv = InitHandle(
      req3.handle(), "a", kDefaultPriority, &req3, pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);

  // Both Requests 2 and 3 are pending.  We release socket 1 which should
  // service request 2.  Request 3 should still be waiting.
  req1.handle()->Reset();
  MessageLoop::current()->RunAllPending();  // Run the DoReleaseSocket()
  ASSERT_TRUE(req2.handle()->socket());
  EXPECT_EQ(OK, req2.WaitForResult());
  EXPECT_FALSE(req3.handle()->socket());

  // Signal job 2, which should service request 3.

  client_socket_factory_.SignalJobs();
  EXPECT_EQ(OK, req3.WaitForResult());

  ASSERT_EQ(3U, request_order_.size());
  EXPECT_EQ(&req1, request_order_[0]);
  EXPECT_EQ(&req2, request_order_[1]);
  EXPECT_EQ(&req3, request_order_[2]);
  EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
}

// The requests are not coupled to the jobs.  So, the requests should finish in
// their priority / insertion order.
TEST_F(ClientSocketPoolBaseTest, PendingJobCompletionOrder) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
  // First two jobs are async.
  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);

  TestSocketRequest req1(&request_order_, &completion_count_);
  int rv = InitHandle(
      req1.handle(), "a", kDefaultPriority, &req1, pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);

  TestSocketRequest req2(&request_order_, &completion_count_);
  rv = InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
                  pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);

  // The pending job is sync.
  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);

  TestSocketRequest req3(&request_order_, &completion_count_);
  rv = InitHandle(
      req3.handle(), "a", kDefaultPriority, &req3, pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);

  EXPECT_EQ(ERR_CONNECTION_FAILED, req1.WaitForResult());
  EXPECT_EQ(OK, req2.WaitForResult());
  EXPECT_EQ(ERR_CONNECTION_FAILED, req3.WaitForResult());

  ASSERT_EQ(3U, request_order_.size());
  EXPECT_EQ(&req1, request_order_[0]);
  EXPECT_EQ(&req2, request_order_[1]);
  EXPECT_EQ(&req3, request_order_[2]);
}

TEST_F(ClientSocketPoolBaseTest, DISABLED_LoadState) {
  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
  connect_job_factory_->set_job_type(
      TestConnectJob::kMockAdvancingLoadStateJob);

  TestSocketRequest req1(&request_order_, &completion_count_);
  int rv = InitHandle(
      req1.handle(), "a", kDefaultPriority, &req1, pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);
  EXPECT_EQ(LOAD_STATE_IDLE, req1.handle()->GetLoadState());

  MessageLoop::current()->RunAllPending();

  TestSocketRequest req2(&request_order_, &completion_count_);
  rv = InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
                  pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);
  EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, req1.handle()->GetLoadState());
  EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, req2.handle()->GetLoadState());
}

TEST_F(ClientSocketPoolBaseTest, CleanupTimedOutIdleSockets) {
  CreatePoolWithIdleTimeouts(
      kDefaultMaxSockets, kDefaultMaxSocketsPerGroup,
      base::TimeDelta(),  // Time out unused sockets immediately.
      base::TimeDelta::FromDays(1));  // Don't time out used sockets.

  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);

  // Startup two mock pending connect jobs, which will sit in the MessageLoop.

  TestSocketRequest req(&request_order_, &completion_count_);
  int rv = InitHandle(req.handle(), "a", LOWEST, &req, pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);
  EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req.handle()));

  TestSocketRequest req2(&request_order_, &completion_count_);
  rv = InitHandle(req2.handle(), "a", LOWEST, &req2, pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);
  EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req2.handle()));

  // Cancel one of the requests.  Wait for the other, which will get the first
  // job.  Release the socket.  Run the loop again to make sure the second
  // socket is sitting idle and the first one is released (since ReleaseSocket()
  // just posts a DoReleaseSocket() task).

  req.handle()->Reset();
  EXPECT_EQ(OK, req2.WaitForResult());
  req2.handle()->Reset();

  // We post all of our delayed tasks with a 2ms delay. I.e. they don't
  // actually become pending until 2ms after they have been created. In order
  // to flush all tasks, we need to wait so that we know there are no
  // soon-to-be-pending tasks waiting.
  PlatformThread::Sleep(10);
  MessageLoop::current()->RunAllPending();

  ASSERT_EQ(2, pool_->IdleSocketCount());

  // Invoke the idle socket cleanup check.  Only one socket should be left, the
  // used socket.  Request it to make sure that it's used.

  pool_->CleanupTimedOutIdleSockets();
  rv = InitHandle(req.handle(), "a", LOWEST, &req, pool_.get(), NULL);
  EXPECT_EQ(OK, rv);
  EXPECT_TRUE(req.handle()->is_reused());
}

// Make sure that we process all pending requests even when we're stalling 
// because of multiple releasing disconnected sockets.
TEST_F(ClientSocketPoolBaseTest, MultipleReleasingDisconnectedSockets) {
  CreatePoolWithIdleTimeouts(
      kDefaultMaxSockets, kDefaultMaxSocketsPerGroup,
      base::TimeDelta(),  // Time out unused sockets immediately.
      base::TimeDelta::FromDays(1));  // Don't time out used sockets.

  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);

  // Startup 4 connect jobs.  Two of them will be pending.

  TestSocketRequest req(&request_order_, &completion_count_);
  int rv = InitHandle(req.handle(), "a", LOWEST, &req, pool_.get(), NULL);
  EXPECT_EQ(OK, rv);

  TestSocketRequest req2(&request_order_, &completion_count_);
  rv = InitHandle(req2.handle(), "a", LOWEST, &req2, pool_.get(), NULL);
  EXPECT_EQ(OK, rv);

  TestSocketRequest req3(&request_order_, &completion_count_);
  rv = InitHandle(req3.handle(), "a", LOWEST, &req3, pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);

  TestSocketRequest req4(&request_order_, &completion_count_);
  rv = InitHandle(req4.handle(), "a", LOWEST, &req4, pool_.get(), NULL);
  EXPECT_EQ(ERR_IO_PENDING, rv);

  // Release two disconnected sockets.

  req.handle()->socket()->Disconnect();
  req.handle()->Reset();
  req2.handle()->socket()->Disconnect();
  req2.handle()->Reset();

  EXPECT_EQ(OK, req3.WaitForResult());
  EXPECT_FALSE(req3.handle()->is_reused());
  EXPECT_EQ(OK, req4.WaitForResult());
  EXPECT_FALSE(req4.handle()->is_reused());
}

}  // namespace

}  // namespace net
