Internal QUICHE change

PiperOrigin-RevId: 243880078
Change-Id: If9b4210b8e0ff73518b628d7460e94a765406f07
diff --git a/quic/quartc/test/bidi_test_runner.cc b/quic/quartc/test/bidi_test_runner.cc
new file mode 100644
index 0000000..ebd6d33
--- /dev/null
+++ b/quic/quartc/test/bidi_test_runner.cc
@@ -0,0 +1,170 @@
+// Copyright (c) 2019 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 "net/third_party/quiche/src/quic/quartc/test/bidi_test_runner.h"
+
+#include "net/third_party/quiche/src/quic/quartc/test/quartc_peer.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+
+bool ContainsSequenceNumbers(const std::vector<ReceivedMessage>& messages,
+                             IdToSequenceNumberMap id_to_sequence_number) {
+  for (const auto& message : messages) {
+    auto it = id_to_sequence_number.find(message.frame.source_id);
+    if (it != id_to_sequence_number.end() &&
+        it->second == message.frame.sequence_number) {
+      id_to_sequence_number.erase(it);
+    }
+  }
+  return id_to_sequence_number.empty();
+}
+
+void LogResults(const std::vector<ReceivedMessage>& messages) {
+  QuicTime::Delta max_delay = QuicTime::Delta::Zero();
+  QuicTime::Delta total_delay = QuicTime::Delta::Zero();
+  QuicByteCount total_throughput = 0;
+  for (const auto& message : messages) {
+    QuicTime::Delta one_way_delay =
+        message.receive_time - message.frame.send_time;
+    QUIC_VLOG(1) << "Frame details: source_id=" << message.frame.source_id
+                 << ", sequence_number=" << message.frame.sequence_number
+                 << ", one_way_delay (ms)=" << one_way_delay.ToMilliseconds();
+    max_delay = std::max(max_delay, one_way_delay);
+    total_delay = total_delay + one_way_delay;
+    total_throughput += message.frame.size;
+  }
+  QuicBandwidth total_bandwidth = QuicBandwidth::FromBytesAndTimeDelta(
+      total_throughput,
+      messages.back().receive_time - messages.front().receive_time);
+  QUIC_LOG(INFO) << "Summary:\n  max_delay (ms)=" << max_delay.ToMilliseconds()
+                 << "\n  average_delay (ms)="
+                 << total_delay.ToMilliseconds() / messages.size()
+                 << "\n  total_throughput (bytes)=" << total_throughput
+                 << "\n  total_bandwidth (bps)="
+                 << total_bandwidth.ToBitsPerSecond();
+}
+
+}  // namespace
+
+BidiTestRunner::BidiTestRunner(simulator::Simulator* simulator,
+                               QuartcPacketTransport* client_transport,
+                               QuartcPacketTransport* server_transport)
+    : simulator_(simulator),
+      client_transport_(client_transport),
+      server_transport_(server_transport) {
+  // Set up default data source configs.
+  // Emulates an audio source with a 20 ms ptime.
+  QuartcDataSource::Config audio;
+  audio.id = 1;
+  audio.frame_interval = QuicTime::Delta::FromMilliseconds(20);
+  audio.min_bandwidth = QuicBandwidth::FromKBitsPerSecond(8);
+  audio.max_bandwidth = QuicBandwidth::FromKBitsPerSecond(64);
+
+  // Emulates a video source at 30 fps.
+  QuartcDataSource::Config video;
+  video.id = 2;
+  video.frame_interval = QuicTime::Delta::FromMicroseconds(33333);
+  video.min_bandwidth = QuicBandwidth::FromKBitsPerSecond(25);
+  video.max_bandwidth = QuicBandwidth::FromKBitsPerSecond(5000);
+
+  // Note: by placing audio first, it takes priority in bandwidth allocations.
+  client_configs_.push_back(audio);
+  client_configs_.push_back(video);
+  server_configs_.push_back(audio);
+  server_configs_.push_back(video);
+}
+
+BidiTestRunner::~BidiTestRunner() {
+  // Note that peers must be deleted before endpoints.  Peers close the
+  // connection when deleted.
+  client_peer_.reset();
+  server_peer_.reset();
+}
+
+bool BidiTestRunner::RunTest(QuicTime::Delta test_duration) {
+  client_peer_ = QuicMakeUnique<QuartcPeer>(
+      simulator_->GetClock(), simulator_->GetAlarmFactory(),
+      simulator_->GetRandomGenerator(), client_configs_);
+  server_peer_ = QuicMakeUnique<QuartcPeer>(
+      simulator_->GetClock(), simulator_->GetAlarmFactory(),
+      simulator_->GetRandomGenerator(), server_configs_);
+
+  QuartcEndpoint::Delegate* server_delegate = server_peer_.get();
+  if (server_interceptor_) {
+    server_interceptor_->SetDelegate(server_delegate);
+    server_delegate = server_interceptor_;
+  }
+  server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+      simulator_->GetAlarmFactory(), simulator_->GetClock(), server_delegate,
+      QuartcSessionConfig());
+
+  QuartcEndpoint::Delegate* client_delegate = client_peer_.get();
+  if (client_interceptor_) {
+    client_interceptor_->SetDelegate(client_delegate);
+    client_delegate = client_interceptor_;
+  }
+  client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+      simulator_->GetAlarmFactory(), simulator_->GetClock(), client_delegate,
+      QuartcSessionConfig(), server_endpoint_->server_crypto_config());
+
+  QuicTime start_time = simulator_->GetClock()->Now();
+  server_endpoint_->Connect(server_transport_);
+  client_endpoint_->Connect(client_transport_);
+
+  // Measure connect latency.
+  if (!simulator_->RunUntil([this] { return client_peer_->Enabled(); })) {
+    return false;
+  }
+  QuicTime client_connected = simulator_->GetClock()->Now();
+  QuicTime::Delta client_connect_latency = client_connected - start_time;
+
+  if (!simulator_->RunUntil([this] { return server_peer_->Enabled(); })) {
+    return false;
+  }
+  QuicTime server_connected = simulator_->GetClock()->Now();
+  QuicTime::Delta server_connect_latency = server_connected - start_time;
+
+  QUIC_LOG(INFO) << "Connect latencies (ms): client=" << client_connect_latency
+                 << ", server=" << server_connect_latency;
+
+  // Run the test.
+  simulator_->RunFor(test_duration);
+
+  // Disable sending and drain.
+  // Note that draining by waiting for the last sequence number sent may be
+  // flaky if packet loss is enabled.  However, simulator-based tests don't
+  // currently have any loss.
+  server_peer_->SetEnabled(false);
+  client_peer_->SetEnabled(false);
+
+  IdToSequenceNumberMap sent_by_server = server_peer_->GetLastSequenceNumbers();
+  if (!simulator_->RunUntil([this, &sent_by_server] {
+        return ContainsSequenceNumbers(client_peer_->received_messages(),
+                                       sent_by_server);
+      })) {
+    return false;
+  }
+
+  IdToSequenceNumberMap sent_by_client = client_peer_->GetLastSequenceNumbers();
+  if (!simulator_->RunUntil([this, &sent_by_client] {
+        return ContainsSequenceNumbers(server_peer_->received_messages(),
+                                       sent_by_client);
+      })) {
+    return false;
+  }
+
+  // Compute results.
+  QUIC_LOG(INFO) << "Printing client->server results:";
+  LogResults(server_peer_->received_messages());
+
+  QUIC_LOG(INFO) << "Printing server->client results:";
+  LogResults(client_peer_->received_messages());
+  return true;
+}
+
+}  // namespace test
+}  // namespace quic
diff --git a/quic/quartc/test/bidi_test_runner.h b/quic/quartc/test/bidi_test_runner.h
new file mode 100644
index 0000000..b76695a
--- /dev/null
+++ b/quic/quartc/test/bidi_test_runner.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2019 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.
+
+#ifndef QUICHE_QUIC_QUARTC_TEST_BIDI_TEST_RUNNER_H_
+#define QUICHE_QUIC_QUARTC_TEST_BIDI_TEST_RUNNER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
+#include "net/third_party/quiche/src/quic/quartc/test/quartc_data_source.h"
+#include "net/third_party/quiche/src/quic/quartc/test/quartc_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace test {
+
+// Interface for a component that intercepts endpoint callbacks before
+// forwarding them to another delegate.
+class QuartcEndpointInterceptor : public QuartcEndpoint::Delegate {
+ public:
+  ~QuartcEndpointInterceptor() override = default;
+
+  // Passes the test's endpoint delegate to this interceptor.  The interceptor
+  // must forward all callbacks to this delegate as soon as it finishes handling
+  // them.
+  virtual void SetDelegate(QuartcEndpoint::Delegate* delegate) = 0;
+};
+
+// Runner for bidirectional media flow tests.
+//
+// BidiTestRunner allows an external fixture to set up transports, then executes
+// a test.  During the test, it sets up two QuartcPeers, connects them through
+// the transports, and sends data in both directions for a specified duration.
+// It then stops sending, waits for any pending messages to finish transmission,
+// and then computes and logs a few basic metrics.
+//
+// For now, the runner computes the maximum and average one-way delay, the total
+// throughput (in bytes) and the average bandwidth (in bits per second).  It
+// logs these to the test's text logs.
+//
+// By default, the BidiTestRunner emulates one video stream and one audio stream
+// in each direction.  The audio stream runs with a 20 ms ptime, between 8 and
+// 64 kbps.  The video stream runs at 30 fps, between 25 kbps and 5 mbps.
+// Individual tests can overwrite the configs.
+//
+// BidiTestRunner provides a way for the test to register an "interceptor" on
+// each endpoint.  This allows a test to reconfigure that endpoint's session
+// prior to beginning the test.  For example, interceptors may be used to attach
+// debug visitors or change the congestion controller.
+class BidiTestRunner {
+ public:
+  // TODO(b/130540842): Make this compatible with non-simulator execution.
+  BidiTestRunner(simulator::Simulator* simulator,
+                 QuartcPacketTransport* client_transport,
+                 QuartcPacketTransport* server_transport);
+
+  virtual ~BidiTestRunner();
+
+  void set_client_configs(std::vector<QuartcDataSource::Config> configs) {
+    client_configs_ = std::move(configs);
+  }
+
+  void set_server_configs(std::vector<QuartcDataSource::Config> configs) {
+    server_configs_ = std::move(configs);
+  }
+
+  void set_client_interceptor(QuartcEndpointInterceptor* interceptor) {
+    client_interceptor_ = interceptor;
+  }
+
+  void set_server_interceptor(QuartcEndpointInterceptor* interceptor) {
+    server_interceptor_ = interceptor;
+  }
+
+  virtual bool RunTest(QuicTime::Delta test_duration);
+
+ private:
+  simulator::Simulator* simulator_;
+  QuartcPacketTransport* client_transport_;
+  QuartcPacketTransport* server_transport_;
+
+  std::vector<QuartcDataSource::Config> client_configs_;
+  std::vector<QuartcDataSource::Config> server_configs_;
+
+  QuartcEndpointInterceptor* client_interceptor_ = nullptr;
+  QuartcEndpointInterceptor* server_interceptor_ = nullptr;
+
+  std::unique_ptr<QuartcServerEndpoint> server_endpoint_;
+  std::unique_ptr<QuartcClientEndpoint> client_endpoint_;
+
+  std::unique_ptr<QuartcPeer> client_peer_;
+  std::unique_ptr<QuartcPeer> server_peer_;
+};
+
+}  // namespace test
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_QUARTC_TEST_BIDI_TEST_RUNNER_H_
diff --git a/quic/quartc/test/quartc_bidi_test.cc b/quic/quartc/test/quartc_bidi_test.cc
new file mode 100644
index 0000000..5c0e234
--- /dev/null
+++ b/quic/quartc/test/quartc_bidi_test.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2019 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 "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
+#include "net/third_party/quiche/src/quic/quartc/test/bidi_test_runner.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuartcBidiTest : public QuicTest {
+ protected:
+  QuartcBidiTest() {}
+
+  void CreateTransports(QuicBandwidth bandwidth,
+                        QuicTime::Delta propagation_delay,
+                        QuicByteCount queue_length) {
+    client_transport_ =
+        QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
+            &simulator_, "client_transport", "server_transport", queue_length);
+    server_transport_ =
+        QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
+            &simulator_, "server_transport", "client_transport", queue_length);
+    client_server_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+        client_transport_.get(), server_transport_.get(), bandwidth,
+        propagation_delay);
+  }
+
+  simulator::Simulator simulator_;
+
+  std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_;
+  std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_;
+  std::unique_ptr<simulator::SymmetricLink> client_server_link_;
+};
+
+TEST_F(QuartcBidiTest, Basic300kbps200ms) {
+  CreateTransports(QuicBandwidth::FromKBitsPerSecond(300),
+                   QuicTime::Delta::FromMilliseconds(200),
+                   10 * kDefaultMaxPacketSize);
+  BidiTestRunner runner(&simulator_, client_transport_.get(),
+                        server_transport_.get());
+  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quic
diff --git a/quic/quartc/test/quartc_peer.cc b/quic/quartc/test/quartc_peer.cc
index 32425c7..036ba03 100644
--- a/quic/quartc/test/quartc_peer.cc
+++ b/quic/quartc/test/quartc_peer.cc
@@ -31,9 +31,9 @@
   }
 }
 
-std::map<int32_t, int64_t> QuartcPeer::GetLastSequenceNumbers() const {
+IdToSequenceNumberMap QuartcPeer::GetLastSequenceNumbers() const {
   DCHECK_GE(configs_.size(), data_sources_.size());
-  std::map<int32_t, int64_t> out;
+  IdToSequenceNumberMap out;
   for (int i = 0; i < data_sources_.size(); ++i) {
     out[configs_[i].id] = data_sources_[i]->sequence_number();
   }
diff --git a/quic/quartc/test/quartc_peer.h b/quic/quartc/test/quartc_peer.h
index 32b29be..68cb9ee 100644
--- a/quic/quartc/test/quartc_peer.h
+++ b/quic/quartc/test/quartc_peer.h
@@ -23,6 +23,9 @@
 namespace quic {
 namespace test {
 
+// Map of source id to sequence number.
+using IdToSequenceNumberMap = std::map<int32_t, int64_t>;
+
 // ParsedQuartcDataFrame with a receive_time.
 struct ReceivedMessage {
   ParsedQuartcDataFrame frame;
@@ -65,7 +68,7 @@
 
   // Returns a map of source id to the sequence number of the last frame
   // produced by that source.
-  std::map<int32_t, int64_t> GetLastSequenceNumbers() const;
+  IdToSequenceNumberMap GetLastSequenceNumbers() const;
 
   // QuartcEndpoint::Delegate overrides.
   void OnSessionCreated(QuartcSession* session) override;