Refactor ConnectJob and TCPConnectJob.
ConnectJob will only call OnConnectJobComplete() on asynchronous completion.
TCPConnectJob now uses DoLoop() internally.
BUG=http://crbug.com/13289
TEST=none

Review URL: http://codereview.chromium.org/151118

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19789 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index ef9f15f8..7bb5ae7 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -134,11 +134,9 @@
                  const ClientSocketPoolBase::Request& request,
                  ConnectJob::Delegate* delegate,
                  ClientSocketFactory* client_socket_factory)
-      : job_type_(job_type),
-        group_name_(group_name),
-        handle_(request.handle),
+      : ConnectJob(group_name, request.handle, delegate),
+        job_type_(job_type),
         client_socket_factory_(client_socket_factory),
-        delegate_(delegate),
         method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
 
   // ConnectJob methods:
@@ -176,22 +174,19 @@
  private:
   int DoConnect(bool succeed, bool was_async) {
     int result = ERR_CONNECTION_FAILED;
-    ClientSocket* socket = NULL;
     if (succeed) {
       result = OK;
-      socket = new MockClientSocket();
-      socket->Connect(NULL);
+      set_socket(new MockClientSocket());
+      socket()->Connect(NULL);
     }
-    delegate_->OnConnectJobComplete(
-        group_name_, handle_, socket, result, was_async);
+
+    if (was_async)
+      delegate()->OnConnectJobComplete(result, this);
     return result;
   }
 
   const JobType job_type_;
-  const std::string group_name_;
-  const ClientSocketHandle* handle_;
   ClientSocketFactory* const client_socket_factory_;
-  Delegate* const delegate_;
   ScopedRunnableMethodFactory<TestConnectJob> method_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(TestConnectJob);
@@ -530,20 +525,35 @@
 
 class RequestSocketCallback : public CallbackRunner< Tuple1<int> > {
  public:
-  RequestSocketCallback(ClientSocketHandle* handle)
+  RequestSocketCallback(ClientSocketHandle* handle,
+                        TestConnectJobFactory* test_connect_job_factory,
+                        TestConnectJob::JobType next_job_type)
       : handle_(handle),
-        within_callback_(false) {}
+        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_);
       handle_->Reset();
       within_callback_ = true;
       int rv = handle_->Init(
           "a", HostResolver::RequestInfo("www.google.com", 80), 0, this);
-      EXPECT_EQ(ERR_IO_PENDING, rv);
+      switch (next_job_type_) {
+        case TestConnectJob::kMockJob:
+          EXPECT_EQ(OK, rv);
+          break;
+        case TestConnectJob::kMockPendingJob:
+          EXPECT_EQ(ERR_IO_PENDING, rv);
+          break;
+        default:
+          FAIL() << "Unexpected job type: " << next_job_type_;
+          break;
+      }
     }
   }
 
@@ -554,19 +564,34 @@
  private:
   ClientSocketHandle* const handle_;
   bool within_callback_;
+  TestConnectJobFactory* const test_connect_job_factory_;
+  TestConnectJob::JobType next_job_type_;
   TestCompletionCallback callback_;
 };
 
-TEST_F(ClientSocketPoolBaseTest, RequestTwice) {
+TEST_F(ClientSocketPoolBaseTest, RequestPendingJobTwice) {
   connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob); 
   ClientSocketHandle handle(pool_.get());
-  RequestSocketCallback callback(&handle);
+  RequestSocketCallback callback(
+      &handle, connect_job_factory_, TestConnectJob::kMockPendingJob);
   int rv = handle.Init(
       "a", ignored_request_info_, 0, &callback);
   ASSERT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(OK, callback.WaitForResult());
+  handle.Reset();
+}
 
+TEST_F(ClientSocketPoolBaseTest, RequestPendingJobThenSynchronous) {
+  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob); 
+  ClientSocketHandle handle(pool_.get());
+  RequestSocketCallback callback(
+      &handle, connect_job_factory_, TestConnectJob::kMockJob);
+  int rv = handle.Init(
+      "a", ignored_request_info_, 0, &callback);
+  ASSERT_EQ(ERR_IO_PENDING, rv);
+
+  EXPECT_EQ(OK, callback.WaitForResult());
   handle.Reset();
 }
 
@@ -615,6 +640,45 @@
     EXPECT_EQ(ERR_CONNECTION_FAILED, reqs[i]->WaitForResult());
 }
 
+// A pending asynchronous job completes, which will free up a socket slot.  The
+// next job finishes synchronously.  The callback for the asynchronous job
+// should be first though.
+TEST_F(ClientSocketPoolBaseTest, PendingJobCompletionOrder) {
+  // First two jobs are async.
+  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob); 
+
+  // Start job 1 (async error).
+  TestSocketRequest req1(pool_.get(), &request_order_);
+  int rv = req1.handle.Init("a", ignored_request_info_, 5, &req1);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Start job 2 (async error).
+  TestSocketRequest req2(pool_.get(), &request_order_);
+  rv = req2.handle.Init("a", ignored_request_info_, 5, &req2);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // The pending job is sync.
+  connect_job_factory_->set_job_type(TestConnectJob::kMockJob); 
+
+  // Request 3 does not have a ConnectJob yet.  It's just pending.
+  TestSocketRequest req3(pool_.get(), &request_order_);
+  rv = req3.handle.Init("a", ignored_request_info_, 5, &req3);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  EXPECT_EQ(ERR_CONNECTION_FAILED, req1.WaitForResult());
+  EXPECT_EQ(ERR_CONNECTION_FAILED, req2.WaitForResult());
+  EXPECT_EQ(OK, req3.WaitForResult());
+
+  ASSERT_EQ(3U, request_order_.size());
+
+  // After job 1 finishes unsuccessfully, it will try to process the pending
+  // requests queue, so it starts up job 3 for request 3.  This job
+  // synchronously succeeds, so the request order is 1, 3, 2.
+  EXPECT_EQ(&req1, request_order_[0]);
+  EXPECT_EQ(&req2, request_order_[2]);
+  EXPECT_EQ(&req3, request_order_[1]);
+}
+
 }  // namespace
 
 }  // namespace net