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;