From 6c95bdc5abacdbf103958507e7a196d60c5f09f5 Mon Sep 17 00:00:00 2001 From: Sebastian Jansson Date: Tue, 27 Mar 2018 11:19:29 +0200 Subject: [PATCH] Adding network control test helper. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding a simple network simulator and a mock network control observer. This prepares for upcoming CLs adding unit tests network controllers. Bug: webrtc:8415 Change-Id: I5e8414576776fb8ae897bec73a1b062c8dd3e393 Reviewed-on: https://webrtc-review.googlesource.com/61507 Commit-Queue: Sebastian Jansson Reviewed-by: Erik Språng Cr-Commit-Position: refs/heads/master@{#22622} --- .../network_control/BUILD.gn | 7 + .../test/mock_network_control.h | 29 ++++ .../test/network_control_tester.cc | 133 ++++++++++++++++++ .../test/network_control_tester.h | 99 +++++++++++++ 4 files changed, 268 insertions(+) create mode 100644 modules/congestion_controller/network_control/test/mock_network_control.h create mode 100644 modules/congestion_controller/network_control/test/network_control_tester.cc create mode 100644 modules/congestion_controller/network_control/test/network_control_tester.h diff --git a/modules/congestion_controller/network_control/BUILD.gn b/modules/congestion_controller/network_control/BUILD.gn index 83641e951d..b57cc5bdd9 100644 --- a/modules/congestion_controller/network_control/BUILD.gn +++ b/modules/congestion_controller/network_control/BUILD.gn @@ -29,11 +29,18 @@ if (rtc_include_tests) { rtc_source_set("network_control_test") { testonly = true sources = [ + "test/mock_network_control.h", + "test/network_control_tester.cc", + "test/network_control_tester.h", "test/network_ostream_operators.cc", "test/network_ostream_operators.h", ] deps = [ ":network_control", + "../../../api:optional", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../../../test:test_support", ] } rtc_source_set("network_control_unittests") { diff --git a/modules/congestion_controller/network_control/test/mock_network_control.h b/modules/congestion_controller/network_control/test/mock_network_control.h new file mode 100644 index 0000000000..72b503442c --- /dev/null +++ b/modules/congestion_controller/network_control/test/mock_network_control.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_CONGESTION_CONTROLLER_NETWORK_CONTROL_TEST_MOCK_NETWORK_CONTROL_H_ +#define MODULES_CONGESTION_CONTROLLER_NETWORK_CONTROL_TEST_MOCK_NETWORK_CONTROL_H_ + +#include "modules/congestion_controller/network_control/include/network_control.h" +#include "test/gmock.h" + +namespace webrtc { +namespace test { +class MockNetworkControllerObserver : public NetworkControllerObserver { + public: + MOCK_METHOD1(OnCongestionWindow, void(CongestionWindow)); + MOCK_METHOD1(OnPacerConfig, void(PacerConfig)); + MOCK_METHOD1(OnProbeClusterConfig, void(ProbeClusterConfig)); + MOCK_METHOD1(OnTargetTransferRate, void(TargetTransferRate)); +}; +} // namespace test +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_NETWORK_CONTROL_TEST_MOCK_NETWORK_CONTROL_H_ diff --git a/modules/congestion_controller/network_control/test/network_control_tester.cc b/modules/congestion_controller/network_control/test/network_control_tester.cc new file mode 100644 index 0000000000..358153b604 --- /dev/null +++ b/modules/congestion_controller/network_control/test/network_control_tester.cc @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018 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 "modules/congestion_controller/network_control/test/network_control_tester.h" + +#include + +#include "modules/congestion_controller/network_control/include/network_control.h" +#include "rtc_base/logging.h" +namespace webrtc { +namespace test { + +NetworkControlState::NetworkControlState() = default; +NetworkControlState::~NetworkControlState() = default; +NetworkControlState::NetworkControlState(const NetworkControlState&) = default; + +NetworkControlCacher::NetworkControlCacher() = default; +NetworkControlCacher::~NetworkControlCacher() = default; + +void NetworkControlCacher::OnCongestionWindow(CongestionWindow msg) { + current_state_.congestion_window = msg; + RTC_LOG(LS_INFO) << "Received window=" << msg.data_window << "\n"; +} +void NetworkControlCacher::OnPacerConfig(PacerConfig msg) { + current_state_.pacer_config = msg; + RTC_LOG(LS_INFO) << "Received pacing at:" << msg.at_time + << ": rate=" << msg.data_rate() << "\n"; +} +void NetworkControlCacher::OnProbeClusterConfig(ProbeClusterConfig msg) { + current_state_.probe_config = msg; + RTC_LOG(LS_INFO) << "Received probe at:" << msg.at_time + << ": target=" << msg.target_data_rate << "\n"; +} +void NetworkControlCacher::OnTargetTransferRate(TargetTransferRate msg) { + current_state_.target_rate = msg; + RTC_LOG(LS_INFO) << "Received target at:" << msg.at_time + << ": rate=" << msg.target_rate << "\n"; +} + +SentPacket SimpleTargetRateProducer::ProduceNext( + const NetworkControlState& cache, + Timestamp current_time, + TimeDelta time_delta) { + DataRate actual_send_rate = + std::max(cache.target_rate->target_rate, cache.pacer_config->pad_rate()); + SentPacket packet; + packet.send_time = current_time; + packet.size = time_delta * actual_send_rate; + return packet; +} + +FeedbackBasedNetworkControllerTester::FeedbackBasedNetworkControllerTester( + NetworkControllerFactoryInterface* factory, + NetworkControllerConfig initial_config) + : current_time_(Timestamp::seconds(100000)), + accumulated_delay_(TimeDelta::ms(0)) { + initial_config.constraints.at_time = current_time_; + controller_ = factory->Create(&cacher_, initial_config); + process_interval_ = factory->GetProcessInterval(); +} + +FeedbackBasedNetworkControllerTester::~FeedbackBasedNetworkControllerTester() = + default; + +PacketResult FeedbackBasedNetworkControllerTester::SimulateSend( + SentPacket packet, + TimeDelta time_delta, + TimeDelta propagation_delay, + DataRate actual_bandwidth) { + TimeDelta bandwidth_delay = packet.size / actual_bandwidth; + accumulated_delay_ = + std::max(accumulated_delay_ - time_delta, TimeDelta::Zero()); + accumulated_delay_ += bandwidth_delay; + TimeDelta total_delay = propagation_delay + accumulated_delay_; + + PacketResult result; + result.sent_packet = packet; + result.receive_time = packet.send_time + total_delay; + return result; +} + +void FeedbackBasedNetworkControllerTester::RunSimulation( + TimeDelta duration, + TimeDelta packet_interval, + DataRate actual_bandwidth, + TimeDelta propagation_delay, + PacketProducer next_packet) { + Timestamp start_time = current_time_; + Timestamp last_process_time = current_time_; + while (current_time_ - start_time < duration) { + SentPacket sent_packet = + next_packet(cacher_.GetState(), current_time_, packet_interval); + controller_->OnSentPacket(sent_packet); + received_packets_.push_back(SimulateSend( + sent_packet, packet_interval, propagation_delay, actual_bandwidth)); + if (received_packets_.size() >= 2 && + current_time_ >= + received_packets_[1].receive_time + propagation_delay) { + TransportPacketsFeedback feedback; + feedback.prior_in_flight = DataSize::Zero(); + for (PacketResult& packet : received_packets_) + feedback.prior_in_flight += packet.sent_packet->size; + while (!received_packets_.empty() && + current_time_ >= + received_packets_.front().receive_time + propagation_delay) { + feedback.packet_feedbacks.push_back(received_packets_.front()); + received_packets_.pop_front(); + } + feedback.feedback_time = + feedback.packet_feedbacks.back().receive_time + propagation_delay; + feedback.data_in_flight = DataSize::Zero(); + for (PacketResult& packet : received_packets_) + feedback.data_in_flight += packet.sent_packet->size; + controller_->OnTransportPacketsFeedback(feedback); + } + current_time_ += packet_interval; + if (current_time_ - last_process_time > process_interval_) { + ProcessInterval interval_msg; + interval_msg.at_time = current_time_; + controller_->OnProcessInterval(interval_msg); + } + } +} + +} // namespace test +} // namespace webrtc diff --git a/modules/congestion_controller/network_control/test/network_control_tester.h b/modules/congestion_controller/network_control/test/network_control_tester.h new file mode 100644 index 0000000000..535b37400c --- /dev/null +++ b/modules/congestion_controller/network_control/test/network_control_tester.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_CONGESTION_CONTROLLER_NETWORK_CONTROL_TEST_NETWORK_CONTROL_TESTER_H_ +#define MODULES_CONGESTION_CONTROLLER_NETWORK_CONTROL_TEST_NETWORK_CONTROL_TESTER_H_ + +#include +#include +#include + +#include "api/optional.h" +#include "modules/congestion_controller/network_control/include/network_control.h" + +namespace webrtc { +namespace test { +struct NetworkControlState { + NetworkControlState(); + NetworkControlState(const NetworkControlState&); + ~NetworkControlState(); + rtc::Optional congestion_window; + rtc::Optional pacer_config; + rtc::Optional probe_config; + rtc::Optional target_rate; +}; + +// Produces one packet per time delta +class SimpleTargetRateProducer { + public: + static SentPacket ProduceNext(const NetworkControlState& state, + Timestamp current_time, + TimeDelta time_delta); +}; +class NetworkControlCacher : public NetworkControllerObserver { + public: + NetworkControlCacher(); + ~NetworkControlCacher() override; + void OnCongestionWindow(CongestionWindow msg) override; + void OnPacerConfig(PacerConfig msg) override; + void OnProbeClusterConfig(ProbeClusterConfig) override; + void OnTargetTransferRate(TargetTransferRate msg) override; + NetworkControlState GetState() { return current_state_; } + + private: + NetworkControlState current_state_; +}; + +class FeedbackBasedNetworkControllerTester { + public: + // A PacketProducer is a function that takes a network control state, a + // timestamp representing the expected send time and a time delta of the send + // times (This allows the PacketProducer to be stateless). It returns a + // SentPacket struct with actual send time and packet size. + using PacketProducer = std::function< + SentPacket(const NetworkControlState&, Timestamp, TimeDelta)>; + FeedbackBasedNetworkControllerTester( + NetworkControllerFactoryInterface* factory, + NetworkControllerConfig initial_config); + ~FeedbackBasedNetworkControllerTester(); + + // Runs the simulations for the given duration, the PacketProducer will be + // called repeatedly based on the given packet interval and the network will + // be simulated using given bandwidth and propagation delay. The simulation + // will call the controller under test with OnSentPacket and + // OnTransportPacketsFeedback. + + // Note that OnTransportPacketsFeedback will only be called for + // packets with resulting feedback time within the simulated duration. Packets + // with later feedback time are saved and used in the next call to + // RunSimulation where enough simulated time has passed. + void RunSimulation(TimeDelta duration, + TimeDelta packet_interval, + DataRate actual_bandwidth, + TimeDelta propagation_delay, + PacketProducer next_packet); + NetworkControlState GetState() { return cacher_.GetState(); } + + private: + PacketResult SimulateSend(SentPacket packet, + TimeDelta time_delta, + TimeDelta propagation_delay, + DataRate actual_bandwidth); + NetworkControlCacher cacher_; + std::unique_ptr controller_; + TimeDelta process_interval_; + Timestamp current_time_; + TimeDelta accumulated_delay_; + std::deque received_packets_; +}; +} // namespace test +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_NETWORK_CONTROL_TEST_NETWORK_CONTROL_TESTER_H_