diff options
Diffstat (limited to 'third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc | 1401 |
1 files changed, 1401 insertions, 0 deletions
diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc new file mode 100644 index 0000000000..7039fe7eb6 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc @@ -0,0 +1,1401 @@ +/* + * Copyright (c) 2017 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 <deque> +#include <limits> +#include <memory> +#include <string> +#include <tuple> + +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h" +#include "logging/rtc_event_log/events/rtc_event_alr_state.h" +#include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" +#include "logging/rtc_event_log/events/rtc_event_audio_playout.h" +#include "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h" +#include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" +#include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h" +#include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h" +#include "logging/rtc_event_log/events/rtc_event_probe_result_success.h" +#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h" +#include "logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h" +#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "logging/rtc_event_log/rtc_event_log_unittest_helper.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" +#include "modules/rtp_rtcp/source/rtcp_packet/bye.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "rtc_base/fake_clock.h" +#include "rtc_base/random.h" +#include "test/gtest.h" + +namespace webrtc { +class RtcEventLogEncoderTest + : public ::testing::TestWithParam< + std::tuple<int, RtcEventLog::EncodingType, size_t, bool>> { + protected: + RtcEventLogEncoderTest() + : seed_(std::get<0>(GetParam())), + prng_(seed_), + encoding_type_(std::get<1>(GetParam())), + event_count_(std::get<2>(GetParam())), + force_repeated_fields_(std::get<3>(GetParam())), + gen_(seed_ * 880001UL), + verifier_(encoding_type_) { + switch (encoding_type_) { + case RtcEventLog::EncodingType::Legacy: + encoder_ = std::make_unique<RtcEventLogEncoderLegacy>(); + break; + case RtcEventLog::EncodingType::NewFormat: + encoder_ = std::make_unique<RtcEventLogEncoderNewFormat>(); + break; + case RtcEventLog::EncodingType::ProtoFree: + encoder_ = std::make_unique<RtcEventLogEncoderV3>(); + break; + } + encoded_ = + encoder_->EncodeLogStart(rtc::TimeMillis(), rtc::TimeUTCMillis()); + } + ~RtcEventLogEncoderTest() override = default; + + // ANA events have some optional fields, so we want to make sure that we get + // correct behavior both when all of the values are there, as well as when + // only some. + void TestRtcEventAudioNetworkAdaptation( + const std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>>&); + + template <typename EventType> + std::unique_ptr<EventType> NewRtpPacket( + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map); + + template <typename ParsedType> + const std::vector<ParsedType>* GetRtpPacketsBySsrc( + const ParsedRtcEventLog* parsed_log, + uint32_t ssrc); + + template <typename EventType, typename ParsedType> + void TestRtpPackets(); + + std::deque<std::unique_ptr<RtcEvent>> history_; + std::unique_ptr<RtcEventLogEncoder> encoder_; + ParsedRtcEventLog parsed_log_; + const uint64_t seed_; + Random prng_; + const RtcEventLog::EncodingType encoding_type_; + const size_t event_count_; + const bool force_repeated_fields_; + test::EventGenerator gen_; + test::EventVerifier verifier_; + std::string encoded_; +}; + +void RtcEventLogEncoderTest::TestRtcEventAudioNetworkAdaptation( + const std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>>& + events) { + ASSERT_TRUE(history_.empty()) << "Function should be called once per test."; + + for (auto& event : events) { + history_.push_back(event->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& ana_configs = parsed_log_.audio_network_adaptation_events(); + + ASSERT_EQ(ana_configs.size(), events.size()); + for (size_t i = 0; i < events.size(); ++i) { + verifier_.VerifyLoggedAudioNetworkAdaptationEvent(*events[i], + ana_configs[i]); + } +} + +template <> +std::unique_ptr<RtcEventRtpPacketIncoming> RtcEventLogEncoderTest::NewRtpPacket( + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map) { + return gen_.NewRtpPacketIncoming(ssrc, extension_map, false); +} + +template <> +std::unique_ptr<RtcEventRtpPacketOutgoing> RtcEventLogEncoderTest::NewRtpPacket( + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map) { + return gen_.NewRtpPacketOutgoing(ssrc, extension_map, false); +} + +template <> +const std::vector<LoggedRtpPacketIncoming>* +RtcEventLogEncoderTest::GetRtpPacketsBySsrc(const ParsedRtcEventLog* parsed_log, + uint32_t ssrc) { + const auto& incoming_streams = parsed_log->incoming_rtp_packets_by_ssrc(); + for (const auto& stream : incoming_streams) { + if (stream.ssrc == ssrc) { + return &stream.incoming_packets; + } + } + return nullptr; +} + +template <> +const std::vector<LoggedRtpPacketOutgoing>* +RtcEventLogEncoderTest::GetRtpPacketsBySsrc(const ParsedRtcEventLog* parsed_log, + uint32_t ssrc) { + const auto& outgoing_streams = parsed_log->outgoing_rtp_packets_by_ssrc(); + for (const auto& stream : outgoing_streams) { + if (stream.ssrc == ssrc) { + return &stream.outgoing_packets; + } + } + return nullptr; +} + +template <typename EventType, typename ParsedType> +void RtcEventLogEncoderTest::TestRtpPackets() { + // SSRCs will be randomly assigned out of this small pool, significant only + // in that it also covers such edge cases as SSRC = 0 and SSRC = 0xffffffff. + // The pool is intentionally small, so as to produce collisions. + const std::vector<uint32_t> kSsrcPool = {0x00000000, 0x12345678, 0xabcdef01, + 0xffffffff, 0x20171024, 0x19840730, + 0x19831230}; + + // TODO(terelius): Test extensions for legacy encoding, too. + RtpHeaderExtensionMap extension_map; + if (encoding_type_ != RtcEventLog::EncodingType::Legacy) { + extension_map = gen_.NewRtpHeaderExtensionMap(true); + } + + // Simulate `event_count_` RTP packets, with SSRCs assigned randomly + // out of the small pool above. + std::map<uint32_t, std::vector<std::unique_ptr<EventType>>> events_by_ssrc; + for (size_t i = 0; i < event_count_; ++i) { + const uint32_t ssrc = kSsrcPool[prng_.Rand(kSsrcPool.size() - 1)]; + std::unique_ptr<EventType> event = + (events_by_ssrc[ssrc].empty() || !force_repeated_fields_) + ? NewRtpPacket<EventType>(ssrc, extension_map) + : events_by_ssrc[ssrc][0]->Copy(); + history_.push_back(event->Copy()); + events_by_ssrc[ssrc].emplace_back(std::move(event)); + } + + // Encode and parse. + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + // For each SSRC, make sure the RTP packets associated with it to have been + // correctly encoded and parsed. + for (auto it = events_by_ssrc.begin(); it != events_by_ssrc.end(); ++it) { + const uint32_t ssrc = it->first; + const auto& original_packets = it->second; + const std::vector<ParsedType>* parsed_rtp_packets = + GetRtpPacketsBySsrc<ParsedType>(&parsed_log_, ssrc); + ASSERT_NE(parsed_rtp_packets, nullptr); + ASSERT_EQ(original_packets.size(), parsed_rtp_packets->size()); + for (size_t i = 0; i < original_packets.size(); ++i) { + verifier_.VerifyLoggedRtpPacket<EventType, ParsedType>( + *original_packets[i], (*parsed_rtp_packets)[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventAlrState) { + std::vector<std::unique_ptr<RtcEventAlrState>> events(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) ? gen_.NewAlrState() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& alr_state_events = parsed_log_.alr_state_events(); + + ASSERT_EQ(alr_state_events.size(), event_count_); + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedAlrStateEvent(*events[i], alr_state_events[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRouteChange) { + if (encoding_type_ == RtcEventLog::EncodingType::Legacy) { + return; + } + std::vector<std::unique_ptr<RtcEventRouteChange>> events(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) ? gen_.NewRouteChange() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& route_change_events = parsed_log_.route_change_events(); + + ASSERT_EQ(route_change_events.size(), event_count_); + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedRouteChangeEvent(*events[i], route_change_events[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRemoteEstimate) { + std::vector<std::unique_ptr<RtcEventRemoteEstimate>> events(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) + ? gen_.NewRemoteEstimate() + : std::make_unique<RtcEventRemoteEstimate>(*events[0]); + history_.push_back(std::make_unique<RtcEventRemoteEstimate>(*events[i])); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& parsed_events = parsed_log_.remote_estimate_events(); + + ASSERT_EQ(parsed_events.size(), event_count_); + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedRemoteEstimateEvent(*events[i], parsed_events[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationBitrate) { + std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>(); + const int bitrate_bps = rtc::checked_cast<int>( + prng_.Rand(0, std::numeric_limits<int32_t>::max())); + runtime_config->bitrate_bps = bitrate_bps; + events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>( + std::move(runtime_config)); + } else { + events[i] = events[0]->Copy(); + } + } + TestRtcEventAudioNetworkAdaptation(events); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationFrameLength) { + std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>(); + const int frame_length_ms = prng_.Rand(1, 1000); + runtime_config->frame_length_ms = frame_length_ms; + events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>( + std::move(runtime_config)); + } else { + events[i] = events[0]->Copy(); + } + } + TestRtcEventAudioNetworkAdaptation(events); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationPacketLoss) { + std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + // To simplify the test, we just check powers of two. + const float plr = std::pow(0.5f, prng_.Rand(1, 8)); + auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>(); + runtime_config->uplink_packet_loss_fraction = plr; + events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>( + std::move(runtime_config)); + } else { + events[i] = events[0]->Copy(); + } + } + TestRtcEventAudioNetworkAdaptation(events); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationFec) { + std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>(); + runtime_config->enable_fec = prng_.Rand<bool>(); + events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>( + std::move(runtime_config)); + } else { + events[i] = events[0]->Copy(); + } + } + TestRtcEventAudioNetworkAdaptation(events); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationDtx) { + std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>(); + runtime_config->enable_dtx = prng_.Rand<bool>(); + events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>( + std::move(runtime_config)); + } else { + events[i] = events[0]->Copy(); + } + } + TestRtcEventAudioNetworkAdaptation(events); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationChannels) { + std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>(); + runtime_config->num_channels = prng_.Rand(1, 2); + events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>( + std::move(runtime_config)); + } else { + events[i] = events[0]->Copy(); + } + } + TestRtcEventAudioNetworkAdaptation(events); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationAll) { + std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>(); + runtime_config->bitrate_bps = rtc::checked_cast<int>( + prng_.Rand(0, std::numeric_limits<int32_t>::max())); + runtime_config->frame_length_ms = prng_.Rand(1, 1000); + runtime_config->uplink_packet_loss_fraction = + std::pow(0.5f, prng_.Rand(1, 8)); + runtime_config->enable_fec = prng_.Rand<bool>(); + runtime_config->enable_dtx = prng_.Rand<bool>(); + runtime_config->num_channels = prng_.Rand(1, 2); + events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>( + std::move(runtime_config)); + } else { + events[i] = events[0]->Copy(); + } + } + TestRtcEventAudioNetworkAdaptation(events); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventAudioPlayout) { + // SSRCs will be randomly assigned out of this small pool, significant only + // in that it also covers such edge cases as SSRC = 0 and SSRC = 0xffffffff. + // The pool is intentionally small, so as to produce collisions. + const std::vector<uint32_t> kSsrcPool = {0x00000000, 0x12345678, 0xabcdef01, + 0xffffffff, 0x20171024, 0x19840730, + 0x19831230}; + + std::map<uint32_t, std::vector<std::unique_ptr<RtcEventAudioPlayout>>> + original_events_by_ssrc; + for (size_t i = 0; i < event_count_; ++i) { + const uint32_t ssrc = kSsrcPool[prng_.Rand(kSsrcPool.size() - 1)]; + std::unique_ptr<RtcEventAudioPlayout> event = + (original_events_by_ssrc[ssrc].empty() || !force_repeated_fields_) + ? gen_.NewAudioPlayout(ssrc) + : original_events_by_ssrc[ssrc][0]->Copy(); + history_.push_back(event->Copy()); + original_events_by_ssrc[ssrc].push_back(std::move(event)); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& parsed_playout_events_by_ssrc = + parsed_log_.audio_playout_events(); + + // Same number of distinct SSRCs. + ASSERT_EQ(parsed_playout_events_by_ssrc.size(), + original_events_by_ssrc.size()); + + for (auto& original_event_it : original_events_by_ssrc) { + const uint32_t ssrc = original_event_it.first; + const auto& original_playout_events = original_event_it.second; + + const auto& parsed_event_it = parsed_playout_events_by_ssrc.find(ssrc); + ASSERT_TRUE(parsed_event_it != parsed_playout_events_by_ssrc.end()); + const auto& parsed_playout_events = parsed_event_it->second; + + // Same number playout events for the SSRC under examination. + ASSERT_EQ(original_playout_events.size(), parsed_playout_events.size()); + + for (size_t i = 0; i < original_playout_events.size(); ++i) { + verifier_.VerifyLoggedAudioPlayoutEvent(*original_playout_events[i], + parsed_playout_events[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventNetEqSetMinimumDelayDecoded) { + // SSRCs will be randomly assigned out of this small pool, significant only + // in that it also covers such edge cases as SSRC = 0 and SSRC = 0xffffffff. + // The pool is intentionally small, so as to produce collisions. + const std::vector<uint32_t> kSsrcPool = {0x00000000, 0x12345678, 0xabcdef01, + 0xffffffff, 0x20171024, 0x19840730, + 0x19831230}; + std::map<uint32_t, std::vector<std::unique_ptr<RtcEventNetEqSetMinimumDelay>>> + original_events_by_ssrc; + for (size_t i = 0; i < event_count_; ++i) { + const uint32_t ssrc = kSsrcPool[prng_.Rand(kSsrcPool.size() - 1)]; + std::unique_ptr<RtcEventNetEqSetMinimumDelay> event = + (original_events_by_ssrc[ssrc].empty() || !force_repeated_fields_) + ? gen_.NewNetEqSetMinimumDelay(ssrc) + : original_events_by_ssrc[ssrc][0]->Copy(); + history_.push_back(event->Copy()); + original_events_by_ssrc[ssrc].push_back(std::move(event)); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& parsed_neteq_set_minimum_delay_events_by_ssrc = + parsed_log_.neteq_set_minimum_delay_events(); + + if (encoding_type_ == RtcEventLog::EncodingType::Legacy) { + ASSERT_EQ(parsed_neteq_set_minimum_delay_events_by_ssrc.size(), 0u); + return; + } + + // Same number of distinct SSRCs. + ASSERT_EQ(parsed_neteq_set_minimum_delay_events_by_ssrc.size(), + original_events_by_ssrc.size()); + + for (auto& original_event_it : original_events_by_ssrc) { + const uint32_t ssrc = original_event_it.first; + const auto& original_neteq_set_minimum_delay_events = + original_event_it.second; + + const auto& parsed_event_it = + parsed_neteq_set_minimum_delay_events_by_ssrc.find(ssrc); + ASSERT_TRUE(parsed_event_it != + parsed_neteq_set_minimum_delay_events_by_ssrc.end()); + const auto& parsed_neteq_set_minimum_delay_events = parsed_event_it->second; + + // Same number playout events for the SSRC under examination. + ASSERT_EQ(original_neteq_set_minimum_delay_events.size(), + parsed_neteq_set_minimum_delay_events.size()); + + for (size_t i = 0; i < original_neteq_set_minimum_delay_events.size(); + ++i) { + verifier_.VerifyLoggedNetEqSetMinimumDelay( + *original_neteq_set_minimum_delay_events[i], + parsed_neteq_set_minimum_delay_events[i]); + } + } +} + +// TODO(eladalon/terelius): Test with multiple events in the batch. +TEST_P(RtcEventLogEncoderTest, RtcEventAudioReceiveStreamConfig) { + uint32_t ssrc = prng_.Rand<uint32_t>(); + RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap(); + std::unique_ptr<RtcEventAudioReceiveStreamConfig> event = + gen_.NewAudioReceiveStreamConfig(ssrc, extensions); + history_.push_back(event->Copy()); + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& audio_recv_configs = parsed_log_.audio_recv_configs(); + + ASSERT_EQ(audio_recv_configs.size(), 1u); + verifier_.VerifyLoggedAudioRecvConfig(*event, audio_recv_configs[0]); +} + +// TODO(eladalon/terelius): Test with multiple events in the batch. +TEST_P(RtcEventLogEncoderTest, RtcEventAudioSendStreamConfig) { + uint32_t ssrc = prng_.Rand<uint32_t>(); + RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap(); + std::unique_ptr<RtcEventAudioSendStreamConfig> event = + gen_.NewAudioSendStreamConfig(ssrc, extensions); + history_.push_back(event->Copy()); + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& audio_send_configs = parsed_log_.audio_send_configs(); + + ASSERT_EQ(audio_send_configs.size(), 1u); + verifier_.VerifyLoggedAudioSendConfig(*event, audio_send_configs[0]); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventBweUpdateDelayBased) { + std::vector<std::unique_ptr<RtcEventBweUpdateDelayBased>> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) + ? gen_.NewBweUpdateDelayBased() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& bwe_delay_updates = parsed_log_.bwe_delay_updates(); + ASSERT_EQ(bwe_delay_updates.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedBweDelayBasedUpdate(*events[i], bwe_delay_updates[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventBweUpdateLossBased) { + std::vector<std::unique_ptr<RtcEventBweUpdateLossBased>> events(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) + ? gen_.NewBweUpdateLossBased() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& bwe_loss_updates = parsed_log_.bwe_loss_updates(); + ASSERT_EQ(bwe_loss_updates.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedBweLossBasedUpdate(*events[i], bwe_loss_updates[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventGenericPacketReceived) { + if (encoding_type_ == RtcEventLog::EncodingType::Legacy) { + return; + } + std::vector<std::unique_ptr<RtcEventGenericPacketReceived>> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) + ? gen_.NewGenericPacketReceived() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& packets_received = parsed_log_.generic_packets_received(); + ASSERT_EQ(packets_received.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedGenericPacketReceived(*events[i], + packets_received[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventGenericPacketSent) { + if (encoding_type_ == RtcEventLog::EncodingType::Legacy) { + return; + } + std::vector<std::unique_ptr<RtcEventGenericPacketSent>> events(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) + ? gen_.NewGenericPacketSent() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& packets_sent = parsed_log_.generic_packets_sent(); + ASSERT_EQ(packets_sent.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedGenericPacketSent(*events[i], packets_sent[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventGenericAcksReceived) { + if (encoding_type_ == RtcEventLog::EncodingType::Legacy) { + return; + } + std::vector<std::unique_ptr<RtcEventGenericAckReceived>> events(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) + ? gen_.NewGenericAckReceived() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& decoded_events = parsed_log_.generic_acks_received(); + ASSERT_EQ(decoded_events.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedGenericAckReceived(*events[i], decoded_events[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventDtlsTransportState) { + std::vector<std::unique_ptr<RtcEventDtlsTransportState>> events(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) + ? gen_.NewDtlsTransportState() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& dtls_transport_states = parsed_log_.dtls_transport_states(); + if (encoding_type_ == RtcEventLog::EncodingType::Legacy) { + ASSERT_EQ(dtls_transport_states.size(), 0u); + return; + } + + ASSERT_EQ(dtls_transport_states.size(), event_count_); + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedDtlsTransportState(*events[i], + dtls_transport_states[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventDtlsWritableState) { + std::vector<std::unique_ptr<RtcEventDtlsWritableState>> events(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) + ? gen_.NewDtlsWritableState() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& dtls_writable_states = parsed_log_.dtls_writable_states(); + if (encoding_type_ == RtcEventLog::EncodingType::Legacy) { + ASSERT_EQ(dtls_writable_states.size(), 0u); + return; + } + + ASSERT_EQ(dtls_writable_states.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedDtlsWritableState(*events[i], + dtls_writable_states[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventFrameDecoded) { + // SSRCs will be randomly assigned out of this small pool, significant only + // in that it also covers such edge cases as SSRC = 0 and SSRC = 0xffffffff. + // The pool is intentionally small, so as to produce collisions. + const std::vector<uint32_t> kSsrcPool = {0x00000000, 0x12345678, 0xabcdef01, + 0xffffffff, 0x20171024, 0x19840730, + 0x19831230}; + + std::map<uint32_t, std::vector<std::unique_ptr<RtcEventFrameDecoded>>> + original_events_by_ssrc; + for (size_t i = 0; i < event_count_; ++i) { + const uint32_t ssrc = kSsrcPool[prng_.Rand(kSsrcPool.size() - 1)]; + std::unique_ptr<RtcEventFrameDecoded> event = + (original_events_by_ssrc[ssrc].empty() || !force_repeated_fields_) + ? gen_.NewFrameDecodedEvent(ssrc) + : original_events_by_ssrc[ssrc][0]->Copy(); + history_.push_back(event->Copy()); + original_events_by_ssrc[ssrc].push_back(std::move(event)); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + auto status = parsed_log_.ParseString(encoded_); + if (!status.ok()) + RTC_LOG(LS_ERROR) << status.message(); + ASSERT_TRUE(status.ok()); + + const auto& decoded_frames_by_ssrc = parsed_log_.decoded_frames(); + if (encoding_type_ == RtcEventLog::EncodingType::Legacy) { + ASSERT_EQ(decoded_frames_by_ssrc.size(), 0u); + return; + } + + // Same number of distinct SSRCs. + ASSERT_EQ(decoded_frames_by_ssrc.size(), original_events_by_ssrc.size()); + + for (const auto& original_event_it : original_events_by_ssrc) { + const uint32_t ssrc = original_event_it.first; + const std::vector<std::unique_ptr<RtcEventFrameDecoded>>& original_frames = + original_event_it.second; + + const auto& parsed_event_it = decoded_frames_by_ssrc.find(ssrc); + ASSERT_TRUE(parsed_event_it != decoded_frames_by_ssrc.end()); + const std::vector<LoggedFrameDecoded>& parsed_frames = + parsed_event_it->second; + + // Same number events for the SSRC under examination. + ASSERT_EQ(original_frames.size(), parsed_frames.size()); + + for (size_t i = 0; i < original_frames.size(); ++i) { + verifier_.VerifyLoggedFrameDecoded(*original_frames[i], parsed_frames[i]); + } + } +} + +// TODO(eladalon/terelius): Test with multiple events in the batch. +TEST_P(RtcEventLogEncoderTest, RtcEventIceCandidatePairConfig) { + std::unique_ptr<RtcEventIceCandidatePairConfig> event = + gen_.NewIceCandidatePairConfig(); + history_.push_back(event->Copy()); + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& ice_candidate_pair_configs = + parsed_log_.ice_candidate_pair_configs(); + + ASSERT_EQ(ice_candidate_pair_configs.size(), 1u); + verifier_.VerifyLoggedIceCandidatePairConfig(*event, + ice_candidate_pair_configs[0]); +} + +// TODO(eladalon/terelius): Test with multiple events in the batch. +TEST_P(RtcEventLogEncoderTest, RtcEventIceCandidatePair) { + std::unique_ptr<RtcEventIceCandidatePair> event = gen_.NewIceCandidatePair(); + history_.push_back(event->Copy()); + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& ice_candidate_pair_events = + parsed_log_.ice_candidate_pair_events(); + + ASSERT_EQ(ice_candidate_pair_events.size(), 1u); + verifier_.VerifyLoggedIceCandidatePairEvent(*event, + ice_candidate_pair_events[0]); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventLoggingStarted) { + const int64_t timestamp_ms = prng_.Rand(1'000'000'000); + const int64_t utc_time_ms = prng_.Rand(1'000'000'000); + + // Overwrite the previously encoded LogStart event. + encoded_ = encoder_->EncodeLogStart(timestamp_ms * 1000, utc_time_ms * 1000); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& start_log_events = parsed_log_.start_log_events(); + + ASSERT_EQ(start_log_events.size(), 1u); + verifier_.VerifyLoggedStartEvent(timestamp_ms * 1000, utc_time_ms * 1000, + start_log_events[0]); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventLoggingStopped) { + const int64_t start_timestamp_ms = prng_.Rand(1'000'000'000); + const int64_t start_utc_time_ms = prng_.Rand(1'000'000'000); + + // Overwrite the previously encoded LogStart event. + encoded_ = encoder_->EncodeLogStart(start_timestamp_ms * 1000, + start_utc_time_ms * 1000); + + const int64_t stop_timestamp_ms = + prng_.Rand(start_timestamp_ms, 2'000'000'000); + encoded_ += encoder_->EncodeLogEnd(stop_timestamp_ms * 1000); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& stop_log_events = parsed_log_.stop_log_events(); + + ASSERT_EQ(stop_log_events.size(), 1u); + verifier_.VerifyLoggedStopEvent(stop_timestamp_ms * 1000, stop_log_events[0]); +} + +// TODO(eladalon/terelius): Test with multiple events in the batch. +TEST_P(RtcEventLogEncoderTest, RtcEventProbeClusterCreated) { + std::unique_ptr<RtcEventProbeClusterCreated> event = + gen_.NewProbeClusterCreated(); + history_.push_back(event->Copy()); + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& bwe_probe_cluster_created_events = + parsed_log_.bwe_probe_cluster_created_events(); + + ASSERT_EQ(bwe_probe_cluster_created_events.size(), 1u); + verifier_.VerifyLoggedBweProbeClusterCreatedEvent( + *event, bwe_probe_cluster_created_events[0]); +} + +// TODO(eladalon/terelius): Test with multiple events in the batch. +TEST_P(RtcEventLogEncoderTest, RtcEventProbeResultFailure) { + std::unique_ptr<RtcEventProbeResultFailure> event = + gen_.NewProbeResultFailure(); + history_.push_back(event->Copy()); + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& bwe_probe_failure_events = parsed_log_.bwe_probe_failure_events(); + + ASSERT_EQ(bwe_probe_failure_events.size(), 1u); + verifier_.VerifyLoggedBweProbeFailureEvent(*event, + bwe_probe_failure_events[0]); +} + +// TODO(eladalon/terelius): Test with multiple events in the batch. +TEST_P(RtcEventLogEncoderTest, RtcEventProbeResultSuccess) { + std::unique_ptr<RtcEventProbeResultSuccess> event = + gen_.NewProbeResultSuccess(); + history_.push_back(event->Copy()); + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& bwe_probe_success_events = parsed_log_.bwe_probe_success_events(); + + ASSERT_EQ(bwe_probe_success_events.size(), 1u); + verifier_.VerifyLoggedBweProbeSuccessEvent(*event, + bwe_probe_success_events[0]); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpPacketIncoming) { + if (force_repeated_fields_) { + // RTCP packets maybe delivered twice (once for audio and once for video). + // As a work around, we're removing duplicates in the parser. + return; + } + + std::vector<std::unique_ptr<RtcEventRtcpPacketIncoming>> events(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) + ? gen_.NewRtcpPacketIncoming() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& incoming_rtcp_packets = parsed_log_.incoming_rtcp_packets(); + ASSERT_EQ(incoming_rtcp_packets.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedRtcpPacketIncoming(*events[i], + incoming_rtcp_packets[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpPacketOutgoing) { + std::vector<std::unique_ptr<RtcEventRtcpPacketOutgoing>> events(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + events[i] = (i == 0 || !force_repeated_fields_) + ? gen_.NewRtcpPacketOutgoing() + : events[0]->Copy(); + history_.push_back(events[i]->Copy()); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& outgoing_rtcp_packets = parsed_log_.outgoing_rtcp_packets(); + ASSERT_EQ(outgoing_rtcp_packets.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedRtcpPacketOutgoing(*events[i], + outgoing_rtcp_packets[i]); + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpReceiverReport) { + if (force_repeated_fields_) { + return; + } + + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>())); + + for (auto direction : {kIncomingPacket, kOutgoingPacket}) { + std::vector<rtcp::ReceiverReport> events(event_count_); + std::vector<int64_t> timestamps_ms(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + timestamps_ms[i] = rtc::TimeMillis(); + events[i] = gen_.NewReceiverReport(); + rtc::Buffer buffer = events[i].Build(); + if (direction == kIncomingPacket) { + history_.push_back( + std::make_unique<RtcEventRtcpPacketIncoming>(buffer)); + } else { + history_.push_back( + std::make_unique<RtcEventRtcpPacketOutgoing>(buffer)); + } + fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000))); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& receiver_reports = parsed_log_.receiver_reports(direction); + ASSERT_EQ(receiver_reports.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedReceiverReport(timestamps_ms[i], events[i], + receiver_reports[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpSenderReport) { + if (force_repeated_fields_) { + return; + } + + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>())); + + for (auto direction : {kIncomingPacket, kOutgoingPacket}) { + std::vector<rtcp::SenderReport> events(event_count_); + std::vector<int64_t> timestamps_ms(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + timestamps_ms[i] = rtc::TimeMillis(); + events[i] = gen_.NewSenderReport(); + rtc::Buffer buffer = events[i].Build(); + if (direction == kIncomingPacket) { + history_.push_back( + std::make_unique<RtcEventRtcpPacketIncoming>(buffer)); + } else { + history_.push_back( + std::make_unique<RtcEventRtcpPacketOutgoing>(buffer)); + } + fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000))); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& sender_reports = parsed_log_.sender_reports(direction); + ASSERT_EQ(sender_reports.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedSenderReport(timestamps_ms[i], events[i], + sender_reports[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpExtendedReports) { + if (force_repeated_fields_) { + return; + } + + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>())); + + for (auto direction : {kIncomingPacket, kOutgoingPacket}) { + std::vector<rtcp::ExtendedReports> events(event_count_); + std::vector<int64_t> timestamps_ms(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + timestamps_ms[i] = rtc::TimeMillis(); + events[i] = gen_.NewExtendedReports(); + rtc::Buffer buffer = events[i].Build(); + if (direction == kIncomingPacket) { + history_.push_back( + std::make_unique<RtcEventRtcpPacketIncoming>(buffer)); + } else { + history_.push_back( + std::make_unique<RtcEventRtcpPacketOutgoing>(buffer)); + } + fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000))); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& extended_reports = parsed_log_.extended_reports(direction); + ASSERT_EQ(extended_reports.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedExtendedReports(timestamps_ms[i], events[i], + extended_reports[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpFir) { + if (force_repeated_fields_) { + return; + } + + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>())); + + for (auto direction : {kIncomingPacket, kOutgoingPacket}) { + std::vector<rtcp::Fir> events(event_count_); + std::vector<int64_t> timestamps_ms(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + timestamps_ms[i] = rtc::TimeMillis(); + events[i] = gen_.NewFir(); + rtc::Buffer buffer = events[i].Build(); + if (direction == kIncomingPacket) { + history_.push_back( + std::make_unique<RtcEventRtcpPacketIncoming>(buffer)); + } else { + history_.push_back( + std::make_unique<RtcEventRtcpPacketOutgoing>(buffer)); + } + fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000))); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& firs = parsed_log_.firs(direction); + ASSERT_EQ(firs.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedFir(timestamps_ms[i], events[i], firs[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpPli) { + if (force_repeated_fields_) { + return; + } + + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>())); + + for (auto direction : {kIncomingPacket, kOutgoingPacket}) { + std::vector<rtcp::Pli> events(event_count_); + std::vector<int64_t> timestamps_ms(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + timestamps_ms[i] = rtc::TimeMillis(); + events[i] = gen_.NewPli(); + rtc::Buffer buffer = events[i].Build(); + if (direction == kIncomingPacket) { + history_.push_back( + std::make_unique<RtcEventRtcpPacketIncoming>(buffer)); + } else { + history_.push_back( + std::make_unique<RtcEventRtcpPacketOutgoing>(buffer)); + } + fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000))); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& plis = parsed_log_.plis(direction); + ASSERT_EQ(plis.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedPli(timestamps_ms[i], events[i], plis[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpBye) { + if (force_repeated_fields_) { + return; + } + + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>())); + + for (auto direction : {kIncomingPacket, kOutgoingPacket}) { + std::vector<rtcp::Bye> events(event_count_); + std::vector<int64_t> timestamps_ms(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + timestamps_ms[i] = rtc::TimeMillis(); + events[i] = gen_.NewBye(); + rtc::Buffer buffer = events[i].Build(); + if (direction == kIncomingPacket) { + history_.push_back( + std::make_unique<RtcEventRtcpPacketIncoming>(buffer)); + } else { + history_.push_back( + std::make_unique<RtcEventRtcpPacketOutgoing>(buffer)); + } + fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000))); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& byes = parsed_log_.byes(direction); + ASSERT_EQ(byes.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedBye(timestamps_ms[i], events[i], byes[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpNack) { + if (force_repeated_fields_) { + return; + } + + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>())); + + for (auto direction : {kIncomingPacket, kOutgoingPacket}) { + std::vector<rtcp::Nack> events(event_count_); + std::vector<int64_t> timestamps_ms(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + timestamps_ms[i] = rtc::TimeMillis(); + events[i] = gen_.NewNack(); + rtc::Buffer buffer = events[i].Build(); + if (direction == kIncomingPacket) { + history_.push_back( + std::make_unique<RtcEventRtcpPacketIncoming>(buffer)); + } else { + history_.push_back( + std::make_unique<RtcEventRtcpPacketOutgoing>(buffer)); + } + fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000))); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& nacks = parsed_log_.nacks(direction); + ASSERT_EQ(nacks.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedNack(timestamps_ms[i], events[i], nacks[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpRemb) { + if (force_repeated_fields_) { + return; + } + + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>())); + + for (auto direction : {kIncomingPacket, kOutgoingPacket}) { + std::vector<rtcp::Remb> events(event_count_); + std::vector<int64_t> timestamps_ms(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + timestamps_ms[i] = rtc::TimeMillis(); + events[i] = gen_.NewRemb(); + rtc::Buffer buffer = events[i].Build(); + if (direction == kIncomingPacket) { + history_.push_back( + std::make_unique<RtcEventRtcpPacketIncoming>(buffer)); + } else { + history_.push_back( + std::make_unique<RtcEventRtcpPacketOutgoing>(buffer)); + } + fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000))); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& rembs = parsed_log_.rembs(direction); + ASSERT_EQ(rembs.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedRemb(timestamps_ms[i], events[i], rembs[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpTransportFeedback) { + if (force_repeated_fields_) { + return; + } + + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>())); + + for (auto direction : {kIncomingPacket, kOutgoingPacket}) { + std::vector<rtcp::TransportFeedback> events; + events.reserve(event_count_); + std::vector<int64_t> timestamps_ms(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + timestamps_ms[i] = rtc::TimeMillis(); + events.emplace_back(gen_.NewTransportFeedback()); + rtc::Buffer buffer = events[i].Build(); + if (direction == kIncomingPacket) { + history_.push_back( + std::make_unique<RtcEventRtcpPacketIncoming>(buffer)); + } else { + history_.push_back( + std::make_unique<RtcEventRtcpPacketOutgoing>(buffer)); + } + fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000))); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& transport_feedbacks = + parsed_log_.transport_feedbacks(direction); + ASSERT_EQ(transport_feedbacks.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedTransportFeedback(timestamps_ms[i], events[i], + transport_feedbacks[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtcpLossNotification) { + if (force_repeated_fields_) { + return; + } + + rtc::ScopedFakeClock fake_clock; + fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>())); + + for (auto direction : {kIncomingPacket, kOutgoingPacket}) { + std::vector<rtcp::LossNotification> events; + events.reserve(event_count_); + std::vector<int64_t> timestamps_ms(event_count_); + for (size_t i = 0; i < event_count_; ++i) { + timestamps_ms[i] = rtc::TimeMillis(); + events.emplace_back(gen_.NewLossNotification()); + rtc::Buffer buffer = events[i].Build(); + if (direction == kIncomingPacket) { + history_.push_back( + std::make_unique<RtcEventRtcpPacketIncoming>(buffer)); + } else { + history_.push_back( + std::make_unique<RtcEventRtcpPacketOutgoing>(buffer)); + } + fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000))); + } + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + + const auto& loss_notifications = parsed_log_.loss_notifications(direction); + ASSERT_EQ(loss_notifications.size(), event_count_); + + for (size_t i = 0; i < event_count_; ++i) { + verifier_.VerifyLoggedLossNotification(timestamps_ms[i], events[i], + loss_notifications[i]); + } + } +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtpPacketIncoming) { + TestRtpPackets<RtcEventRtpPacketIncoming, LoggedRtpPacketIncoming>(); +} + +TEST_P(RtcEventLogEncoderTest, RtcEventRtpPacketOutgoing) { + TestRtpPackets<RtcEventRtpPacketOutgoing, LoggedRtpPacketOutgoing>(); +} + +// TODO(eladalon/terelius): Test with multiple events in the batch. +TEST_P(RtcEventLogEncoderTest, RtcEventVideoReceiveStreamConfig) { + uint32_t ssrc = prng_.Rand<uint32_t>(); + RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap(); + std::unique_ptr<RtcEventVideoReceiveStreamConfig> event = + gen_.NewVideoReceiveStreamConfig(ssrc, extensions); + history_.push_back(event->Copy()); + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& video_recv_configs = parsed_log_.video_recv_configs(); + + ASSERT_EQ(video_recv_configs.size(), 1u); + verifier_.VerifyLoggedVideoRecvConfig(*event, video_recv_configs[0]); +} + +// TODO(eladalon/terelius): Test with multiple events in the batch. +TEST_P(RtcEventLogEncoderTest, RtcEventVideoSendStreamConfig) { + uint32_t ssrc = prng_.Rand<uint32_t>(); + RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap(); + std::unique_ptr<RtcEventVideoSendStreamConfig> event = + gen_.NewVideoSendStreamConfig(ssrc, extensions); + history_.push_back(event->Copy()); + + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok()); + const auto& video_send_configs = parsed_log_.video_send_configs(); + + ASSERT_EQ(video_send_configs.size(), 1u); + verifier_.VerifyLoggedVideoSendConfig(*event, video_send_configs[0]); +} + +INSTANTIATE_TEST_SUITE_P( + RandomSeeds, + RtcEventLogEncoderTest, + ::testing::Combine(/* Random seed*: */ ::testing::Values(1, 2, 3, 4, 5), + /* Encoding: */ + ::testing::Values(RtcEventLog::EncodingType::Legacy, + RtcEventLog::EncodingType::NewFormat), + /* Event count: */ ::testing::Values(1, 2, 10, 100), + /* Repeated fields: */ ::testing::Bool())); + +class RtcEventLogEncoderSimpleTest + : public ::testing::TestWithParam<RtcEventLog::EncodingType> { + protected: + RtcEventLogEncoderSimpleTest() : encoding_type_(GetParam()) { + switch (encoding_type_) { + case RtcEventLog::EncodingType::Legacy: + encoder_ = std::make_unique<RtcEventLogEncoderLegacy>(); + break; + case RtcEventLog::EncodingType::NewFormat: + encoder_ = std::make_unique<RtcEventLogEncoderNewFormat>(); + break; + case RtcEventLog::EncodingType::ProtoFree: + encoder_ = std::make_unique<RtcEventLogEncoderV3>(); + break; + } + encoded_ = + encoder_->EncodeLogStart(rtc::TimeMillis(), rtc::TimeUTCMillis()); + } + ~RtcEventLogEncoderSimpleTest() override = default; + + std::deque<std::unique_ptr<RtcEvent>> history_; + std::unique_ptr<RtcEventLogEncoder> encoder_; + ParsedRtcEventLog parsed_log_; + const RtcEventLog::EncodingType encoding_type_; + std::string encoded_; +}; + +TEST_P(RtcEventLogEncoderSimpleTest, RtcEventLargeCompoundRtcpPacketIncoming) { + // Create a compound packet containing multiple Bye messages. + rtc::Buffer packet; + size_t index = 0; + for (int i = 0; i < 8; i++) { + rtcp::Bye bye; + std::string reason(255, 'a'); // Add some arbitrary data. + bye.SetReason(reason); + bye.SetSenderSsrc(0x12345678); + packet.SetSize(packet.size() + bye.BlockLength()); + bool created = + bye.Create(packet.data(), &index, packet.capacity(), nullptr); + ASSERT_TRUE(created); + ASSERT_EQ(index, packet.size()); + } + + EXPECT_GT(packet.size(), static_cast<size_t>(IP_PACKET_SIZE)); + auto event = std::make_unique<RtcEventRtcpPacketIncoming>(packet); + history_.push_back(event->Copy()); + encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end()); + + ParsedRtcEventLog::ParseStatus status = parsed_log_.ParseString(encoded_); + ASSERT_TRUE(status.ok()) << status.message(); + + const auto& incoming_rtcp_packets = parsed_log_.incoming_rtcp_packets(); + ASSERT_EQ(incoming_rtcp_packets.size(), 1u); + ASSERT_EQ(incoming_rtcp_packets[0].rtcp.raw_data.size(), packet.size()); + EXPECT_EQ(memcmp(incoming_rtcp_packets[0].rtcp.raw_data.data(), packet.data(), + packet.size()), + 0); +} + +INSTANTIATE_TEST_SUITE_P( + LargeCompoundRtcp, + RtcEventLogEncoderSimpleTest, + ::testing::Values(RtcEventLog::EncodingType::Legacy, + RtcEventLog::EncodingType::NewFormat)); + +} // namespace webrtc |