| /* Copyright (c) 2021 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/events/rtc_event_field_encoding.h" | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <limits> | |
| #include <optional> | |
| #include <span> | |
| #include <string> | |
| #include <tuple> | |
| #include <type_traits> | |
| #include <vector> | |
| #include "absl/strings/string_view.h" | |
| #include "api/rtc_event_log/rtc_event.h" | |
| #include "logging/rtc_event_log/encoder/var_int.h" | |
| #include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" | |
| #include "rtc_base/checks.h" | |
| #include "test/gtest.h" | |
| namespace webrtc { | |
| namespace { | |
| constexpr int32_t kInt32Max = std::numeric_limits<int32_t>::max(); | |
| constexpr int32_t kInt32Min = std::numeric_limits<int32_t>::min(); | |
| constexpr uint32_t kUint32Max = std::numeric_limits<uint32_t>::max(); | |
| constexpr int64_t kInt64Max = std::numeric_limits<int64_t>::max(); | |
| constexpr int64_t kInt64Min = std::numeric_limits<int64_t>::min(); | |
| constexpr uint64_t kUint64Max = std::numeric_limits<uint64_t>::max(); | |
| template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> | |
| size_t ExpectedVarIntSize(T value) { | |
| size_t bytes = 0; | |
| uint64_t x = EncodeAsUnsigned(value); | |
| do { | |
| ++bytes; | |
| x = x >> 7; | |
| } while (x > 0); | |
| return bytes; | |
| } | |
| template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> | |
| size_t ExpectedBaseValueSize(const FieldParameters& params, T value) { | |
| switch (params.field_type) { | |
| case FieldType::kFixed8: | |
| return 1; | |
| case FieldType::kFixed32: | |
| return 4; | |
| case FieldType::kFixed64: | |
| return 8; | |
| case FieldType::kVarInt: | |
| return ExpectedVarIntSize(value); | |
| default: | |
| break; | |
| } | |
| RTC_DCHECK_NOTREACHED(); | |
| return 0; | |
| } | |
| template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> | |
| size_t ExpectedEncodingSize(const FieldParameters& params, | |
| const std::vector<T>& v, | |
| size_t expected_bits_per_delta) { | |
| if (v.size() == 0) | |
| return 0; | |
| uint64_t numeric_field_type = static_cast<uint64_t>(params.field_type); | |
| RTC_DCHECK_LT(numeric_field_type, 1u << 3); | |
| size_t tag_size = | |
| ExpectedVarIntSize((params.field_id << 3) + numeric_field_type); | |
| T base = v[0]; | |
| size_t base_size = ExpectedBaseValueSize(params, base); | |
| if (v.size() == 1) | |
| return tag_size + base_size; | |
| size_t delta_header_size = 1; | |
| // Check if there is an element *not* equal to base. | |
| if (std::all_of(v.begin(), v.end(), [base](T x) { return x == base; })) { | |
| return tag_size + base_size + delta_header_size; | |
| } | |
| size_t delta_size = ((v.size() - 1) * expected_bits_per_delta + 7) / 8; | |
| return tag_size + base_size + delta_header_size + delta_size; | |
| } | |
| template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> | |
| size_t ExpectedEncodingSize(const FieldParameters& params, | |
| const std::vector<std::optional<T>>& v, | |
| size_t expected_bits_per_delta) { | |
| size_t num_existing_values = | |
| v.size() - std::count(v.begin(), v.end(), std::nullopt); | |
| auto first_existing_value = std::find_if( | |
| v.begin(), v.end(), [](std::optional<T> x) { return x.has_value(); }); | |
| if (num_existing_values == 0) | |
| return 0; | |
| uint64_t numeric_field_type = static_cast<uint64_t>(params.field_type); | |
| RTC_DCHECK_LT(numeric_field_type, 1u << 3); | |
| size_t tag_size = | |
| ExpectedVarIntSize((params.field_id << 3) + numeric_field_type); | |
| T base = first_existing_value->value(); | |
| size_t base_size = ExpectedBaseValueSize(params, base); | |
| if (num_existing_values == 1 && v.size() == 1) | |
| return tag_size + base_size; | |
| size_t delta_header_size = (num_existing_values == v.size() ? 1 : 2); | |
| size_t positions_size = | |
| (num_existing_values == v.size() ? 0 : (v.size() + 7) / 8); | |
| // Check if there is an element *not* equal to base. | |
| if (std::all_of(v.begin(), v.end(), | |
| [base](std::optional<T> x) { return x == base; })) { | |
| return tag_size + base_size + delta_header_size + positions_size; | |
| } | |
| size_t delta_size = | |
| ((num_existing_values - 1) * expected_bits_per_delta + 7) / 8; | |
| return tag_size + base_size + delta_header_size + positions_size + delta_size; | |
| } | |
| size_t ExpectedStringEncodingSize(const FieldParameters& params, | |
| const std::vector<std::string>& values) { | |
| EXPECT_EQ(params.field_type, FieldType::kString); | |
| uint64_t numeric_field_type = static_cast<uint64_t>(params.field_type); | |
| RTC_DCHECK_LT(numeric_field_type, 1u << 3); | |
| size_t tag_size = | |
| ExpectedVarIntSize((params.field_id << 3) + numeric_field_type); | |
| size_t expected_size = tag_size; | |
| if (values.size() > 1) { | |
| // VarInt encoding header reserved for future use. Currently always 0. | |
| expected_size += 1; | |
| } | |
| for (const auto& s : values) { | |
| expected_size += ExpectedVarIntSize(s.size()); | |
| expected_size += s.size(); | |
| } | |
| return expected_size; | |
| } | |
| } // namespace | |
| class RtcTestEvent final : public RtcEvent { | |
| public: | |
| RtcTestEvent(bool b, | |
| int32_t signed32, | |
| uint32_t unsigned32, | |
| int64_t signed64, | |
| uint64_t unsigned64) | |
| : b_(b), | |
| signed32_(signed32), | |
| unsigned32_(unsigned32), | |
| signed64_(signed64), | |
| unsigned64_(unsigned64) {} | |
| RtcTestEvent(bool b, | |
| int32_t signed32, | |
| uint32_t unsigned32, | |
| int64_t signed64, | |
| uint64_t unsigned64, | |
| std::optional<int32_t> optional_signed32, | |
| std::optional<int64_t> optional_signed64, | |
| uint32_t wrapping21, | |
| absl::string_view string) | |
| : b_(b), | |
| signed32_(signed32), | |
| unsigned32_(unsigned32), | |
| signed64_(signed64), | |
| unsigned64_(unsigned64), | |
| optional_signed32_(optional_signed32), | |
| optional_signed64_(optional_signed64), | |
| wrapping21_(wrapping21), | |
| string_(string) {} | |
| ~RtcTestEvent() override = default; | |
| Type GetType() const override { return static_cast<Type>(4711); } | |
| bool IsConfigEvent() const override { return false; } | |
| static constexpr EventParameters event_params{ | |
| .name = "TestEvent", | |
| .id = static_cast<RtcEvent::Type>(4711)}; | |
| static constexpr FieldParameters timestamp_params{ | |
| .name = "timestamp_ms", | |
| .field_id = FieldParameters::kTimestampField, | |
| .field_type = FieldType::kVarInt, | |
| .value_width = 64}; | |
| static constexpr FieldParameters bool_params{.name = "b", | |
| .field_id = 2, | |
| .field_type = FieldType::kFixed8, | |
| .value_width = 1}; | |
| static constexpr FieldParameters signed32_params{ | |
| .name = "signed32", | |
| .field_id = 3, | |
| .field_type = FieldType::kVarInt, | |
| .value_width = 32}; | |
| static constexpr FieldParameters unsigned32_params{ | |
| .name = "unsigned32", | |
| .field_id = 4, | |
| .field_type = FieldType::kFixed32, | |
| .value_width = 32}; | |
| static constexpr FieldParameters signed64_params{ | |
| .name = "signed64", | |
| .field_id = 5, | |
| .field_type = FieldType::kFixed64, | |
| .value_width = 64}; | |
| static constexpr FieldParameters unsigned64_params{ | |
| .name = "unsigned64", | |
| .field_id = 6, | |
| .field_type = FieldType::kVarInt, | |
| .value_width = 64}; | |
| static constexpr FieldParameters optional32_params{ | |
| .name = "optional_signed32", | |
| .field_id = 7, | |
| .field_type = FieldType::kFixed32, | |
| .value_width = 32}; | |
| static constexpr FieldParameters optional64_params{ | |
| .name = "optional_signed64", | |
| .field_id = 8, | |
| .field_type = FieldType::kVarInt, | |
| .value_width = 64}; | |
| static constexpr FieldParameters wrapping21_params{ | |
| .name = "wrapping21", | |
| .field_id = 9, | |
| .field_type = FieldType::kFixed32, | |
| .value_width = 21}; | |
| static constexpr FieldParameters string_params{ | |
| .name = "string", | |
| .field_id = 10, | |
| .field_type = FieldType::kString, | |
| .value_width = 0}; | |
| static constexpr Type kType = static_cast<RtcEvent::Type>(4711); | |
| const bool b_; | |
| const int32_t signed32_; | |
| const uint32_t unsigned32_; | |
| const int64_t signed64_; | |
| const uint64_t unsigned64_; | |
| const std::optional<int32_t> optional_signed32_ = std::nullopt; | |
| const std::optional<int64_t> optional_signed64_ = std::nullopt; | |
| const uint32_t wrapping21_ = 0; | |
| const std::string string_; | |
| }; | |
| class RtcEventFieldTest : public ::testing::Test { | |
| protected: | |
| void SetUp() override {} | |
| void CreateFullEvents( | |
| const std::vector<bool>& bool_values, | |
| const std::vector<int32_t>& signed32_values, | |
| const std::vector<uint32_t>& unsigned32_values, | |
| const std::vector<int64_t>& signed64_values, | |
| const std::vector<uint64_t>& unsigned64_values, | |
| const std::vector<std::optional<int32_t>>& optional32_values, | |
| const std::vector<std::optional<int64_t>>& optional64_values, | |
| const std::vector<uint32_t>& wrapping21_values, | |
| const std::vector<std::string>& string_values) { | |
| size_t size = bool_values.size(); | |
| RTC_CHECK_EQ(signed32_values.size(), size); | |
| RTC_CHECK_EQ(unsigned32_values.size(), size); | |
| RTC_CHECK_EQ(signed64_values.size(), size); | |
| RTC_CHECK_EQ(unsigned64_values.size(), size); | |
| RTC_CHECK_EQ(optional32_values.size(), size); | |
| RTC_CHECK_EQ(optional64_values.size(), size); | |
| RTC_CHECK_EQ(wrapping21_values.size(), size); | |
| RTC_CHECK_EQ(string_values.size(), size); | |
| for (size_t i = 0; i < size; i++) { | |
| batch_.push_back(new RtcTestEvent( | |
| bool_values[i], signed32_values[i], unsigned32_values[i], | |
| signed64_values[i], unsigned64_values[i], optional32_values[i], | |
| optional64_values[i], wrapping21_values[i], string_values[i])); | |
| } | |
| } | |
| void PrintBytes(absl::string_view s) { | |
| for (auto c : s) { | |
| fprintf(stderr, "%d ", static_cast<uint8_t>(c)); | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| void ParseEventHeader(absl::string_view encoded_event) { | |
| uint64_t event_tag; | |
| bool success; | |
| std::tie(success, encoded_event) = DecodeVarInt(encoded_event, &event_tag); | |
| ASSERT_TRUE(success); | |
| uint64_t event_id = event_tag >> 1; | |
| ASSERT_EQ(event_id, static_cast<uint64_t>(RtcTestEvent::event_params.id)); | |
| bool batched = event_tag & 1u; | |
| ASSERT_EQ(batched, batch_.size() > 1u); | |
| uint64_t size; | |
| std::tie(success, encoded_event) = DecodeVarInt(encoded_event, &size); | |
| ASSERT_EQ(encoded_event.size(), size); | |
| ASSERT_TRUE(parser_.Initialize(encoded_event, batched).ok()); | |
| } | |
| void ParseAndVerifyTimestamps() { | |
| auto result = parser_.ParseNumericField(RtcTestEvent::timestamp_params); | |
| ASSERT_TRUE(result.ok()) << result.message().c_str(); | |
| ASSERT_EQ(result.value().size(), batch_.size()); | |
| for (size_t i = 0; i < batch_.size(); i++) { | |
| EXPECT_EQ(result.value()[i], | |
| static_cast<uint64_t>(batch_[i]->timestamp_ms())); | |
| } | |
| } | |
| void ParseAndVerifyStringField( | |
| const FieldParameters& params, | |
| const std::vector<std::string>& expected_values, | |
| size_t expected_skipped_bytes = 0) { | |
| size_t expected_size = ExpectedStringEncodingSize(params, expected_values) + | |
| expected_skipped_bytes; | |
| size_t size_before = parser_.RemainingBytes(); | |
| auto result = parser_.ParseStringField(params); | |
| ASSERT_TRUE(result.ok()) << result.message().c_str(); | |
| ASSERT_EQ(result.value().size(), expected_values.size()); | |
| for (size_t i = 0; i < expected_values.size(); i++) { | |
| EXPECT_EQ(result.value()[i], expected_values[i]); | |
| } | |
| size_t size_after = parser_.RemainingBytes(); | |
| EXPECT_EQ(size_before - size_after, expected_size) | |
| << " for field " << params.name; | |
| } | |
| template <typename T> | |
| void ParseAndVerifyField(const FieldParameters& params, | |
| const std::vector<T>& expected_values, | |
| size_t expected_bits_per_delta, | |
| size_t expected_skipped_bytes = 0) { | |
| size_t expected_size = | |
| ExpectedEncodingSize(params, expected_values, expected_bits_per_delta) + | |
| expected_skipped_bytes; | |
| size_t size_before = parser_.RemainingBytes(); | |
| auto result = parser_.ParseNumericField(params); | |
| ASSERT_TRUE(result.ok()) << result.message().c_str(); | |
| ASSERT_EQ(result.value().size(), expected_values.size()); | |
| for (size_t i = 0; i < expected_values.size(); i++) { | |
| EXPECT_EQ(DecodeFromUnsignedToType<T>(result.value()[i]), | |
| expected_values[i]); | |
| } | |
| size_t size_after = parser_.RemainingBytes(); | |
| EXPECT_EQ(size_before - size_after, expected_size) | |
| << " for field " << params.name; | |
| } | |
| template <typename T> | |
| void ParseAndVerifyOptionalField( | |
| const FieldParameters& params, | |
| const std::vector<std::optional<T>>& expected_values, | |
| size_t expected_bits_per_delta, | |
| size_t expected_skipped_bytes = 0) { | |
| size_t expected_size = | |
| ExpectedEncodingSize(params, expected_values, expected_bits_per_delta) + | |
| expected_skipped_bytes; | |
| size_t size_before = parser_.RemainingBytes(); | |
| auto result = parser_.ParseOptionalNumericField(params); | |
| ASSERT_TRUE(result.ok()) << result.message().c_str(); | |
| std::span<uint64_t> values = result.value().values; | |
| std::span<uint8_t> positions = result.value().positions; | |
| ASSERT_EQ(positions.size(), expected_values.size()); | |
| auto value_it = values.begin(); | |
| for (size_t i = 0; i < expected_values.size(); i++) { | |
| if (positions[i]) { | |
| ASSERT_NE(value_it, values.end()); | |
| ASSERT_TRUE(expected_values[i].has_value()); | |
| EXPECT_EQ(DecodeFromUnsignedToType<T>(*value_it), | |
| expected_values[i].value()); | |
| ++value_it; | |
| } else { | |
| EXPECT_EQ(std::nullopt, expected_values[i]); | |
| } | |
| } | |
| EXPECT_EQ(value_it, values.end()); | |
| size_t size_after = parser_.RemainingBytes(); | |
| EXPECT_EQ(size_before - size_after, expected_size); | |
| } | |
| void ParseAndVerifyMissingField(const FieldParameters& params) { | |
| auto result = parser_.ParseNumericField(params, /*required_field=*/false); | |
| ASSERT_TRUE(result.ok()) << result.message().c_str(); | |
| EXPECT_EQ(result.value().size(), 0u); | |
| } | |
| void ParseAndVerifyMissingOptionalField(const FieldParameters& params) { | |
| auto result = | |
| parser_.ParseOptionalNumericField(params, /*required_field=*/false); | |
| ASSERT_TRUE(result.ok()) << result.message().c_str(); | |
| std::span<uint64_t> values = result.value().values; | |
| std::span<uint8_t> positions = result.value().positions; | |
| EXPECT_EQ(positions.size(), 0u); | |
| EXPECT_EQ(values.size(), 0u); | |
| } | |
| void TearDown() override { | |
| for (const RtcEvent* event : batch_) { | |
| delete event; | |
| } | |
| } | |
| std::vector<const RtcEvent*> batch_; | |
| EventParser parser_; | |
| }; | |
| TEST_F(RtcEventFieldTest, EmptyList) { | |
| EventEncoder encoder(RtcTestEvent::event_params, batch_); | |
| encoder.EncodeField(RtcTestEvent::bool_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); | |
| std::string s = encoder.AsString(); | |
| EXPECT_TRUE(s.empty()); | |
| } | |
| TEST_F(RtcEventFieldTest, Singleton) { | |
| std::vector<bool> bool_values = {true}; | |
| std::vector<int32_t> signed32_values = {-2}; | |
| std::vector<uint32_t> unsigned32_values = {123456789}; | |
| std::vector<int64_t> signed64_values = {-9876543210}; | |
| std::vector<uint64_t> unsigned64_values = {9876543210}; | |
| std::vector<std::optional<int32_t>> optional32_values = {kInt32Min}; | |
| std::vector<std::optional<int64_t>> optional64_values = {kInt64Max}; | |
| std::vector<uint32_t> wrapping21_values = {(1 << 21) - 1}; | |
| std::vector<std::string> string_values = {"foo"}; | |
| CreateFullEvents(bool_values, signed32_values, unsigned32_values, | |
| signed64_values, unsigned64_values, optional32_values, | |
| optional64_values, wrapping21_values, string_values); | |
| EventEncoder encoder(RtcTestEvent::event_params, batch_); | |
| encoder.EncodeField(RtcTestEvent::bool_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); | |
| encoder.EncodeField(RtcTestEvent::signed32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::unsigned32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); | |
| encoder.EncodeField(RtcTestEvent::signed64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::unsigned64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::wrapping21_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); | |
| encoder.EncodeField(RtcTestEvent::string_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); | |
| std::string s = encoder.AsString(); | |
| // Optional debug printing | |
| // PrintBytes(s); | |
| ParseEventHeader(s); | |
| ParseAndVerifyTimestamps(); | |
| ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, | |
| optional32_values, /*no deltas*/ 0); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, | |
| optional64_values, /*no deltas*/ 0); | |
| ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); | |
| EXPECT_EQ(parser_.RemainingBytes(), 0u); | |
| } | |
| TEST_F(RtcEventFieldTest, EqualElements) { | |
| std::vector<bool> bool_values = {true, true, true, true}; | |
| std::vector<int32_t> signed32_values = {-2, -2, -2, -2}; | |
| std::vector<uint32_t> unsigned32_values = {123456789, 123456789, 123456789, | |
| 123456789}; | |
| std::vector<int64_t> signed64_values = {-9876543210, -9876543210, -9876543210, | |
| -9876543210}; | |
| std::vector<uint64_t> unsigned64_values = {9876543210, 9876543210, 9876543210, | |
| 9876543210}; | |
| std::vector<std::optional<int32_t>> optional32_values = { | |
| kInt32Min, kInt32Min, kInt32Min, kInt32Min}; | |
| std::vector<std::optional<int64_t>> optional64_values = { | |
| kInt64Max, kInt64Max, kInt64Max, kInt64Max}; | |
| std::vector<uint32_t> wrapping21_values = {(1 << 21) - 1, (1 << 21) - 1, | |
| (1 << 21) - 1, (1 << 21) - 1}; | |
| std::vector<std::string> string_values = {"foo", "foo", "foo", "foo"}; | |
| CreateFullEvents(bool_values, signed32_values, unsigned32_values, | |
| signed64_values, unsigned64_values, optional32_values, | |
| optional64_values, wrapping21_values, string_values); | |
| EventEncoder encoder(RtcTestEvent::event_params, batch_); | |
| encoder.EncodeField(RtcTestEvent::bool_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); | |
| encoder.EncodeField(RtcTestEvent::signed32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::unsigned32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); | |
| encoder.EncodeField(RtcTestEvent::signed64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::unsigned64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::wrapping21_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); | |
| encoder.EncodeField(RtcTestEvent::string_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); | |
| std::string s = encoder.AsString(); | |
| // Optional debug printing | |
| // PrintBytes(s); | |
| ParseEventHeader(s); | |
| ParseAndVerifyTimestamps(); | |
| ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, | |
| optional32_values, /*no deltas*/ 0); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, | |
| optional64_values, /*no deltas*/ 0); | |
| ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, | |
| /*no deltas*/ 0); | |
| ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); | |
| EXPECT_EQ(parser_.RemainingBytes(), 0u); | |
| } | |
| TEST_F(RtcEventFieldTest, Increasing) { | |
| std::vector<bool> bool_values = {false, true, false, true}; | |
| std::vector<int32_t> signed32_values = {-2, -1, 0, 1}; | |
| std::vector<uint32_t> unsigned32_values = {kUint32Max - 1, kUint32Max, 0, 1}; | |
| std::vector<int64_t> signed64_values = {kInt64Max - 1, kInt64Max, kInt64Min, | |
| kInt64Min + 1}; | |
| std::vector<uint64_t> unsigned64_values = {kUint64Max - 1, kUint64Max, 0, 1}; | |
| std::vector<std::optional<int32_t>> optional32_values = { | |
| kInt32Max - 1, kInt32Max, kInt32Min, kInt32Min + 1}; | |
| std::vector<std::optional<int64_t>> optional64_values = { | |
| kInt64Max - 1, kInt64Max, kInt64Min, kInt64Min + 1}; | |
| std::vector<uint32_t> wrapping21_values = {(1 << 21) - 2, (1 << 21) - 1, 0, | |
| 1}; | |
| std::vector<std::string> string_values = { | |
| "", "a", "bc", "def"}; // No special compression of strings. | |
| CreateFullEvents(bool_values, signed32_values, unsigned32_values, | |
| signed64_values, unsigned64_values, optional32_values, | |
| optional64_values, wrapping21_values, string_values); | |
| EventEncoder encoder(RtcTestEvent::event_params, batch_); | |
| encoder.EncodeField(RtcTestEvent::bool_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); | |
| encoder.EncodeField(RtcTestEvent::signed32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::unsigned32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); | |
| encoder.EncodeField(RtcTestEvent::signed64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::unsigned64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::wrapping21_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); | |
| encoder.EncodeField(RtcTestEvent::string_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); | |
| std::string s = encoder.AsString(); | |
| // Optional debug printing | |
| // PrintBytes(s); | |
| ParseEventHeader(s); | |
| ParseAndVerifyTimestamps(); | |
| ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, | |
| optional32_values, /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, | |
| optional64_values, /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); | |
| EXPECT_EQ(parser_.RemainingBytes(), 0u); | |
| } | |
| TEST_F(RtcEventFieldTest, Decreasing) { | |
| std::vector<bool> bool_values = {true, false, true, false}; | |
| std::vector<int32_t> signed32_values = {2, 1, 0, -1}; | |
| std::vector<uint32_t> unsigned32_values = {1, 0, kUint32Max, kUint32Max - 1}; | |
| std::vector<int64_t> signed64_values = {kInt64Min + 1, kInt64Min, kInt64Max, | |
| kInt64Max - 1}; | |
| std::vector<uint64_t> unsigned64_values = {1, 0, kUint64Max, kUint64Max - 1}; | |
| std::vector<std::optional<int32_t>> optional32_values = { | |
| kInt32Min + 1, kInt32Min, kInt32Max, kInt32Max - 1}; | |
| std::vector<std::optional<int64_t>> optional64_values = { | |
| kInt64Min + 1, kInt64Min, kInt64Max, kInt64Max - 1}; | |
| std::vector<uint32_t> wrapping21_values = {1, 0, (1 << 21) - 1, | |
| (1 << 21) - 2}; | |
| std::vector<std::string> string_values = { | |
| "def", "bc", "a", ""}; // No special compression of strings. | |
| CreateFullEvents(bool_values, signed32_values, unsigned32_values, | |
| signed64_values, unsigned64_values, optional32_values, | |
| optional64_values, wrapping21_values, string_values); | |
| EventEncoder encoder(RtcTestEvent::event_params, batch_); | |
| encoder.EncodeField(RtcTestEvent::bool_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); | |
| encoder.EncodeField(RtcTestEvent::signed32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::unsigned32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); | |
| encoder.EncodeField(RtcTestEvent::signed64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::unsigned64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::wrapping21_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); | |
| encoder.EncodeField(RtcTestEvent::string_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); | |
| std::string s = encoder.AsString(); | |
| // Optional debug printing | |
| // PrintBytes(s); | |
| ParseEventHeader(s); | |
| ParseAndVerifyTimestamps(); | |
| ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, | |
| optional32_values, /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, | |
| optional64_values, /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, | |
| /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); | |
| EXPECT_EQ(parser_.RemainingBytes(), 0u); | |
| } | |
| TEST_F(RtcEventFieldTest, SkipsDeprecatedFields) { | |
| // Expect parser to skip fields it doesn't recognize, but find subsequent | |
| // fields. | |
| std::vector<bool> bool_values = {true, false}; | |
| std::vector<int32_t> signed32_values = {kInt32Min / 2, kInt32Max / 2}; | |
| std::vector<uint32_t> unsigned32_values = {0, kUint32Max / 2}; | |
| std::vector<int64_t> signed64_values = {kInt64Min / 2, kInt64Max / 2}; | |
| std::vector<uint64_t> unsigned64_values = {0, kUint64Max / 2}; | |
| std::vector<std::optional<int32_t>> optional32_values = {kInt32Max / 2, | |
| kInt32Min / 2}; | |
| std::vector<std::optional<int64_t>> optional64_values = {kInt64Min / 2, | |
| kInt64Max / 2}; | |
| std::vector<uint32_t> wrapping21_values = {0, 1 << 20}; | |
| std::vector<std::string> string_values = {"foo", "bar"}; | |
| size_t signed32_encoding_size = | |
| /*tag*/ 1 + /* varint base*/ 5 + /* delta_header*/ 1 + /*deltas*/ 4; | |
| size_t signed64_encoding_size = | |
| /*tag*/ 1 + /* fixed64 base*/ 8 + /* delta_header*/ 1 + /*deltas*/ 8; | |
| size_t optional32_encoding_size = | |
| /*tag*/ 1 + /* fixed32 base*/ 4 + /* delta_header*/ 1 + /*deltas*/ 4; | |
| CreateFullEvents(bool_values, signed32_values, unsigned32_values, | |
| signed64_values, unsigned64_values, optional32_values, | |
| optional64_values, wrapping21_values, string_values); | |
| EventEncoder encoder(RtcTestEvent::event_params, batch_); | |
| encoder.EncodeField(RtcTestEvent::bool_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); | |
| encoder.EncodeField(RtcTestEvent::signed32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::unsigned32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); | |
| encoder.EncodeField(RtcTestEvent::signed64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::unsigned64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::wrapping21_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); | |
| encoder.EncodeField(RtcTestEvent::string_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); | |
| std::string s = encoder.AsString(); | |
| // Optional debug printing | |
| // PrintBytes(s); | |
| ParseEventHeader(s); | |
| ParseAndVerifyTimestamps(); | |
| ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, | |
| /*expected_bits_per_delta=*/1); | |
| // Skips parsing the `signed32_values`. The following unsigned fields should | |
| // still be found. | |
| ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, | |
| /*expected_bits_per_delta=*/31, | |
| /*expected_skipped_bytes=*/signed32_encoding_size); | |
| // Skips parsing the `signed64_values`. The following unsigned fields should | |
| // still be found. | |
| ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, | |
| /*expected_bits_per_delta=*/63, signed64_encoding_size); | |
| // Skips parsing the `optional32_values`. The following unsigned fields should | |
| // still be found. | |
| ParseAndVerifyOptionalField( | |
| RtcTestEvent::optional64_params, optional64_values, | |
| /*expected_bits_per_delta=*/63, optional32_encoding_size); | |
| ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, | |
| /*expected_bits_per_delta=*/20); | |
| ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); | |
| EXPECT_EQ(parser_.RemainingBytes(), 0u); | |
| } | |
| TEST_F(RtcEventFieldTest, SkipsMissingFields) { | |
| // Expect parsing of missing field to succeed but return an empty list. | |
| std::vector<bool> bool_values = {true, false}; | |
| std::vector<int32_t> signed32_values = {kInt32Min / 2, kInt32Max / 2}; | |
| std::vector<uint32_t> unsigned32_values = {0, kUint32Max / 2}; | |
| std::vector<int64_t> signed64_values = {kInt64Min / 2, kInt64Max / 2}; | |
| std::vector<uint64_t> unsigned64_values = {0, kUint64Max / 2}; | |
| std::vector<std::optional<int32_t>> optional32_values = {kInt32Max / 2, | |
| kInt32Min / 2}; | |
| std::vector<std::optional<int64_t>> optional64_values = {kInt64Min / 2, | |
| kInt64Max / 2}; | |
| std::vector<uint32_t> wrapping21_values = {0, 1 << 20}; | |
| std::vector<std::string> string_values = {"foo", "foo"}; | |
| CreateFullEvents(bool_values, signed32_values, unsigned32_values, | |
| signed64_values, unsigned64_values, optional32_values, | |
| optional64_values, wrapping21_values, string_values); | |
| EventEncoder encoder(RtcTestEvent::event_params, batch_); | |
| // Skip encoding the `bool_values`. | |
| encoder.EncodeField(RtcTestEvent::signed32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); | |
| // Skip encoding the `unsigned32_values`. | |
| encoder.EncodeField(RtcTestEvent::signed64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); | |
| // Skip encoding the `unsigned64_values`. | |
| encoder.EncodeField( | |
| RtcTestEvent::optional32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); | |
| // Skip encoding the `optional64_values`. | |
| encoder.EncodeField( | |
| RtcTestEvent::wrapping21_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); | |
| encoder.EncodeField(RtcTestEvent::string_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); | |
| std::string s = encoder.AsString(); | |
| // Optional debug printing | |
| // PrintBytes(s); | |
| ParseEventHeader(s); | |
| ParseAndVerifyTimestamps(); | |
| ParseAndVerifyMissingField(RtcTestEvent::bool_params); | |
| ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, | |
| /*expected_bits_per_delta=*/31); | |
| ParseAndVerifyMissingField(RtcTestEvent::unsigned32_params); | |
| ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, | |
| /*expected_bits_per_delta=*/63); | |
| ParseAndVerifyMissingField(RtcTestEvent::unsigned64_params); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, | |
| optional32_values, | |
| /*expected_bits_per_delta=*/31); | |
| ParseAndVerifyMissingOptionalField(RtcTestEvent::optional64_params); | |
| ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, | |
| /*expected_bits_per_delta=*/20); | |
| ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); | |
| EXPECT_EQ(parser_.RemainingBytes(), 0u); | |
| } | |
| TEST_F(RtcEventFieldTest, OptionalFields) { | |
| std::vector<std::optional<int32_t>> optional32_values = { | |
| 2, std::nullopt, 4, std::nullopt, 6, std::nullopt}; | |
| std::vector<std::optional<int64_t>> optional64_values = { | |
| std::nullopt, 1024, std::nullopt, 1025, std::nullopt, 1026}; | |
| std::vector<uint32_t> wrapping21_values = {(1 << 21) - 3, 0, 2, 5, 5, 6}; | |
| for (size_t i = 0; i < optional32_values.size(); i++) { | |
| batch_.push_back(new RtcTestEvent(0, 0, 0, 0, 0, optional32_values[i], | |
| optional64_values[i], | |
| wrapping21_values[i], "")); | |
| } | |
| EventEncoder encoder(RtcTestEvent::event_params, batch_); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::wrapping21_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); | |
| std::string s = encoder.AsString(); | |
| // Optional debug output | |
| // PrintBytes(s); | |
| ParseEventHeader(s); | |
| ParseAndVerifyTimestamps(); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, | |
| optional32_values, /*expected_bits_per_delta=*/2); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, | |
| optional64_values, /*expected_bits_per_delta=*/1); | |
| ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, | |
| /*expected_bits_per_delta=*/2); | |
| EXPECT_EQ(parser_.RemainingBytes(), 0u); | |
| } | |
| TEST_F(RtcEventFieldTest, AllNulloptTreatedAsMissing) { | |
| std::vector<std::optional<int32_t>> optional32_values = { | |
| std::nullopt, std::nullopt, std::nullopt, | |
| std::nullopt, std::nullopt, std::nullopt}; | |
| std::vector<std::optional<int64_t>> optional64_values = { | |
| std::nullopt, 1024, std::nullopt, 1025, std::nullopt, 1026}; | |
| for (size_t i = 0; i < optional32_values.size(); i++) { | |
| batch_.push_back(new RtcTestEvent(0, 0, 0, 0, 0, optional32_values[i], | |
| optional64_values[i], 0, "")); | |
| } | |
| EventEncoder encoder(RtcTestEvent::event_params, batch_); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional32_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); | |
| encoder.EncodeField( | |
| RtcTestEvent::optional64_params, | |
| ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); | |
| std::string s = encoder.AsString(); | |
| // Optional debug output | |
| // PrintBytes(s); | |
| ParseEventHeader(s); | |
| ParseAndVerifyTimestamps(); | |
| ParseAndVerifyMissingOptionalField(RtcTestEvent::optional32_params); | |
| ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, | |
| optional64_values, /*expected_bits_per_delta=*/1); | |
| EXPECT_EQ(parser_.RemainingBytes(), 0u); | |
| } | |
| } // namespace webrtc |