diff options
Diffstat (limited to 'third_party/libwebrtc/p2p/base/pseudo_tcp_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/p2p/base/pseudo_tcp_unittest.cc | 880 |
1 files changed, 880 insertions, 0 deletions
diff --git a/third_party/libwebrtc/p2p/base/pseudo_tcp_unittest.cc b/third_party/libwebrtc/p2p/base/pseudo_tcp_unittest.cc new file mode 100644 index 0000000000..e56c6fa2c5 --- /dev/null +++ b/third_party/libwebrtc/p2p/base/pseudo_tcp_unittest.cc @@ -0,0 +1,880 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "p2p/base/pseudo_tcp.h" + +#include <string.h> + +#include <algorithm> +#include <cstddef> +#include <string> +#include <utility> +#include <vector> + +#include "api/task_queue/pending_task_safety_flag.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" +#include "rtc_base/gunit.h" +#include "rtc_base/helpers.h" +#include "rtc_base/logging.h" +#include "rtc_base/memory_stream.h" +#include "rtc_base/time_utils.h" +#include "test/gtest.h" + +using ::cricket::PseudoTcp; +using ::webrtc::ScopedTaskSafety; +using ::webrtc::TaskQueueBase; +using ::webrtc::TimeDelta; + +static const int kConnectTimeoutMs = 10000; // ~3 * default RTO of 3000ms +static const int kTransferTimeoutMs = 15000; +static const int kBlockSize = 4096; + +class PseudoTcpForTest : public cricket::PseudoTcp { + public: + PseudoTcpForTest(cricket::IPseudoTcpNotify* notify, uint32_t conv) + : PseudoTcp(notify, conv) {} + + bool isReceiveBufferFull() const { return PseudoTcp::isReceiveBufferFull(); } + + void disableWindowScale() { PseudoTcp::disableWindowScale(); } +}; + +class PseudoTcpTestBase : public ::testing::Test, + public cricket::IPseudoTcpNotify { + public: + PseudoTcpTestBase() + : local_(this, 1), + remote_(this, 1), + have_connected_(false), + have_disconnected_(false), + local_mtu_(65535), + remote_mtu_(65535), + delay_(0), + loss_(0) { + // Set use of the test RNG to get predictable loss patterns. Otherwise, + // this test would occasionally get really unlucky loss and time out. + rtc::SetRandomTestMode(true); + } + ~PseudoTcpTestBase() { + // Put it back for the next test. + rtc::SetRandomTestMode(false); + } + // If true, both endpoints will send the "connect" segment simultaneously, + // rather than `local_` sending it followed by a response from `remote_`. + // Note that this is what chromoting ends up doing. + void SetSimultaneousOpen(bool enabled) { simultaneous_open_ = enabled; } + void SetLocalMtu(int mtu) { + local_.NotifyMTU(mtu); + local_mtu_ = mtu; + } + void SetRemoteMtu(int mtu) { + remote_.NotifyMTU(mtu); + remote_mtu_ = mtu; + } + void SetDelay(int delay) { delay_ = delay; } + void SetLoss(int percent) { loss_ = percent; } + // Used to cause the initial "connect" segment to be lost, needed for a + // regression test. + void DropNextPacket() { drop_next_packet_ = true; } + void SetOptNagling(bool enable_nagles) { + local_.SetOption(PseudoTcp::OPT_NODELAY, !enable_nagles); + remote_.SetOption(PseudoTcp::OPT_NODELAY, !enable_nagles); + } + void SetOptAckDelay(int ack_delay) { + local_.SetOption(PseudoTcp::OPT_ACKDELAY, ack_delay); + remote_.SetOption(PseudoTcp::OPT_ACKDELAY, ack_delay); + } + void SetOptSndBuf(int size) { + local_.SetOption(PseudoTcp::OPT_SNDBUF, size); + remote_.SetOption(PseudoTcp::OPT_SNDBUF, size); + } + void SetRemoteOptRcvBuf(int size) { + remote_.SetOption(PseudoTcp::OPT_RCVBUF, size); + } + void SetLocalOptRcvBuf(int size) { + local_.SetOption(PseudoTcp::OPT_RCVBUF, size); + } + void DisableRemoteWindowScale() { remote_.disableWindowScale(); } + void DisableLocalWindowScale() { local_.disableWindowScale(); } + + protected: + int Connect() { + int ret = local_.Connect(); + if (ret == 0) { + UpdateLocalClock(); + } + if (simultaneous_open_) { + ret = remote_.Connect(); + if (ret == 0) { + UpdateRemoteClock(); + } + } + return ret; + } + void Close() { + local_.Close(false); + UpdateLocalClock(); + } + + virtual void OnTcpOpen(PseudoTcp* tcp) { + // Consider ourselves connected when the local side gets OnTcpOpen. + // OnTcpWriteable isn't fired at open, so we trigger it now. + RTC_LOG(LS_VERBOSE) << "Opened"; + if (tcp == &local_) { + have_connected_ = true; + OnTcpWriteable(tcp); + } + } + // Test derived from the base should override + // virtual void OnTcpReadable(PseudoTcp* tcp) + // and + // virtual void OnTcpWritable(PseudoTcp* tcp) + virtual void OnTcpClosed(PseudoTcp* tcp, uint32_t error) { + // Consider ourselves closed when the remote side gets OnTcpClosed. + // TODO(?): OnTcpClosed is only ever notified in case of error in + // the current implementation. Solicited close is not (yet) supported. + RTC_LOG(LS_VERBOSE) << "Closed"; + EXPECT_EQ(0U, error); + if (tcp == &remote_) { + have_disconnected_ = true; + } + } + virtual WriteResult TcpWritePacket(PseudoTcp* tcp, + const char* buffer, + size_t len) { + // Drop a packet if the test called DropNextPacket. + if (drop_next_packet_) { + drop_next_packet_ = false; + RTC_LOG(LS_VERBOSE) << "Dropping packet due to DropNextPacket, size=" + << len; + return WR_SUCCESS; + } + // Randomly drop the desired percentage of packets. + if (rtc::CreateRandomId() % 100 < static_cast<uint32_t>(loss_)) { + RTC_LOG(LS_VERBOSE) << "Randomly dropping packet, size=" << len; + return WR_SUCCESS; + } + // Also drop packets that are larger than the configured MTU. + if (len > static_cast<size_t>(std::min(local_mtu_, remote_mtu_))) { + RTC_LOG(LS_VERBOSE) << "Dropping packet that exceeds path MTU, size=" + << len; + return WR_SUCCESS; + } + PseudoTcp* other; + ScopedTaskSafety* timer; + if (tcp == &local_) { + other = &remote_; + timer = &remote_timer_; + } else { + other = &local_; + timer = &local_timer_; + } + std::string packet(buffer, len); + ++packets_in_flight_; + TaskQueueBase::Current()->PostDelayedTask( + [other, timer, packet = std::move(packet), this] { + --packets_in_flight_; + other->NotifyPacket(packet.c_str(), packet.size()); + UpdateClock(*other, *timer); + }, + TimeDelta::Millis(delay_)); + return WR_SUCCESS; + } + + void UpdateLocalClock() { UpdateClock(local_, local_timer_); } + void UpdateRemoteClock() { UpdateClock(remote_, remote_timer_); } + static void UpdateClock(PseudoTcp& tcp, ScopedTaskSafety& timer) { + long interval = 0; // NOLINT + tcp.GetNextClock(PseudoTcp::Now(), interval); + interval = std::max<int>(interval, 0L); // sometimes interval is < 0 + timer.reset(); + TaskQueueBase::Current()->PostDelayedTask( + SafeTask(timer.flag(), + [&tcp, &timer] { + tcp.NotifyClock(PseudoTcp::Now()); + UpdateClock(tcp, timer); + }), + TimeDelta::Millis(interval)); + } + + rtc::AutoThread main_thread_; + PseudoTcpForTest local_; + PseudoTcpForTest remote_; + ScopedTaskSafety local_timer_; + ScopedTaskSafety remote_timer_; + rtc::MemoryStream send_stream_; + rtc::MemoryStream recv_stream_; + bool have_connected_; + bool have_disconnected_; + int local_mtu_; + int remote_mtu_; + int delay_; + int loss_; + bool drop_next_packet_ = false; + bool simultaneous_open_ = false; + int packets_in_flight_ = 0; +}; + +class PseudoTcpTest : public PseudoTcpTestBase { + public: + void TestTransfer(int size) { + uint32_t start; + int32_t elapsed; + size_t received; + // Create some dummy data to send. + send_stream_.ReserveSize(size); + for (int i = 0; i < size; ++i) { + uint8_t ch = static_cast<uint8_t>(i); + size_t written; + int error; + send_stream_.Write(rtc::MakeArrayView(&ch, 1), written, error); + } + send_stream_.Rewind(); + // Prepare the receive stream. + recv_stream_.ReserveSize(size); + // Connect and wait until connected. + start = rtc::Time32(); + EXPECT_EQ(0, Connect()); + EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs); + // Sending will start from OnTcpWriteable and complete when all data has + // been received. + EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs); + elapsed = rtc::Time32() - start; + recv_stream_.GetSize(&received); + // Ensure we closed down OK and we got the right data. + // TODO(?): Ensure the errors are cleared properly. + // EXPECT_EQ(0, local_.GetError()); + // EXPECT_EQ(0, remote_.GetError()); + EXPECT_EQ(static_cast<size_t>(size), received); + EXPECT_EQ(0, + memcmp(send_stream_.GetBuffer(), recv_stream_.GetBuffer(), size)); + RTC_LOG(LS_INFO) << "Transferred " << received << " bytes in " << elapsed + << " ms (" << size * 8 / elapsed << " Kbps)"; + } + + private: + // IPseudoTcpNotify interface + + virtual void OnTcpReadable(PseudoTcp* tcp) { + // Stream bytes to the recv stream as they arrive. + if (tcp == &remote_) { + ReadData(); + + // TODO(?): OnTcpClosed() is currently only notified on error - + // there is no on-the-wire equivalent of TCP FIN. + // So we fake the notification when all the data has been read. + size_t received, required; + recv_stream_.GetPosition(&received); + send_stream_.GetSize(&required); + if (received == required) + OnTcpClosed(&remote_, 0); + } + } + virtual void OnTcpWriteable(PseudoTcp* tcp) { + // Write bytes from the send stream when we can. + // Shut down when we've sent everything. + if (tcp == &local_) { + RTC_LOG(LS_VERBOSE) << "Flow Control Lifted"; + bool done; + WriteData(&done); + if (done) { + Close(); + } + } + } + + void ReadData() { + char block[kBlockSize]; + size_t position; + int rcvd; + do { + rcvd = remote_.Recv(block, sizeof(block)); + if (rcvd != -1) { + size_t written; + int error; + recv_stream_.Write( + rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), rcvd), + written, error); + recv_stream_.GetPosition(&position); + RTC_LOG(LS_VERBOSE) << "Received: " << position; + } + } while (rcvd > 0); + } + void WriteData(bool* done) { + size_t position, tosend; + int sent; + char block[kBlockSize]; + do { + send_stream_.GetPosition(&position); + int error; + if (send_stream_.Read( + rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), kBlockSize), + tosend, error) != rtc::SR_EOS) { + sent = local_.Send(block, tosend); + UpdateLocalClock(); + if (sent != -1) { + send_stream_.SetPosition(position + sent); + RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent; + } else { + send_stream_.SetPosition(position); + RTC_LOG(LS_VERBOSE) << "Flow Controlled"; + } + } else { + sent = static_cast<int>(tosend = 0); + } + } while (sent > 0); + *done = (tosend == 0); + } + + private: + rtc::MemoryStream send_stream_; + rtc::MemoryStream recv_stream_; +}; + +class PseudoTcpTestPingPong : public PseudoTcpTestBase { + public: + PseudoTcpTestPingPong() + : iterations_remaining_(0), + sender_(NULL), + receiver_(NULL), + bytes_per_send_(0) {} + void SetBytesPerSend(int bytes) { bytes_per_send_ = bytes; } + void TestPingPong(int size, int iterations) { + uint32_t start, elapsed; + iterations_remaining_ = iterations; + receiver_ = &remote_; + sender_ = &local_; + // Create some dummy data to send. + send_stream_.ReserveSize(size); + for (int i = 0; i < size; ++i) { + uint8_t ch = static_cast<uint8_t>(i); + size_t written; + int error; + send_stream_.Write(rtc::MakeArrayView(&ch, 1), written, error); + } + send_stream_.Rewind(); + // Prepare the receive stream. + recv_stream_.ReserveSize(size); + // Connect and wait until connected. + start = rtc::Time32(); + EXPECT_EQ(0, Connect()); + EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs); + // Sending will start from OnTcpWriteable and stop when the required + // number of iterations have completed. + EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs); + elapsed = rtc::TimeSince(start); + RTC_LOG(LS_INFO) << "Performed " << iterations << " pings in " << elapsed + << " ms"; + } + + private: + // IPseudoTcpNotify interface + + virtual void OnTcpReadable(PseudoTcp* tcp) { + if (tcp != receiver_) { + RTC_LOG_F(LS_ERROR) << "unexpected OnTcpReadable"; + return; + } + // Stream bytes to the recv stream as they arrive. + ReadData(); + // If we've received the desired amount of data, rewind things + // and send it back the other way! + size_t position, desired; + recv_stream_.GetPosition(&position); + send_stream_.GetSize(&desired); + if (position == desired) { + if (receiver_ == &local_ && --iterations_remaining_ == 0) { + Close(); + // TODO(?): Fake OnTcpClosed() on the receiver for now. + OnTcpClosed(&remote_, 0); + return; + } + PseudoTcp* tmp = receiver_; + receiver_ = sender_; + sender_ = tmp; + recv_stream_.Rewind(); + send_stream_.Rewind(); + OnTcpWriteable(sender_); + } + } + virtual void OnTcpWriteable(PseudoTcp* tcp) { + if (tcp != sender_) + return; + // Write bytes from the send stream when we can. + // Shut down when we've sent everything. + RTC_LOG(LS_VERBOSE) << "Flow Control Lifted"; + WriteData(); + } + + void ReadData() { + char block[kBlockSize]; + size_t position; + int rcvd; + do { + rcvd = receiver_->Recv(block, sizeof(block)); + if (rcvd != -1) { + size_t written; + int error; + recv_stream_.Write( + rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(block), rcvd), + written, error); + recv_stream_.GetPosition(&position); + RTC_LOG(LS_VERBOSE) << "Received: " << position; + } + } while (rcvd > 0); + } + void WriteData() { + size_t position, tosend; + int sent; + char block[kBlockSize]; + do { + send_stream_.GetPosition(&position); + tosend = bytes_per_send_ ? bytes_per_send_ : sizeof(block); + int error; + if (send_stream_.Read( + rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), tosend), + tosend, error) != rtc::SR_EOS) { + sent = sender_->Send(block, tosend); + UpdateLocalClock(); + if (sent != -1) { + send_stream_.SetPosition(position + sent); + RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent; + } else { + send_stream_.SetPosition(position); + RTC_LOG(LS_VERBOSE) << "Flow Controlled"; + } + } else { + sent = static_cast<int>(tosend = 0); + } + } while (sent > 0); + } + + private: + int iterations_remaining_; + PseudoTcp* sender_; + PseudoTcp* receiver_; + int bytes_per_send_; +}; + +// Fill the receiver window until it is full, drain it and then +// fill it with the same amount. This is to test that receiver window +// contracts and enlarges correctly. +class PseudoTcpTestReceiveWindow : public PseudoTcpTestBase { + public: + // Not all the data are transfered, `size` just need to be big enough + // to fill up the receiver window twice. + void TestTransfer(int size) { + // Create some dummy data to send. + send_stream_.ReserveSize(size); + for (int i = 0; i < size; ++i) { + uint8_t ch = static_cast<uint8_t>(i); + size_t written; + int error; + send_stream_.Write(rtc::MakeArrayView(&ch, 1), written, error); + } + send_stream_.Rewind(); + + // Prepare the receive stream. + recv_stream_.ReserveSize(size); + + // Connect and wait until connected. + EXPECT_EQ(0, Connect()); + EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs); + + TaskQueueBase::Current()->PostTask([this] { WriteData(); }); + EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs); + + ASSERT_EQ(2u, send_position_.size()); + ASSERT_EQ(2u, recv_position_.size()); + + const size_t estimated_recv_window = EstimateReceiveWindowSize(); + + // The difference in consecutive send positions should equal the + // receive window size or match very closely. This verifies that receive + // window is open after receiver drained all the data. + const size_t send_position_diff = send_position_[1] - send_position_[0]; + EXPECT_GE(1024u, estimated_recv_window - send_position_diff); + + // Receiver drained the receive window twice. + EXPECT_EQ(2 * estimated_recv_window, recv_position_[1]); + } + + uint32_t EstimateReceiveWindowSize() const { + return static_cast<uint32_t>(recv_position_[0]); + } + + uint32_t EstimateSendWindowSize() const { + return static_cast<uint32_t>(send_position_[0] - recv_position_[0]); + } + + private: + // IPseudoTcpNotify interface + virtual void OnTcpReadable(PseudoTcp* tcp) {} + + virtual void OnTcpWriteable(PseudoTcp* tcp) {} + + void ReadUntilIOPending() { + char block[kBlockSize]; + size_t position; + int rcvd; + + do { + rcvd = remote_.Recv(block, sizeof(block)); + if (rcvd != -1) { + size_t written; + int error; + recv_stream_.Write( + rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), rcvd), + written, error); + recv_stream_.GetPosition(&position); + RTC_LOG(LS_VERBOSE) << "Received: " << position; + } + } while (rcvd > 0); + + recv_stream_.GetPosition(&position); + recv_position_.push_back(position); + + // Disconnect if we have done two transfers. + if (recv_position_.size() == 2u) { + Close(); + OnTcpClosed(&remote_, 0); + } else { + WriteData(); + } + } + + void WriteData() { + size_t position, tosend; + int sent; + char block[kBlockSize]; + do { + send_stream_.GetPosition(&position); + int error; + if (send_stream_.Read( + rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), + sizeof(block)), + tosend, error) != rtc::SR_EOS) { + sent = local_.Send(block, tosend); + UpdateLocalClock(); + if (sent != -1) { + send_stream_.SetPosition(position + sent); + RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent; + } else { + send_stream_.SetPosition(position); + RTC_LOG(LS_VERBOSE) << "Flow Controlled"; + } + } else { + sent = static_cast<int>(tosend = 0); + } + } while (sent > 0); + // At this point, we've filled up the available space in the send queue. + + if (packets_in_flight_ > 0) { + // If there are packet tasks, attempt to continue sending after giving + // those packets time to process, which should free up the send buffer. + rtc::Thread::Current()->PostDelayedTask([this] { WriteData(); }, + TimeDelta::Millis(10)); + } else { + if (!remote_.isReceiveBufferFull()) { + RTC_LOG(LS_ERROR) << "This shouldn't happen - the send buffer is full, " + "the receive buffer is not, and there are no " + "remaining messages to process."; + } + send_stream_.GetPosition(&position); + send_position_.push_back(position); + + // Drain the receiver buffer. + ReadUntilIOPending(); + } + } + + private: + rtc::MemoryStream send_stream_; + rtc::MemoryStream recv_stream_; + + std::vector<size_t> send_position_; + std::vector<size_t> recv_position_; +}; + +// Basic end-to-end data transfer tests + +// Test the normal case of sending data from one side to the other. +TEST_F(PseudoTcpTest, TestSend) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + TestTransfer(1000000); +} + +// Test sending data with a 50 ms RTT. Transmission should take longer due +// to a slower ramp-up in send rate. +TEST_F(PseudoTcpTest, TestSendWithDelay) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetDelay(50); + TestTransfer(1000000); +} + +// Test sending data with packet loss. Transmission should take much longer due +// to send back-off when loss occurs. +TEST_F(PseudoTcpTest, TestSendWithLoss) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetLoss(10); + TestTransfer(100000); // less data so test runs faster +} + +// Test sending data with a 50 ms RTT and 10% packet loss. Transmission should +// take much longer due to send back-off and slower detection of loss. +TEST_F(PseudoTcpTest, TestSendWithDelayAndLoss) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetDelay(50); + SetLoss(10); + TestTransfer(100000); // less data so test runs faster +} + +// Test sending data with 10% packet loss and Nagling disabled. Transmission +// should take about the same time as with Nagling enabled. +TEST_F(PseudoTcpTest, TestSendWithLossAndOptNaglingOff) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetLoss(10); + SetOptNagling(false); + TestTransfer(100000); // less data so test runs faster +} + +// Regression test for bugs.webrtc.org/9208. +// +// This bug resulted in corrupted data if a "connect" segment was received after +// a data segment. This is only possible if: +// +// * The initial "connect" segment is lost, and retransmitted later. +// * Both sides send "connect"s simultaneously, such that the local side thinks +// a connection is established even before its "connect" has been +// acknowledged. +// * Nagle algorithm disabled, allowing a data segment to be sent before the +// "connect" has been acknowledged. +TEST_F(PseudoTcpTest, + TestSendWhenFirstPacketLostWithOptNaglingOffAndSimultaneousOpen) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + DropNextPacket(); + SetOptNagling(false); + SetSimultaneousOpen(true); + TestTransfer(10000); +} + +// Test sending data with 10% packet loss and Delayed ACK disabled. +// Transmission should be slightly faster than with it enabled. +TEST_F(PseudoTcpTest, TestSendWithLossAndOptAckDelayOff) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetLoss(10); + SetOptAckDelay(0); + TestTransfer(100000); +} + +// Test sending data with 50ms delay and Nagling disabled. +TEST_F(PseudoTcpTest, TestSendWithDelayAndOptNaglingOff) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetDelay(50); + SetOptNagling(false); + TestTransfer(100000); // less data so test runs faster +} + +// Test sending data with 50ms delay and Delayed ACK disabled. +TEST_F(PseudoTcpTest, TestSendWithDelayAndOptAckDelayOff) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetDelay(50); + SetOptAckDelay(0); + TestTransfer(100000); // less data so test runs faster +} + +// Test a large receive buffer with a sender that doesn't support scaling. +TEST_F(PseudoTcpTest, TestSendRemoteNoWindowScale) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetLocalOptRcvBuf(100000); + DisableRemoteWindowScale(); + TestTransfer(1000000); +} + +// Test a large sender-side receive buffer with a receiver that doesn't support +// scaling. +TEST_F(PseudoTcpTest, TestSendLocalNoWindowScale) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetRemoteOptRcvBuf(100000); + DisableLocalWindowScale(); + TestTransfer(1000000); +} + +// Test when both sides use window scaling. +TEST_F(PseudoTcpTest, TestSendBothUseWindowScale) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetRemoteOptRcvBuf(100000); + SetLocalOptRcvBuf(100000); + TestTransfer(1000000); +} + +// Test using a large window scale value. +TEST_F(PseudoTcpTest, TestSendLargeInFlight) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetRemoteOptRcvBuf(100000); + SetLocalOptRcvBuf(100000); + SetOptSndBuf(150000); + TestTransfer(1000000); +} + +TEST_F(PseudoTcpTest, TestSendBothUseLargeWindowScale) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetRemoteOptRcvBuf(1000000); + SetLocalOptRcvBuf(1000000); + TestTransfer(10000000); +} + +// Test using a small receive buffer. +TEST_F(PseudoTcpTest, TestSendSmallReceiveBuffer) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetRemoteOptRcvBuf(10000); + SetLocalOptRcvBuf(10000); + TestTransfer(1000000); +} + +// Test using a very small receive buffer. +TEST_F(PseudoTcpTest, TestSendVerySmallReceiveBuffer) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetRemoteOptRcvBuf(100); + SetLocalOptRcvBuf(100); + TestTransfer(100000); +} + +// Ping-pong (request/response) tests + +// Test sending <= 1x MTU of data in each ping/pong. Should take <10ms. +TEST_F(PseudoTcpTestPingPong, TestPingPong1xMtu) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + TestPingPong(100, 100); +} + +// Test sending 2x-3x MTU of data in each ping/pong. Should take <10ms. +TEST_F(PseudoTcpTestPingPong, TestPingPong3xMtu) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + TestPingPong(400, 100); +} + +// Test sending 1x-2x MTU of data in each ping/pong. +// Should take ~1s, due to interaction between Nagling and Delayed ACK. +TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtu) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + TestPingPong(2000, 5); +} + +// Test sending 1x-2x MTU of data in each ping/pong with Delayed ACK off. +// Should take <10ms. +TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtuWithAckDelayOff) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetOptAckDelay(0); + TestPingPong(2000, 100); +} + +// Test sending 1x-2x MTU of data in each ping/pong with Nagling off. +// Should take <10ms. +TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtuWithNaglingOff) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetOptNagling(false); + TestPingPong(2000, 5); +} + +// Test sending a ping as pair of short (non-full) segments. +// Should take ~1s, due to Delayed ACK interaction with Nagling. +TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegments) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetOptAckDelay(5000); + SetBytesPerSend(50); // i.e. two Send calls per payload + TestPingPong(100, 5); +} + +// Test sending ping as a pair of short (non-full) segments, with Nagling off. +// Should take <10ms. +TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegmentsWithNaglingOff) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetOptNagling(false); + SetBytesPerSend(50); // i.e. two Send calls per payload + TestPingPong(100, 5); +} + +// Test sending <= 1x MTU of data ping/pong, in two segments, no Delayed ACK. +// Should take ~1s. +TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegmentsWithAckDelayOff) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetBytesPerSend(50); // i.e. two Send calls per payload + SetOptAckDelay(0); + TestPingPong(100, 5); +} + +// Test that receive window expands and contract correctly. +TEST_F(PseudoTcpTestReceiveWindow, TestReceiveWindow) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetOptNagling(false); + SetOptAckDelay(0); + TestTransfer(1024 * 1000); +} + +// Test setting send window size to a very small value. +TEST_F(PseudoTcpTestReceiveWindow, TestSetVerySmallSendWindowSize) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetOptNagling(false); + SetOptAckDelay(0); + SetOptSndBuf(900); + TestTransfer(1024 * 1000); + EXPECT_EQ(900u, EstimateSendWindowSize()); +} + +// Test setting receive window size to a value other than default. +TEST_F(PseudoTcpTestReceiveWindow, TestSetReceiveWindowSize) { + SetLocalMtu(1500); + SetRemoteMtu(1500); + SetOptNagling(false); + SetOptAckDelay(0); + SetRemoteOptRcvBuf(100000); + SetLocalOptRcvBuf(100000); + TestTransfer(1024 * 1000); + EXPECT_EQ(100000u, EstimateReceiveWindowSize()); +} + +/* Test sending data with mismatched MTUs. We should detect this and reduce +// our packet size accordingly. +// TODO(?): This doesn't actually work right now. The current code +// doesn't detect if the MTU is set too high on either side. +TEST_F(PseudoTcpTest, TestSendWithMismatchedMtus) { + SetLocalMtu(1500); + SetRemoteMtu(1280); + TestTransfer(1000000); +} +*/ |