text: // Copyright (c) 2012 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 "jingle/glue/chrome_async_socket.h"
  
  #include <deque>
  #include <string>
  
  #include "base/basictypes.h"
  #include "base/logging.h"
  #include "base/memory/scoped_ptr.h"
  #include "base/message_loop.h"
  #include "jingle/glue/resolving_client_socket_factory.h"
  #include "net/base/address_list.h"
  #include "net/base/net_errors.h"
  #include "net/base/net_util.h"
  #include "net/cert/mock_cert_verifier.h"
  #include "net/http/transport_security_state.h"
  #include "net/socket/socket_test_util.h"
  #include "net/socket/ssl_client_socket.h"
  #include "net/ssl/ssl_config_service.h"
  #include "net/url_request/url_request_context_getter.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #include "third_party/libjingle/source/talk/base/ipaddress.h"
  #include "third_party/libjingle/source/talk/base/sigslot.h"
  #include "third_party/libjingle/source/talk/base/socketaddress.h"
  
  namespace jingle_glue {
  
  namespace {
  
  // Data provider that handles reads/writes for ChromeAsyncSocket.
  class AsyncSocketDataProvider : public net::SocketDataProvider {
   public:
    AsyncSocketDataProvider() : has_pending_read_(false) {}
  
    virtual ~AsyncSocketDataProvider() {
      EXPECT_TRUE(writes_.empty());
      EXPECT_TRUE(reads_.empty());
    }
  
    // If there's no read, sets the "has pending read" flag.  Otherwise,
    // pops the next read.
    virtual net::MockRead GetNextRead() OVERRIDE {
      if (reads_.empty()) {
        DCHECK(!has_pending_read_);
        has_pending_read_ = true;
        const net::MockRead pending_read(net::SYNCHRONOUS, net::ERR_IO_PENDING);
        return pending_read;
      }
      net::MockRead mock_read = reads_.front();
      reads_.pop_front();
      return mock_read;
    }
  
    // Simply pops the next write and, if applicable, compares it to
    // |data|.
    virtual net::MockWriteResult OnWrite(const std::string& data) OVERRIDE {
      DCHECK(!writes_.empty());
      net::MockWrite mock_write = writes_.front();
      writes_.pop_front();
      if (mock_write.result != net::OK) {
        return net::MockWriteResult(mock_write.mode, mock_write.result);
      }
      std::string expected_data(mock_write.data, mock_write.data_len);
      EXPECT_EQ(expected_data, data);
      if (expected_data != data) {
        return net::MockWriteResult(net::SYNCHRONOUS, net::ERR_UNEXPECTED);
      }
      return net::MockWriteResult(mock_write.mode, data.size());
    }
  
    // We ignore resets so we can pre-load the socket data provider with
    // read/write events.
    virtual void Reset() OVERRIDE {}
  
    // If there is a pending read, completes it with the given read.
    // Otherwise, queues up the given read.
    void AddRead(const net::MockRead& mock_read) {
      DCHECK_NE(mock_read.result, net::ERR_IO_PENDING);
      if (has_pending_read_) {
        socket()->OnReadComplete(mock_read);
        has_pending_read_ = false;
        return;
      }
      reads_.push_back(mock_read);
    }
  
    // Simply queues up the given write.
    void AddWrite(const net::MockWrite& mock_write) {
      writes_.push_back(mock_write);
    }
  
   private:
    std::deque<net::MockRead> reads_;
    bool has_pending_read_;
  
    std::deque<net::MockWrite> writes_;
  
    DISALLOW_COPY_AND_ASSIGN(AsyncSocketDataProvider);
  };
  
  class MockXmppClientSocketFactory : public ResolvingClientSocketFactory {
   public:
    MockXmppClientSocketFactory(
        net::ClientSocketFactory* mock_client_socket_factory,
        const net::AddressList& address_list)
            : mock_client_socket_factory_(mock_client_socket_factory),
              address_list_(address_list),
              cert_verifier_(new net::MockCertVerifier),
              transport_security_state_(new net::TransportSecurityState) {
    }
  
    // ResolvingClientSocketFactory implementation.
    virtual net::StreamSocket* CreateTransportClientSocket(
        const net::HostPortPair& host_and_port) OVERRIDE {
      return mock_client_socket_factory_->CreateTransportClientSocket(
          address_list_, NULL, net::NetLog::Source());
    }
  
    virtual net::SSLClientSocket* CreateSSLClientSocket(
        net::ClientSocketHandle* transport_socket,
        const net::HostPortPair& host_and_port) OVERRIDE {
      net::SSLClientSocketContext context;
      context.cert_verifier = cert_verifier_.get();
      context.transport_security_state = transport_security_state_.get();
      return mock_client_socket_factory_->CreateSSLClientSocket(
          transport_socket, host_and_port, ssl_config_, context);
    }
  
   private:
    scoped_ptr<net::ClientSocketFactory> mock_client_socket_factory_;
    net::AddressList address_list_;
    net::SSLConfig ssl_config_;
    scoped_ptr<net::CertVerifier> cert_verifier_;
    scoped_ptr<net::TransportSecurityState> transport_security_state_;
  };
  
  class ChromeAsyncSocketTest
      : public testing::Test,
        public sigslot::has_slots<> {
   protected:
    ChromeAsyncSocketTest()
        : ssl_socket_data_provider_(net::ASYNC, net::OK),
          addr_("localhost", 35) {}
  
    virtual ~ChromeAsyncSocketTest() {}
  
    virtual void SetUp() {
      scoped_ptr<net::MockClientSocketFactory> mock_client_socket_factory(
          new net::MockClientSocketFactory());
      mock_client_socket_factory->AddSocketDataProvider(
          &async_socket_data_provider_);
      mock_client_socket_factory->AddSSLSocketDataProvider(
          &ssl_socket_data_provider_);
  
      // Fake DNS resolution for |addr_| and pass it to the factory.
      net::IPAddressNumber resolved_addr;
      EXPECT_TRUE(net::ParseIPLiteralToNumber("127.0.0.1", &resolved_addr));
      const net::AddressList address_list =
          net::AddressList::CreateFromIPAddress(resolved_addr, addr_.port());
      scoped_ptr<MockXmppClientSocketFactory> mock_xmpp_client_socket_factory(
          new MockXmppClientSocketFactory(
              mock_client_socket_factory.release(),
              address_list));
      chrome_async_socket_.reset(
          new ChromeAsyncSocket(mock_xmpp_client_socket_factory.release(),
                                14, 20)),
  
      chrome_async_socket_->SignalConnected.connect(
          this, &ChromeAsyncSocketTest::OnConnect);
      chrome_async_socket_->SignalSSLConnected.connect(
          this, &ChromeAsyncSocketTest::OnSSLConnect);
      chrome_async_socket_->SignalClosed.connect(
          this, &ChromeAsyncSocketTest::OnClose);
      chrome_async_socket_->SignalRead.connect(
          this, &ChromeAsyncSocketTest::OnRead);
      chrome_async_socket_->SignalError.connect(
          this, &ChromeAsyncSocketTest::OnError);
    }
  
    virtual void TearDown() {
      // Run any tasks that we forgot to pump.
      message_loop_.RunUntilIdle();
      ExpectClosed();
      ExpectNoSignal();
      chrome_async_socket_.reset();
    }
  
    enum Signal {
      SIGNAL_CONNECT,
      SIGNAL_SSL_CONNECT,
      SIGNAL_CLOSE,
      SIGNAL_READ,
      SIGNAL_ERROR,
    };
  
    // Helper struct that records the state at the time of a signal.
  
    struct SignalSocketState {
      SignalSocketState()
          : signal(SIGNAL_ERROR),
            state(ChromeAsyncSocket::STATE_CLOSED),
            error(ChromeAsyncSocket::ERROR_NONE),
            net_error(net::OK) {}
  
      SignalSocketState(
          Signal signal,
          ChromeAsyncSocket::State state,
          ChromeAsyncSocket::Error error,
          net::Error net_error)
          : signal(signal),
            state(state),
            error(error),
            net_error(net_error) {}
  
      bool IsEqual(const SignalSocketState& other) const {
        return
            (signal == other.signal) &&
            (state == other.state) &&
            (error == other.error) &&
            (net_error == other.net_error);
      }
  
      static SignalSocketState FromAsyncSocket(
          Signal signal,
          buzz::AsyncSocket* async_socket) {
        return SignalSocketState(signal,
                                 async_socket->state(),
                                 async_socket->error(),
                                 static_cast<net::Error>(
                                     async_socket->GetError()));
      }
  
      static SignalSocketState NoError(
          Signal signal, buzz::AsyncSocket::State state) {
          return SignalSocketState(signal, state,
                                   buzz::AsyncSocket::ERROR_NONE,
                                   net::OK);
      }
  
      Signal signal;
      ChromeAsyncSocket::State state;
      ChromeAsyncSocket::Error error;
      net::Error net_error;
    };
  
    void CaptureSocketState(Signal signal) {
      signal_socket_states_.push_back(
          SignalSocketState::FromAsyncSocket(
              signal, chrome_async_socket_.get()));
    }
  
    void OnConnect() {
      CaptureSocketState(SIGNAL_CONNECT);
    }
  
    void OnSSLConnect() {
      CaptureSocketState(SIGNAL_SSL_CONNECT);
    }
  
    void OnClose() {
      CaptureSocketState(SIGNAL_CLOSE);
    }
  
    void OnRead() {
      CaptureSocketState(SIGNAL_READ);
    }
  
    void OnError() {
      ADD_FAILURE();
    }
  
    // State expect functions.
  
    void ExpectState(ChromeAsyncSocket::State state,
                     ChromeAsyncSocket::Error error,
                     net::Error net_error) {
      EXPECT_EQ(state, chrome_async_socket_->state());
      EXPECT_EQ(error, chrome_async_socket_->error());
      EXPECT_EQ(net_error, chrome_async_socket_->GetError());
    }
  
    void ExpectNonErrorState(ChromeAsyncSocket::State state) {
      ExpectState(state, ChromeAsyncSocket::ERROR_NONE, net::OK);
    }
  
    void ExpectErrorState(ChromeAsyncSocket::State state,
                          ChromeAsyncSocket::Error error) {
      ExpectState(state, error, net::OK);
    }
  
    void ExpectClosed() {
      ExpectNonErrorState(ChromeAsyncSocket::STATE_CLOSED);
    }
  
    // Signal expect functions.
  
    void ExpectNoSignal() {
      if (!signal_socket_states_.empty()) {
        ADD_FAILURE() << signal_socket_states_.front().signal;
      }
    }
  
    void ExpectSignalSocketState(
        SignalSocketState expected_signal_socket_state) {
      if (signal_socket_states_.empty()) {
        ADD_FAILURE() << expected_signal_socket_state.signal;
        return;
      }
      EXPECT_TRUE(expected_signal_socket_state.IsEqual(
          signal_socket_states_.front()))
          << signal_socket_states_.front().signal;
      signal_socket_states_.pop_front();
    }
  
    void ExpectReadSignal() {
      ExpectSignalSocketState(
          SignalSocketState::NoError(
              SIGNAL_READ, ChromeAsyncSocket::STATE_OPEN));
    }
  
    void ExpectSSLConnectSignal() {
      ExpectSignalSocketState(
          SignalSocketState::NoError(SIGNAL_SSL_CONNECT,
                                     ChromeAsyncSocket::STATE_TLS_OPEN));
    }
  
    void ExpectSSLReadSignal() {
      ExpectSignalSocketState(
          SignalSocketState::NoError(
              SIGNAL_READ, ChromeAsyncSocket::STATE_TLS_OPEN));
    }
  
    // Open/close utility functions.
  
    void DoOpenClosed() {
      ExpectClosed();
      async_socket_data_provider_.set_connect_data(
          net::MockConnect(net::SYNCHRONOUS, net::OK));
      EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
      ExpectNonErrorState(ChromeAsyncSocket::STATE_CONNECTING);
  
      message_loop_.RunUntilIdle();
      // We may not necessarily be open; may have been other events
      // queued up.
      ExpectSignalSocketState(
          SignalSocketState::NoError(
              SIGNAL_CONNECT, ChromeAsyncSocket::STATE_OPEN));
    }
  
    void DoCloseOpened(SignalSocketState expected_signal_socket_state) {
      // We may be in an error state, so just compare state().
      EXPECT_EQ(ChromeAsyncSocket::STATE_OPEN, chrome_async_socket_->state());
      EXPECT_TRUE(chrome_async_socket_->Close());
      ExpectSignalSocketState(expected_signal_socket_state);
      ExpectNonErrorState(ChromeAsyncSocket::STATE_CLOSED);
    }
  
    void DoCloseOpenedNoError() {
      DoCloseOpened(
          SignalSocketState::NoError(
              SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
    }
  
    void DoSSLOpenClosed() {
      const char kDummyData[] = "dummy_data";
      async_socket_data_provider_.AddRead(net::MockRead(kDummyData));
      DoOpenClosed();
      ExpectReadSignal();
      EXPECT_EQ(kDummyData, DrainRead(1));
  
      EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
      message_loop_.RunUntilIdle();
      ExpectSSLConnectSignal();
      ExpectNoSignal();
      ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_OPEN);
    }
  
    void DoSSLCloseOpened(SignalSocketState expected_signal_socket_state) {
      // We may be in an error state, so just compare state().
      EXPECT_EQ(ChromeAsyncSocket::STATE_TLS_OPEN,
                chrome_async_socket_->state());
      EXPECT_TRUE(chrome_async_socket_->Close());
      ExpectSignalSocketState(expected_signal_socket_state);
      ExpectNonErrorState(ChromeAsyncSocket::STATE_CLOSED);
    }
  
    void DoSSLCloseOpenedNoError() {
      DoSSLCloseOpened(
          SignalSocketState::NoError(
              SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
    }
  
    // Read utility fucntions.
  
    std::string DrainRead(size_t buf_size) {
      std::string read;
      scoped_ptr<char[]> buf(new char[buf_size]);
      size_t len_read;
      while (true) {
        bool success =
            chrome_async_socket_->Read(buf.get(), buf_size, &len_read);
        if (!success) {
          ADD_FAILURE();
          break;
        }
        if (len_read == 0U) {
          break;
        }
        read.append(buf.get(), len_read);
      }
      return read;
    }
  
    // ChromeAsyncSocket expects a message loop.
    base::MessageLoop message_loop_;
  
    AsyncSocketDataProvider async_socket_data_provider_;
    net::SSLSocketDataProvider ssl_socket_data_provider_;
  
    scoped_ptr<ChromeAsyncSocket> chrome_async_socket_;
    std::deque<SignalSocketState> signal_socket_states_;
    const talk_base::SocketAddress addr_;
  
   private:
    DISALLOW_COPY_AND_ASSIGN(ChromeAsyncSocketTest);
  };
  
  TEST_F(ChromeAsyncSocketTest, InitialState) {
    ExpectClosed();
    ExpectNoSignal();
  }
  
  TEST_F(ChromeAsyncSocketTest, EmptyClose) {
    ExpectClosed();
    EXPECT_TRUE(chrome_async_socket_->Close());
    ExpectClosed();
  }
  
  TEST_F(ChromeAsyncSocketTest, ImmediateConnectAndClose) {
    DoOpenClosed();
  
    ExpectNonErrorState(ChromeAsyncSocket::STATE_OPEN);
  
    DoCloseOpenedNoError();
  }
  
  // After this, no need to test immediate successful connect and
  // Close() so thoroughly.
  
  TEST_F(ChromeAsyncSocketTest, DoubleClose) {
    DoOpenClosed();
  
    EXPECT_TRUE(chrome_async_socket_->Close());
    ExpectClosed();
    ExpectSignalSocketState(
        SignalSocketState::NoError(
            SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
  
    EXPECT_TRUE(chrome_async_socket_->Close());
    ExpectClosed();
  }
  
  TEST_F(ChromeAsyncSocketTest, NoHostnameConnect) {
    talk_base::IPAddress ip_address;
    EXPECT_TRUE(talk_base::IPFromString("127.0.0.1", &ip_address));
    const talk_base::SocketAddress no_hostname_addr(ip_address, addr_.port());
    EXPECT_FALSE(chrome_async_socket_->Connect(no_hostname_addr));
    ExpectErrorState(ChromeAsyncSocket::STATE_CLOSED,
                     ChromeAsyncSocket::ERROR_DNS);
  
    EXPECT_TRUE(chrome_async_socket_->Close());
    ExpectClosed();
  }
  
  TEST_F(ChromeAsyncSocketTest, ZeroPortConnect) {
    const talk_base::SocketAddress zero_port_addr(addr_.hostname(), 0);
    EXPECT_FALSE(chrome_async_socket_->Connect(zero_port_addr));
    ExpectErrorState(ChromeAsyncSocket::STATE_CLOSED,
                     ChromeAsyncSocket::ERROR_DNS);
  
    EXPECT_TRUE(chrome_async_socket_->Close());
    ExpectClosed();
  }
  
  TEST_F(ChromeAsyncSocketTest, DoubleConnect) {
    EXPECT_DEBUG_DEATH({
      DoOpenClosed();
  
      EXPECT_FALSE(chrome_async_socket_->Connect(addr_));
      ExpectErrorState(ChromeAsyncSocket::STATE_OPEN,
                       ChromeAsyncSocket::ERROR_WRONGSTATE);
  
      DoCloseOpened(
          SignalSocketState(SIGNAL_CLOSE,
                            ChromeAsyncSocket::STATE_CLOSED,
                            ChromeAsyncSocket::ERROR_WRONGSTATE,
                            net::OK));
    }, "non-closed socket");
  }
  
  TEST_F(ChromeAsyncSocketTest, ImmediateConnectCloseBeforeRead) {
    DoOpenClosed();
  
    EXPECT_TRUE(chrome_async_socket_->Close());
    ExpectClosed();
    ExpectSignalSocketState(
        SignalSocketState::NoError(
            SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
  
    message_loop_.RunUntilIdle();
  }
  
  TEST_F(ChromeAsyncSocketTest, HangingConnect) {
    EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
    ExpectNonErrorState(ChromeAsyncSocket::STATE_CONNECTING);
    ExpectNoSignal();
  
    EXPECT_TRUE(chrome_async_socket_->Close());
    ExpectClosed();
    ExpectSignalSocketState(
        SignalSocketState::NoError(
            SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
  }
  
  TEST_F(ChromeAsyncSocketTest, PendingConnect) {
    async_socket_data_provider_.set_connect_data(
        net::MockConnect(net::ASYNC, net::OK));
    EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
    ExpectNonErrorState(ChromeAsyncSocket::STATE_CONNECTING);
    ExpectNoSignal();
  
    message_loop_.RunUntilIdle();
    ExpectNonErrorState(ChromeAsyncSocket::STATE_OPEN);
    ExpectSignalSocketState(
        SignalSocketState::NoError(
            SIGNAL_CONNECT, ChromeAsyncSocket::STATE_OPEN));
    ExpectNoSignal();
  
    message_loop_.RunUntilIdle();
  
    DoCloseOpenedNoError();
  }
  
  // After this no need to test successful pending connect so
  // thoroughly.
  
  TEST_F(ChromeAsyncSocketTest, PendingConnectCloseBeforeRead) {
    async_socket_data_provider_.set_connect_data(
        net::MockConnect(net::ASYNC, net::OK));
    EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
  
    message_loop_.RunUntilIdle();
    ExpectSignalSocketState(
        SignalSocketState::NoError(
            SIGNAL_CONNECT, ChromeAsyncSocket::STATE_OPEN));
  
    DoCloseOpenedNoError();
  
    message_loop_.RunUntilIdle();
  }
  
  TEST_F(ChromeAsyncSocketTest, PendingConnectError) {
    async_socket_data_provider_.set_connect_data(
        net::MockConnect(net::ASYNC, net::ERR_TIMED_OUT));
    EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
  
    message_loop_.RunUntilIdle();
  
    ExpectSignalSocketState(
        SignalSocketState(
            SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
            ChromeAsyncSocket::ERROR_WINSOCK, net::ERR_TIMED_OUT));
  }
  
  // After this we can assume Connect() and Close() work as expected.
  
  TEST_F(ChromeAsyncSocketTest, EmptyRead) {
    DoOpenClosed();
  
    char buf[4096];
    size_t len_read = 10000U;
    EXPECT_TRUE(chrome_async_socket_->Read(buf, sizeof(buf), &len_read));
    EXPECT_EQ(0U, len_read);
  
    DoCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, WrongRead) {
    EXPECT_DEBUG_DEATH({
      async_socket_data_provider_.set_connect_data(
          net::MockConnect(net::ASYNC, net::OK));
      EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
      ExpectNonErrorState(ChromeAsyncSocket::STATE_CONNECTING);
      ExpectNoSignal();
  
      char buf[4096];
      size_t len_read;
      EXPECT_FALSE(chrome_async_socket_->Read(buf, sizeof(buf), &len_read));
      ExpectErrorState(ChromeAsyncSocket::STATE_CONNECTING,
                       ChromeAsyncSocket::ERROR_WRONGSTATE);
      EXPECT_TRUE(chrome_async_socket_->Close());
      ExpectSignalSocketState(
          SignalSocketState(
              SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
              ChromeAsyncSocket::ERROR_WRONGSTATE, net::OK));
    }, "non-open");
  }
  
  TEST_F(ChromeAsyncSocketTest, WrongReadClosed) {
    char buf[4096];
    size_t len_read;
    EXPECT_FALSE(chrome_async_socket_->Read(buf, sizeof(buf), &len_read));
    ExpectErrorState(ChromeAsyncSocket::STATE_CLOSED,
                     ChromeAsyncSocket::ERROR_WRONGSTATE);
    EXPECT_TRUE(chrome_async_socket_->Close());
  }
  
  const char kReadData[] = "mydatatoread";
  
  TEST_F(ChromeAsyncSocketTest, Read) {
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
    DoOpenClosed();
  
    ExpectReadSignal();
    ExpectNoSignal();
  
    EXPECT_EQ(kReadData, DrainRead(1));
  
    message_loop_.RunUntilIdle();
  
    DoCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, ReadTwice) {
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
    DoOpenClosed();
  
    ExpectReadSignal();
    ExpectNoSignal();
  
    EXPECT_EQ(kReadData, DrainRead(1));
  
    message_loop_.RunUntilIdle();
  
    const char kReadData2[] = "mydatatoread2";
    async_socket_data_provider_.AddRead(net::MockRead(kReadData2));
  
    ExpectReadSignal();
    ExpectNoSignal();
  
    EXPECT_EQ(kReadData2, DrainRead(1));
  
    DoCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, ReadError) {
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
    DoOpenClosed();
  
    ExpectReadSignal();
    ExpectNoSignal();
  
    EXPECT_EQ(kReadData, DrainRead(1));
  
    message_loop_.RunUntilIdle();
  
    async_socket_data_provider_.AddRead(
        net::MockRead(net::SYNCHRONOUS, net::ERR_TIMED_OUT));
  
    ExpectSignalSocketState(
        SignalSocketState(
            SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
            ChromeAsyncSocket::ERROR_WINSOCK, net::ERR_TIMED_OUT));
  }
  
  TEST_F(ChromeAsyncSocketTest, ReadEmpty) {
    async_socket_data_provider_.AddRead(net::MockRead(""));
    DoOpenClosed();
  
    ExpectSignalSocketState(
        SignalSocketState::NoError(
            SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
  }
  
  TEST_F(ChromeAsyncSocketTest, PendingRead) {
    DoOpenClosed();
  
    ExpectNoSignal();
  
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
  
    ExpectSignalSocketState(
        SignalSocketState::NoError(
            SIGNAL_READ, ChromeAsyncSocket::STATE_OPEN));
    ExpectNoSignal();
  
    EXPECT_EQ(kReadData, DrainRead(1));
  
    message_loop_.RunUntilIdle();
  
    DoCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, PendingEmptyRead) {
    DoOpenClosed();
  
    ExpectNoSignal();
  
    async_socket_data_provider_.AddRead(net::MockRead(""));
  
    ExpectSignalSocketState(
        SignalSocketState::NoError(
            SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
  }
  
  TEST_F(ChromeAsyncSocketTest, PendingReadError) {
    DoOpenClosed();
  
    ExpectNoSignal();
  
    async_socket_data_provider_.AddRead(
        net::MockRead(net::ASYNC, net::ERR_TIMED_OUT));
  
    ExpectSignalSocketState(
        SignalSocketState(
            SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
            ChromeAsyncSocket::ERROR_WINSOCK, net::ERR_TIMED_OUT));
  }
  
  // After this we can assume non-SSL Read() works as expected.
  
  TEST_F(ChromeAsyncSocketTest, WrongWrite) {
    EXPECT_DEBUG_DEATH({
      std::string data("foo");
      EXPECT_FALSE(chrome_async_socket_->Write(data.data(), data.size()));
      ExpectErrorState(ChromeAsyncSocket::STATE_CLOSED,
                       ChromeAsyncSocket::ERROR_WRONGSTATE);
      EXPECT_TRUE(chrome_async_socket_->Close());
    }, "non-open");
  }
  
  const char kWriteData[] = "mydatatowrite";
  
  TEST_F(ChromeAsyncSocketTest, SyncWrite) {
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::SYNCHRONOUS, kWriteData, 3));
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::SYNCHRONOUS, kWriteData + 3, 5));
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::SYNCHRONOUS,
                       kWriteData + 8, arraysize(kWriteData) - 8));
    DoOpenClosed();
  
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
    message_loop_.RunUntilIdle();
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 3, 5));
    message_loop_.RunUntilIdle();
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 8,
                                            arraysize(kWriteData) - 8));
    message_loop_.RunUntilIdle();
  
    ExpectNoSignal();
  
    DoCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, AsyncWrite) {
    DoOpenClosed();
  
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::ASYNC, kWriteData, 3));
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::ASYNC, kWriteData + 3, 5));
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::ASYNC, kWriteData + 8, arraysize(kWriteData) - 8));
  
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
    message_loop_.RunUntilIdle();
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 3, 5));
    message_loop_.RunUntilIdle();
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 8,
                                            arraysize(kWriteData) - 8));
    message_loop_.RunUntilIdle();
  
    ExpectNoSignal();
  
    DoCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, AsyncWriteError) {
    DoOpenClosed();
  
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::ASYNC, kWriteData, 3));
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::ASYNC, kWriteData + 3, 5));
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::ASYNC, net::ERR_TIMED_OUT));
  
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
    message_loop_.RunUntilIdle();
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 3, 5));
    message_loop_.RunUntilIdle();
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 8,
                                            arraysize(kWriteData) - 8));
    message_loop_.RunUntilIdle();
  
    ExpectSignalSocketState(
        SignalSocketState(
            SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
            ChromeAsyncSocket::ERROR_WINSOCK, net::ERR_TIMED_OUT));
  }
  
  TEST_F(ChromeAsyncSocketTest, LargeWrite) {
    EXPECT_DEBUG_DEATH({
      DoOpenClosed();
  
      std::string large_data(100, 'x');
      EXPECT_FALSE(chrome_async_socket_->Write(large_data.data(),
                                               large_data.size()));
      ExpectState(ChromeAsyncSocket::STATE_OPEN,
                  ChromeAsyncSocket::ERROR_WINSOCK,
                  net::ERR_INSUFFICIENT_RESOURCES);
      DoCloseOpened(
          SignalSocketState(
              SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
              ChromeAsyncSocket::ERROR_WINSOCK,
              net::ERR_INSUFFICIENT_RESOURCES));
      }, "exceed the max write buffer");
  }
  
  TEST_F(ChromeAsyncSocketTest, LargeAccumulatedWrite) {
    EXPECT_DEBUG_DEATH({
      DoOpenClosed();
  
      std::string data(15, 'x');
      EXPECT_TRUE(chrome_async_socket_->Write(data.data(), data.size()));
      EXPECT_FALSE(chrome_async_socket_->Write(data.data(), data.size()));
      ExpectState(ChromeAsyncSocket::STATE_OPEN,
                  ChromeAsyncSocket::ERROR_WINSOCK,
                  net::ERR_INSUFFICIENT_RESOURCES);
      DoCloseOpened(
          SignalSocketState(
              SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
              ChromeAsyncSocket::ERROR_WINSOCK,
              net::ERR_INSUFFICIENT_RESOURCES));
      }, "exceed the max write buffer");
  }
  
  // After this we can assume non-SSL I/O works as expected.
  
  TEST_F(ChromeAsyncSocketTest, HangingSSLConnect) {
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
    DoOpenClosed();
    ExpectReadSignal();
  
    EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
    ExpectNoSignal();
  
    ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_CONNECTING);
    EXPECT_TRUE(chrome_async_socket_->Close());
    ExpectSignalSocketState(
        SignalSocketState::NoError(SIGNAL_CLOSE,
                                   ChromeAsyncSocket::STATE_CLOSED));
    ExpectNonErrorState(ChromeAsyncSocket::STATE_CLOSED);
  }
  
  TEST_F(ChromeAsyncSocketTest, ImmediateSSLConnect) {
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
    DoOpenClosed();
    ExpectReadSignal();
  
    EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
    message_loop_.RunUntilIdle();
    ExpectSSLConnectSignal();
    ExpectNoSignal();
    ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_OPEN);
  
    DoSSLCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, DoubleSSLConnect) {
    EXPECT_DEBUG_DEATH({
      async_socket_data_provider_.AddRead(net::MockRead(kReadData));
      DoOpenClosed();
      ExpectReadSignal();
  
      EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
      message_loop_.RunUntilIdle();
      ExpectSSLConnectSignal();
      ExpectNoSignal();
      ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_OPEN);
  
      EXPECT_FALSE(chrome_async_socket_->StartTls("fakedomain.com"));
  
      DoSSLCloseOpened(
          SignalSocketState(SIGNAL_CLOSE,
                            ChromeAsyncSocket::STATE_CLOSED,
                            ChromeAsyncSocket::ERROR_WRONGSTATE,
                            net::OK));
    }, "wrong state");
  }
  
  TEST_F(ChromeAsyncSocketTest, FailedSSLConnect) {
    ssl_socket_data_provider_.connect =
        net::MockConnect(net::ASYNC, net::ERR_CERT_COMMON_NAME_INVALID),
  
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
    DoOpenClosed();
    ExpectReadSignal();
  
    EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
    message_loop_.RunUntilIdle();
    ExpectSignalSocketState(
        SignalSocketState(
            SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
            ChromeAsyncSocket::ERROR_WINSOCK,
            net::ERR_CERT_COMMON_NAME_INVALID));
  
    EXPECT_TRUE(chrome_async_socket_->Close());
    ExpectClosed();
  }
  
  TEST_F(ChromeAsyncSocketTest, ReadDuringSSLConnecting) {
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
    DoOpenClosed();
    ExpectReadSignal();
    EXPECT_EQ(kReadData, DrainRead(1));
  
    EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
    ExpectNoSignal();
  
    // Shouldn't do anything.
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
  
    char buf[4096];
    size_t len_read = 10000U;
    EXPECT_TRUE(chrome_async_socket_->Read(buf, sizeof(buf), &len_read));
    EXPECT_EQ(0U, len_read);
  
    message_loop_.RunUntilIdle();
    ExpectSSLConnectSignal();
    ExpectSSLReadSignal();
    ExpectNoSignal();
    ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_OPEN);
  
    len_read = 10000U;
    EXPECT_TRUE(chrome_async_socket_->Read(buf, sizeof(buf), &len_read));
    EXPECT_EQ(kReadData, std::string(buf, len_read));
  
    DoSSLCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, WriteDuringSSLConnecting) {
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
    DoOpenClosed();
    ExpectReadSignal();
  
    EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
    ExpectNoSignal();
    ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_CONNECTING);
  
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::ASYNC, kWriteData, 3));
  
    // Shouldn't do anything.
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
  
    // TODO(akalin): Figure out how to test that the write happens
    // *after* the SSL connect.
  
    message_loop_.RunUntilIdle();
    ExpectSSLConnectSignal();
    ExpectNoSignal();
  
    message_loop_.RunUntilIdle();
  
    DoSSLCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, SSLConnectDuringPendingRead) {
    EXPECT_DEBUG_DEATH({
      DoOpenClosed();
  
      EXPECT_FALSE(chrome_async_socket_->StartTls("fakedomain.com"));
  
      DoCloseOpened(
          SignalSocketState(SIGNAL_CLOSE,
                            ChromeAsyncSocket::STATE_CLOSED,
                            ChromeAsyncSocket::ERROR_WRONGSTATE,
                            net::OK));
    }, "wrong state");
  }
  
  TEST_F(ChromeAsyncSocketTest, SSLConnectDuringPostedWrite) {
    EXPECT_DEBUG_DEATH({
      DoOpenClosed();
  
      async_socket_data_provider_.AddWrite(
          net::MockWrite(net::ASYNC, kWriteData, 3));
      EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
  
      EXPECT_FALSE(chrome_async_socket_->StartTls("fakedomain.com"));
  
      message_loop_.RunUntilIdle();
  
      DoCloseOpened(
          SignalSocketState(SIGNAL_CLOSE,
                            ChromeAsyncSocket::STATE_CLOSED,
                            ChromeAsyncSocket::ERROR_WRONGSTATE,
                            net::OK));
    }, "wrong state");
  }
  
  // After this we can assume SSL connect works as expected.
  
  TEST_F(ChromeAsyncSocketTest, SSLRead) {
    DoSSLOpenClosed();
    async_socket_data_provider_.AddRead(net::MockRead(kReadData));
    message_loop_.RunUntilIdle();
  
    ExpectSSLReadSignal();
    ExpectNoSignal();
  
    EXPECT_EQ(kReadData, DrainRead(1));
  
    message_loop_.RunUntilIdle();
  
    DoSSLCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, SSLSyncWrite) {
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::SYNCHRONOUS, kWriteData, 3));
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::SYNCHRONOUS, kWriteData + 3, 5));
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::SYNCHRONOUS,
                       kWriteData + 8, arraysize(kWriteData) - 8));
    DoSSLOpenClosed();
  
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
    message_loop_.RunUntilIdle();
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 3, 5));
    message_loop_.RunUntilIdle();
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 8,
                                            arraysize(kWriteData) - 8));
    message_loop_.RunUntilIdle();
  
    ExpectNoSignal();
  
    DoSSLCloseOpenedNoError();
  }
  
  TEST_F(ChromeAsyncSocketTest, SSLAsyncWrite) {
    DoSSLOpenClosed();
  
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::ASYNC, kWriteData, 3));
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::ASYNC, kWriteData + 3, 5));
    async_socket_data_provider_.AddWrite(
        net::MockWrite(net::ASYNC, kWriteData + 8, arraysize(kWriteData) - 8));
  
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
    message_loop_.RunUntilIdle();
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 3, 5));
    message_loop_.RunUntilIdle();
    EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 8,
                                            arraysize(kWriteData) - 8));
    message_loop_.RunUntilIdle();
  
    ExpectNoSignal();
  
    DoSSLCloseOpenedNoError();
  }
  
  }  // namespace
  
  }  // namespace jingle_glue
  