|  | /* | 
|  | *  Copyright (c) 2023 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 "logging/rtc_event_log/dependency_descriptor_encoder_decoder.h" | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "logging/rtc_event_log/encoder/delta_encoding.h" | 
|  | #include "logging/rtc_event_log/encoder/optional_blob_encoding.h" | 
|  | #include "logging/rtc_event_log/rtc_event_log2_proto_include.h"  // IWYU pragma: keep | 
|  | #include "rtc_base/checks.h" | 
|  | #include "test/gmock.h" | 
|  | #include "test/gtest.h" | 
|  |  | 
|  | using ::testing::ElementsAre; | 
|  | using ::testing::Eq; | 
|  | using ::testing::IsEmpty; | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace { | 
|  |  | 
|  | constexpr uint64_t kNone = 0; | 
|  | constexpr uint64_t kEnd = 1; | 
|  | constexpr uint64_t kBegin = 2; | 
|  | constexpr uint64_t kBeginEnd = 3; | 
|  |  | 
|  | class DdBuilder { | 
|  | public: | 
|  | DdBuilder() : raw_dd(3) {} | 
|  | DdBuilder& B() { | 
|  | raw_dd[0] |= 1 << 7; | 
|  | return *this; | 
|  | } | 
|  | DdBuilder& E() { | 
|  | raw_dd[0] |= 1 << 6; | 
|  | return *this; | 
|  | } | 
|  | DdBuilder& Tid(uint8_t id) { | 
|  | RTC_DCHECK_LE(id, 0x3F); | 
|  | raw_dd[0] |= id; | 
|  | return *this; | 
|  | } | 
|  | DdBuilder& Fid(uint16_t id) { | 
|  | raw_dd[1] = id >> 8; | 
|  | raw_dd[2] = id; | 
|  | return *this; | 
|  | } | 
|  | DdBuilder& Ext(const std::string& ext) { | 
|  | raw_dd.insert(raw_dd.end(), ext.begin(), ext.end()); | 
|  | return *this; | 
|  | } | 
|  | std::vector<uint8_t> Build() { return raw_dd; } | 
|  |  | 
|  | private: | 
|  | std::vector<uint8_t> raw_dd; | 
|  | }; | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorEncoding, SinglePacket) { | 
|  | auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( | 
|  | {DdBuilder().B().E().Tid(12).Fid(345).Ext("abc").Build()}); | 
|  | EXPECT_THAT(encoded->start_end_bit(), Eq(kBeginEnd)); | 
|  | EXPECT_THAT(encoded->template_id(), Eq(12u)); | 
|  | EXPECT_THAT(encoded->frame_id(), Eq(345u)); | 
|  | EXPECT_THAT(encoded->extended_infos(), Eq(EncodeOptionalBlobs({{"abc"}}))); | 
|  | EXPECT_THAT(encoded->has_start_end_bit_deltas(), Eq(false)); | 
|  | EXPECT_THAT(encoded->has_template_id_deltas(), Eq(false)); | 
|  | EXPECT_THAT(encoded->has_frame_id_deltas(), Eq(false)); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorEncoding, MultiplePackets) { | 
|  | auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( | 
|  | {DdBuilder().B().E().Tid(1).Fid(1).Ext("abc").Build(), | 
|  | DdBuilder().B().Tid(2).Fid(3).Ext("def").Build(), | 
|  | DdBuilder().E().Tid(2).Fid(3).Build()}); | 
|  |  | 
|  | EXPECT_THAT(encoded->start_end_bit(), Eq(kBeginEnd)); | 
|  | EXPECT_THAT(encoded->template_id(), Eq(1u)); | 
|  | EXPECT_THAT(encoded->frame_id(), Eq(1u)); | 
|  | EXPECT_THAT(encoded->extended_infos(), | 
|  | Eq(EncodeOptionalBlobs({{"abc"}, {"def"}, {}}))); | 
|  | EXPECT_THAT(encoded->start_end_bit_deltas(), | 
|  | Eq(EncodeDeltas({kBeginEnd}, {{kBegin}, {kEnd}}))); | 
|  | EXPECT_THAT(encoded->template_id_deltas(), Eq(EncodeDeltas({1}, {{2}, {2}}))); | 
|  | EXPECT_THAT(encoded->frame_id_deltas(), Eq(EncodeDeltas({1}, {{3}, {3}}))); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorEncoding, MultiplePacketsOneFrame) { | 
|  | auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( | 
|  | {DdBuilder().B().Tid(1).Fid(1).Ext("abc").Build(), | 
|  | DdBuilder().Tid(1).Fid(1).Build(), | 
|  | DdBuilder().E().Tid(1).Fid(1).Build()}); | 
|  |  | 
|  | EXPECT_THAT(encoded->start_end_bit(), Eq(kBegin)); | 
|  | EXPECT_THAT(encoded->template_id(), Eq(1u)); | 
|  | EXPECT_THAT(encoded->frame_id(), Eq(1u)); | 
|  | EXPECT_THAT(encoded->extended_infos(), | 
|  | Eq(EncodeOptionalBlobs({{"abc"}, {}, {}}))); | 
|  | EXPECT_THAT(encoded->start_end_bit_deltas(), | 
|  | Eq(EncodeDeltas({kBegin}, {{kNone}, {kEnd}}))); | 
|  | EXPECT_THAT(encoded->has_template_id_deltas(), Eq(false)); | 
|  | EXPECT_THAT(encoded->has_frame_id_deltas(), Eq(false)); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorEncoding, FirstPacketMissingDd) { | 
|  | auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( | 
|  | {{}, | 
|  | DdBuilder().B().E().Tid(1).Fid(1).Build(), | 
|  | DdBuilder().E().Tid(2).Fid(3).Ext("three!").Build()}); | 
|  |  | 
|  | EXPECT_THAT(encoded->has_start_end_bit(), Eq(false)); | 
|  | EXPECT_THAT(encoded->has_template_id(), Eq(false)); | 
|  | EXPECT_THAT(encoded->has_frame_id(), Eq(false)); | 
|  |  | 
|  | EXPECT_THAT(encoded->extended_infos(), | 
|  | Eq(EncodeOptionalBlobs({{}, {}, {"three!"}}))); | 
|  | EXPECT_THAT(encoded->start_end_bit_deltas(), | 
|  | Eq(EncodeDeltas({}, {{kBeginEnd}, {kEnd}}))); | 
|  | EXPECT_THAT(encoded->template_id_deltas(), Eq(EncodeDeltas({}, {{1}, {2}}))); | 
|  | EXPECT_THAT(encoded->frame_id_deltas(), Eq(EncodeDeltas({}, {{1}, {3}}))); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorEncoding, SomePacketMissingDd) { | 
|  | auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( | 
|  | {DdBuilder().B().E().Tid(1).Fid(1).Build(), | 
|  | {}, | 
|  | DdBuilder().E().Tid(2).Fid(3).Build()}); | 
|  |  | 
|  | EXPECT_THAT(encoded->start_end_bit(), Eq(kBeginEnd)); | 
|  | EXPECT_THAT(encoded->template_id(), Eq(1u)); | 
|  | EXPECT_THAT(encoded->frame_id(), Eq(1u)); | 
|  | EXPECT_THAT(encoded->extended_infos(), Eq(EncodeOptionalBlobs({{}, {}, {}}))); | 
|  | EXPECT_THAT(encoded->start_end_bit_deltas(), | 
|  | Eq(EncodeDeltas({kBeginEnd}, {{}, {kEnd}}))); | 
|  | EXPECT_THAT(encoded->template_id_deltas(), Eq(EncodeDeltas({1}, {{}, {2}}))); | 
|  | EXPECT_THAT(encoded->frame_id_deltas(), Eq(EncodeDeltas({1}, {{}, {3}}))); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorDecoding, SinglePacket) { | 
|  | rtclog2::DependencyDescriptorsWireInfo encoded; | 
|  | encoded.set_start_end_bit(kBeginEnd); | 
|  | encoded.set_template_id(1); | 
|  | encoded.set_frame_id(2); | 
|  | encoded.set_extended_infos(EncodeOptionalBlobs({"abc"})); | 
|  |  | 
|  | auto decoded = | 
|  | RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 1); | 
|  |  | 
|  | auto expected = DdBuilder().B().E().Tid(1).Fid(2).Ext("abc").Build(); | 
|  | ASSERT_THAT(decoded.ok(), Eq(true)); | 
|  | EXPECT_THAT(decoded.value(), ElementsAre(expected)); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorDecoding, MultiplePackets) { | 
|  | rtclog2::DependencyDescriptorsWireInfo encoded; | 
|  | encoded.set_start_end_bit(kBeginEnd); | 
|  | encoded.set_template_id(1); | 
|  | encoded.set_frame_id(1); | 
|  | encoded.set_extended_infos(EncodeOptionalBlobs({{"abc"}, {"def"}, {}})); | 
|  | encoded.set_start_end_bit_deltas( | 
|  | EncodeDeltas({kBeginEnd}, {{kBegin}, {kEnd}})); | 
|  | encoded.set_template_id_deltas(EncodeDeltas({1}, {{2}, {2}})); | 
|  | encoded.set_frame_id_deltas(EncodeDeltas({1}, {{3}, {3}})); | 
|  |  | 
|  | auto decoded = | 
|  | RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); | 
|  |  | 
|  | auto first_expected = DdBuilder().B().E().Tid(1).Fid(1).Ext("abc").Build(); | 
|  | auto second_expected = DdBuilder().B().Tid(2).Fid(3).Ext("def").Build(); | 
|  | auto third_expected = DdBuilder().E().Tid(2).Fid(3).Build(); | 
|  | ASSERT_THAT(decoded.ok(), Eq(true)); | 
|  | EXPECT_THAT(decoded.value(), | 
|  | ElementsAre(first_expected, second_expected, third_expected)); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorDecoding, MultiplePacketsOneFrame) { | 
|  | rtclog2::DependencyDescriptorsWireInfo encoded; | 
|  | encoded.set_start_end_bit(kBegin); | 
|  | encoded.set_template_id(1u); | 
|  | encoded.set_frame_id(1u); | 
|  | encoded.set_extended_infos(EncodeOptionalBlobs({{"abc"}, {}, {}})); | 
|  | encoded.set_start_end_bit_deltas(EncodeDeltas({kBegin}, {{kNone}, {kEnd}})); | 
|  |  | 
|  | auto decoded = | 
|  | RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); | 
|  |  | 
|  | auto first_expected = DdBuilder().B().Tid(1).Fid(1).Ext("abc").Build(); | 
|  | auto second_expected = DdBuilder().Tid(1).Fid(1).Build(); | 
|  | auto third_expected = DdBuilder().E().Tid(1).Fid(1).Build(); | 
|  | ASSERT_THAT(decoded.ok(), Eq(true)); | 
|  | EXPECT_THAT(decoded.value(), | 
|  | ElementsAre(first_expected, second_expected, third_expected)); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorDecoding, FirstPacketMissingDd) { | 
|  | rtclog2::DependencyDescriptorsWireInfo encoded; | 
|  | encoded.set_extended_infos(EncodeOptionalBlobs({{}, {}, {"three!"}})); | 
|  | encoded.set_start_end_bit_deltas(EncodeDeltas({}, {{kBeginEnd}, {kEnd}})); | 
|  | encoded.set_template_id_deltas(EncodeDeltas({}, {{1}, {2}})); | 
|  | encoded.set_frame_id_deltas(EncodeDeltas({}, {{1}, {3}})); | 
|  |  | 
|  | auto decoded = | 
|  | RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); | 
|  |  | 
|  | auto second_expected = DdBuilder().B().E().Tid(1).Fid(1).Build(); | 
|  | auto third_expected = DdBuilder().E().Tid(2).Fid(3).Ext("three!").Build(); | 
|  | ASSERT_THAT(decoded.ok(), Eq(true)); | 
|  | EXPECT_THAT(decoded.value(), | 
|  | ElementsAre(IsEmpty(), second_expected, third_expected)); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorDecoding, SomePacketMissingDd) { | 
|  | rtclog2::DependencyDescriptorsWireInfo encoded; | 
|  | encoded.set_start_end_bit(kBeginEnd); | 
|  | encoded.set_template_id(1u); | 
|  | encoded.set_frame_id(1u); | 
|  | encoded.set_extended_infos(EncodeOptionalBlobs({{}, {}, {}})); | 
|  | encoded.set_start_end_bit_deltas(EncodeDeltas({kBeginEnd}, {{}, {kEnd}})); | 
|  | encoded.set_template_id_deltas(EncodeDeltas({1}, {{}, {2}})); | 
|  | encoded.set_frame_id_deltas(EncodeDeltas({1}, {{}, {3}})); | 
|  |  | 
|  | auto decoded = | 
|  | RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); | 
|  |  | 
|  | auto first_expected = DdBuilder().B().E().Tid(1).Fid(1).Build(); | 
|  | auto third_expected = DdBuilder().E().Tid(2).Fid(3).Build(); | 
|  | ASSERT_THAT(decoded.ok(), Eq(true)); | 
|  | EXPECT_THAT(decoded.value(), | 
|  | ElementsAre(first_expected, IsEmpty(), third_expected)); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorDecoding, ExtendedWithoutRequiredFields) { | 
|  | rtclog2::DependencyDescriptorsWireInfo encoded; | 
|  | encoded.set_start_end_bit_deltas(EncodeDeltas({}, {{kNone}})); | 
|  | encoded.set_template_id_deltas(EncodeDeltas({}, {{1}})); | 
|  | encoded.set_frame_id_deltas(EncodeDeltas({}, {{1}})); | 
|  | encoded.set_extended_infos(EncodeOptionalBlobs({{"Should not be here"}, {}})); | 
|  |  | 
|  | auto decoded = | 
|  | RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 2); | 
|  |  | 
|  | ASSERT_THAT(decoded.ok(), Eq(false)); | 
|  | } | 
|  |  | 
|  | TEST(RtcEventLogDependencyDescriptorDecoding, MissingRequiredField) { | 
|  | rtclog2::DependencyDescriptorsWireInfo encoded; | 
|  | encoded.set_start_end_bit(kBeginEnd); | 
|  | encoded.set_template_id(1u); | 
|  | encoded.set_frame_id(1u); | 
|  |  | 
|  | { | 
|  | rtclog2::DependencyDescriptorsWireInfo missing = encoded; | 
|  | missing.clear_start_end_bit(); | 
|  | EXPECT_THAT( | 
|  | RtcEventLogDependencyDescriptorEncoderDecoder::Decode(missing, 1).ok(), | 
|  | Eq(false)); | 
|  | } | 
|  |  | 
|  | { | 
|  | rtclog2::DependencyDescriptorsWireInfo missing = encoded; | 
|  | missing.clear_template_id(); | 
|  | EXPECT_THAT( | 
|  | RtcEventLogDependencyDescriptorEncoderDecoder::Decode(missing, 1).ok(), | 
|  | Eq(false)); | 
|  | } | 
|  |  | 
|  | { | 
|  | rtclog2::DependencyDescriptorsWireInfo missing = encoded; | 
|  | missing.clear_frame_id(); | 
|  | EXPECT_THAT( | 
|  | RtcEventLogDependencyDescriptorEncoderDecoder::Decode(missing, 1).ok(), | 
|  | Eq(false)); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace webrtc |