Chromium Code Reviews| Index: webrtc/base/physicalsocketserver_unittest.cc |
| diff --git a/webrtc/base/physicalsocketserver_unittest.cc b/webrtc/base/physicalsocketserver_unittest.cc |
| index a2fde80b42c951c640e80c1ef9fc28f8f0547176..b6a45c3e77daa39a4d50ac8bb63317bc8d253d51 100644 |
| --- a/webrtc/base/physicalsocketserver_unittest.cc |
| +++ b/webrtc/base/physicalsocketserver_unittest.cc |
| @@ -29,8 +29,15 @@ class FakeSocketDispatcher : public SocketDispatcher { |
| : SocketDispatcher(ss) { |
| } |
| + FakeSocketDispatcher(SOCKET s, PhysicalSocketServer* ss) |
| + : SocketDispatcher(s, ss) { |
| + } |
| + |
| protected: |
| SOCKET DoAccept(SOCKET socket, sockaddr* addr, socklen_t* addrlen) override; |
| + int DoSend(SOCKET socket, const char* buf, int len, int flags) override; |
| + int DoSendTo(SOCKET socket, const char* buf, int len, int flags, |
| + const struct sockaddr* dest_addr, socklen_t addrlen) override; |
| }; |
| class FakePhysicalSocketServer : public PhysicalSocketServer { |
| @@ -59,6 +66,16 @@ class FakePhysicalSocketServer : public PhysicalSocketServer { |
| } |
| } |
| + AsyncSocket* WrapSocket(SOCKET s) override { |
| + SocketDispatcher* dispatcher = new FakeSocketDispatcher(s, this); |
| + if (dispatcher->Initialize()) { |
| + return dispatcher; |
| + } else { |
| + delete dispatcher; |
| + return nullptr; |
| + } |
|
pthatcher1
2016/01/30 00:03:10
Can you flip this around?
if (!dispatcher->Initia
joachim
2016/01/31 23:29:07
Done. This was implemented similar to the existing
|
| + } |
| + |
| PhysicalSocketTest* GetTest() const { return test_; } |
| private: |
| @@ -71,18 +88,25 @@ class PhysicalSocketTest : public SocketTest { |
| void SetFailAccept(bool fail) { fail_accept_ = fail; } |
| bool FailAccept() const { return fail_accept_; } |
| + // Maximum size to ::send to a socket. Set to 0 to disable limiting. |
|
pthatcher1
2016/01/30 00:03:10
Can you make "disabled limited" < 0, such as -1.
joachim
2016/01/31 23:29:06
Done.
|
| + void SetMaximumSendSize(int max_size) { max_send_size_ = max_size; } |
| + int MaximumSendSize() const { return max_send_size_; } |
|
pthatcher1
2016/01/30 00:03:10
Please change MaximumSendSize => MaxSendSize in bo
joachim
2016/01/31 23:29:07
Done.
|
| + |
| protected: |
| PhysicalSocketTest() |
| : server_(new FakePhysicalSocketServer(this)), |
| scope_(server_.get()), |
| - fail_accept_(false) { |
| + fail_accept_(false), |
| + max_send_size_(0) { |
| } |
| void ConnectInternalAcceptError(const IPAddress& loopback); |
| + void PartialWriteStayWritable(const IPAddress& loopback); |
|
pthatcher1
2016/01/30 00:03:10
Can you name this WritableAfterPartialWrite?
joachim
2016/01/31 23:29:06
Done.
|
| rtc::scoped_ptr<FakePhysicalSocketServer> server_; |
| SocketServerScope scope_; |
| bool fail_accept_; |
| + int max_send_size_; |
| }; |
| SOCKET FakeSocketDispatcher::DoAccept(SOCKET socket, |
| @@ -97,6 +121,29 @@ SOCKET FakeSocketDispatcher::DoAccept(SOCKET socket, |
| return SocketDispatcher::DoAccept(socket, addr, addrlen); |
| } |
| +int FakeSocketDispatcher::DoSend(SOCKET socket, const char* buf, int len, |
| + int flags) { |
| + FakePhysicalSocketServer* ss = |
| + static_cast<FakePhysicalSocketServer*>(socketserver()); |
| + if (ss->GetTest()->MaximumSendSize() > 0) { |
| + len = std::min(len, ss->GetTest()->MaximumSendSize()); |
| + } |
| + |
| + return SocketDispatcher::DoSend(socket, buf, len, flags); |
| +} |
| + |
| +int FakeSocketDispatcher::DoSendTo(SOCKET socket, const char* buf, int len, |
| + int flags, const struct sockaddr* dest_addr, socklen_t addrlen) { |
| + FakePhysicalSocketServer* ss = |
| + static_cast<FakePhysicalSocketServer*>(socketserver()); |
| + if (ss->GetTest()->MaximumSendSize() > 0) { |
| + len = std::min(len, ss->GetTest()->MaximumSendSize()); |
| + } |
| + |
| + return SocketDispatcher::DoSendTo(socket, buf, len, flags, dest_addr, |
| + addrlen); |
| +} |
| + |
| TEST_F(PhysicalSocketTest, TestConnectIPv4) { |
| SocketTest::TestConnectIPv4(); |
| } |
| @@ -209,6 +256,146 @@ TEST_F(PhysicalSocketTest, MAYBE_TestConnectAcceptErrorIPv6) { |
| ConnectInternalAcceptError(kIPv6Loopback); |
| } |
| +void PhysicalSocketTest::PartialWriteStayWritable(const IPAddress& loopback) { |
| + testing::StreamSink sink; |
| + SocketAddress accept_addr; |
| + |
| + // Simulate a really small maximum send size. |
| + const int kMaximumSendSize = 128; |
|
pthatcher1
2016/01/30 00:03:10
kMaxSendSize
joachim
2016/01/31 23:29:06
Done.
|
| + SetMaximumSendSize(kMaximumSendSize); |
| + |
| + // Create test data. |
| + const size_t kDataSize = 128 * 1024; |
| + scoped_ptr<char[]> send_buffer(new char[kDataSize]); |
| + scoped_ptr<char[]> recv_buffer(new char[kDataSize]); |
|
pthatcher1
2016/01/30 00:03:10
It seems like this would benefit from using rtc::B
joachim
2016/01/31 23:29:07
Done. As I used "SocketTest::TcpInternal" as base
|
| + size_t send_pos = 0, recv_pos = 0; |
|
pthatcher1
2016/01/30 00:03:10
You can replace all instances of recv_pos with rec
joachim
2016/01/31 23:29:06
Done.
|
| + for (size_t i = 0; i < kDataSize; ++i) { |
| + send_buffer[i] = static_cast<char>(i % 256); |
| + recv_buffer[i] = 0; |
| + } |
| + |
| + // Create client. |
| + scoped_ptr<AsyncSocket> client( |
| + server_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); |
| + sink.Monitor(client.get()); |
| + |
| + // Create server and listen. |
| + scoped_ptr<AsyncSocket> server( |
| + server_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); |
| + sink.Monitor(server.get()); |
| + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); |
| + EXPECT_EQ(0, server->Listen(5)); |
| + |
| + // Attempt connection. |
| + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); |
| + |
| + // Accept connection. |
| + EXPECT_TRUE_WAIT((sink.Check(server.get(), testing::SSE_READ)), kTimeout); |
| + scoped_ptr<AsyncSocket> accepted(server->Accept(&accept_addr)); |
| + ASSERT_TRUE(accepted); |
| + sink.Monitor(accepted.get()); |
| + |
| + // Both sides are now connected. |
| + EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout); |
| + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_OPEN)); |
| + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); |
| + EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress()); |
| + |
| + // Send and receive a bunch of data. |
|
pthatcher1
2016/01/30 00:03:10
Can you move the definition of the send and recv b
joachim
2016/01/31 23:29:06
Done.
|
| + bool send_waiting_for_writability = false; |
|
pthatcher1
2016/01/30 00:03:10
Why not just "writable", with the reversed meaning
pthatcher1
2016/01/30 00:03:10
Can you call
sender = accepted;
receiver = clien
joachim
2016/01/31 23:29:06
Done.
joachim
2016/01/31 23:29:06
Done.
|
| + bool send_expect_success = true; |
|
pthatcher1
2016/01/30 00:03:10
Can you call this send_called = false? That would
joachim
2016/01/31 23:29:07
Done.
|
| + bool recv_waiting_for_readability = true; |
|
pthatcher1
2016/01/30 00:03:10
Similarly, "readable = false" here.
joachim
2016/01/31 23:29:06
Done.
|
| + bool recv_expect_success = false; |
|
pthatcher1
2016/01/30 00:03:10
And this "recv_called = false"?
joachim
2016/01/31 23:29:06
Done.
|
| + int data_in_flight = 0; |
| + while (recv_pos < kDataSize) { |
| + // Send as much as we can if we've been cleared to send. |
|
pthatcher1
2016/01/30 00:03:09
if we've been cleared to send => while we're clear
joachim
2016/01/31 23:29:06
Done.
|
| + while (!send_waiting_for_writability && send_pos < kDataSize) { |
| + int tosend = static_cast<int>(kDataSize - send_pos); |
| + int sent = accepted->Send(send_buffer.get() + send_pos, tosend); |
| + if (send_expect_success) { |
| + // The first Send() after connecting or getting writability should |
| + // succeed and send some data. |
| + EXPECT_GT(sent, 0); |
| + send_expect_success = false; |
| + } |
| + if (sent >= 0) { |
| + EXPECT_LE(sent, tosend); |
| + EXPECT_LE(sent, kMaximumSendSize); |
| + send_pos += sent; |
| + data_in_flight += sent; |
|
pthatcher1
2016/01/30 00:03:10
I think you can replace all instances of data_in_f
joachim
2016/01/31 23:29:06
Done.
|
| + } |
| + if (sent < tosend) { |
| + // Partial data or nothing sent. |
| + ASSERT_TRUE(accepted->IsBlocking()); |
| + send_waiting_for_writability = true; |
| + } |
| + } |
| + |
| + // Read all the sent data. |
| + while (data_in_flight > 0) { |
|
pthatcher1
2016/01/30 00:03:10
Or in this case:
while (recv_buffer.size() < sent
joachim
2016/01/31 23:29:06
Done.
|
| + if (recv_waiting_for_readability) { |
| + // Wait until data is available. |
| + EXPECT_TRUE_WAIT(sink.Check(client.get(), testing::SSE_READ), kTimeout); |
| + recv_waiting_for_readability = false; |
| + recv_expect_success = true; |
| + } |
| + |
| + // Receive as much as we can get in a single recv call. |
| + int rcvd = client->Recv(recv_buffer.get() + recv_pos, |
| + kDataSize - recv_pos); |
| + |
| + if (recv_expect_success) { |
| + // The first Recv() after getting readability should succeed and receive |
| + // some data. |
| + EXPECT_GT(rcvd, 0); |
| + recv_expect_success = false; |
| + } |
| + if (rcvd >= 0) { |
| + EXPECT_LE(rcvd, data_in_flight); |
| + recv_pos += rcvd; |
| + data_in_flight -= rcvd; |
| + } else { |
| + ASSERT_TRUE(client->IsBlocking()); |
| + recv_waiting_for_readability = true; |
| + } |
| + } |
| + |
| + // Once all that we've sent has been rcvd, expect to be able to send again. |
| + if (send_waiting_for_writability) { |
| + EXPECT_TRUE_WAIT(sink.Check(accepted.get(), testing::SSE_WRITE), |
| + kTimeout); |
| + send_waiting_for_writability = false; |
| + send_expect_success = true; |
| + } |
| + } |
| + |
| + // The received data matches the sent data. |
| + EXPECT_EQ(kDataSize, send_pos); |
| + EXPECT_EQ(kDataSize, recv_pos); |
| + EXPECT_EQ(0, memcmp(recv_buffer.get(), send_buffer.get(), kDataSize)); |
| + |
| + // Close down. |
| + accepted->Close(); |
| + EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout); |
| + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_CLOSE)); |
| + client->Close(); |
| +} |
| + |
| +TEST_F(PhysicalSocketTest, TestPartialWriteStayWritableIPv4) { |
| + PartialWriteStayWritable(kIPv4Loopback); |
| +} |
| + |
| +// Crashes on Linux. See webrtc:4923. |
| +#if defined(WEBRTC_LINUX) |
| +#define MAYBE_TestPartialWriteStayWritableIPv6 \ |
| + DISABLED_TestPartialWriteStayWritableIPv6 |
| +#else |
| +#define MAYBE_TestPartialWriteStayWritableIPv6 TestPartialWriteStayWritableIPv6 |
| +#endif |
| +TEST_F(PhysicalSocketTest, MAYBE_TestPartialWriteStayWritableIPv6) { |
| + PartialWriteStayWritable(kIPv6Loopback); |
| +} |
| + |
| // Crashes on Linux. See webrtc:4923. |
| #if defined(WEBRTC_LINUX) |
| #define MAYBE_TestConnectFailIPv6 DISABLED_TestConnectFailIPv6 |