summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/p2p/base/pseudo_tcp_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/p2p/base/pseudo_tcp_unittest.cc')
-rw-r--r--third_party/libwebrtc/p2p/base/pseudo_tcp_unittest.cc881
1 files changed, 881 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..48df2b119a
--- /dev/null
+++ b/third_party/libwebrtc/p2p/base/pseudo_tcp_unittest.cc
@@ -0,0 +1,881 @@
+/*
+ * 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 received;
+ do {
+ received = remote_.Recv(block, sizeof(block));
+ if (received != -1) {
+ size_t written;
+ int error;
+ recv_stream_.Write(
+ rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), received),
+ written, error);
+ recv_stream_.GetPosition(&position);
+ RTC_LOG(LS_VERBOSE) << "Received: " << position;
+ }
+ } while (received > 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 received;
+ do {
+ received = receiver_->Recv(block, sizeof(block));
+ if (received != -1) {
+ size_t written;
+ int error;
+ recv_stream_.Write(
+ rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(block),
+ received),
+ written, error);
+ recv_stream_.GetPosition(&position);
+ RTC_LOG(LS_VERBOSE) << "Received: " << position;
+ }
+ } while (received > 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 received;
+
+ do {
+ received = remote_.Recv(block, sizeof(block));
+ if (received != -1) {
+ size_t written;
+ int error;
+ recv_stream_.Write(
+ rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), received),
+ written, error);
+ recv_stream_.GetPosition(&position);
+ RTC_LOG(LS_VERBOSE) << "Received: " << position;
+ }
+ } while (received > 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);
+}
+*/