blob: 7e7bccd126dea87bbc9bb4f4daa11a85eb74d5e5 [file] [log] [blame]
/*
* 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/encoder/optional_blob_encoding.h"
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include "test/gmock.h"
#include "test/gtest.h"
using ::testing::ElementsAre;
using ::testing::IsEmpty;
namespace webrtc {
namespace {
class BitBuilder {
public:
BitBuilder& Bit(uint8_t bit) {
if (total_bits_ % 8 == 0) {
bits_.push_back(0);
}
bits_[total_bits_ / 8] |= bit << (7 - (total_bits_ % 8));
++total_bits_;
return *this;
}
BitBuilder& Bytes(const std::vector<uint8_t>& bytes) {
for (uint8_t byte : bytes) {
for (int i = 1; i <= 8; ++i) {
uint8_t bit = (byte >> (8 - i)) & 1;
Bit(bit);
}
}
return *this;
}
BitBuilder& ByteAlign() {
while (total_bits_ % 8 > 0) {
Bit(0);
}
return *this;
}
std::string AsString() { return std::string(bits_.begin(), bits_.end()); }
private:
std::vector<uint8_t> bits_;
uint64_t total_bits_ = 0;
};
TEST(OptionalBlobEncoding, AllBlobsPresent) {
std::string encoded = EncodeOptionalBlobs({"a", "b", "c"});
std::string expected = BitBuilder()
.Bit(1)
.ByteAlign()
.Bytes({0x01, 'a'})
.Bytes({0x01, 'b'})
.Bytes({0x01, 'c'})
.AsString();
EXPECT_EQ(encoded, expected);
}
TEST(OptionalBlobEncoding, SomeBlobsPresent) {
std::string encoded = EncodeOptionalBlobs({"a", std::nullopt, "c"});
std::string expected = BitBuilder()
.Bit(0)
.Bit(1)
.Bit(0)
.Bit(1)
.ByteAlign()
.Bytes({0x01, 'a'})
.Bytes({0x01, 'c'})
.AsString();
EXPECT_EQ(encoded, expected);
}
TEST(OptionalBlobEncoding, NoBlobsPresent) {
std::string encoded =
EncodeOptionalBlobs({std::nullopt, std::nullopt, std::nullopt});
EXPECT_THAT(encoded, IsEmpty());
}
TEST(OptionalBlobEncoding, EmptyBlobsPresent) {
std::string encoded = EncodeOptionalBlobs({std::nullopt, "", std::nullopt});
std::string expected = BitBuilder()
.Bit(0)
.Bit(0)
.Bit(1)
.Bit(0)
.ByteAlign()
.Bytes({0x0})
.AsString();
EXPECT_EQ(encoded, expected);
}
TEST(OptionalBlobEncoding, ZeroBlobs) {
std::string encoded = EncodeOptionalBlobs({});
EXPECT_EQ(encoded, std::string());
}
TEST(OptionalBlobEncoding, LongBlobs) {
std::string medium_string(100, 'a');
std::string long_string(200, 'b');
std::string encoded = EncodeOptionalBlobs({medium_string, long_string});
std::string expected =
BitBuilder()
.Bit(1)
.ByteAlign()
.Bytes({0x64})
.Bytes({medium_string.begin(), medium_string.end()})
.Bytes({0xC8, 0x01})
.Bytes({long_string.begin(), long_string.end()})
.AsString();
EXPECT_EQ(encoded, expected);
}
TEST(OptionalBlobDecoding, AllBlobsPresent) {
std::string encoded = BitBuilder()
.Bit(1)
.ByteAlign()
.Bytes({0x01, 'a'})
.Bytes({0x01, 'b'})
.Bytes({0x01, 'c'})
.AsString();
auto decoded = DecodeOptionalBlobs(encoded, 3);
EXPECT_THAT(decoded, ElementsAre("a", "b", "c"));
}
TEST(OptionalBlobDecoding, SomeBlobsPresent) {
std::string encoded = BitBuilder()
.Bit(0)
.Bit(1)
.Bit(0)
.Bit(1)
.ByteAlign()
.Bytes({0x01, 'a'})
.Bytes({0x01, 'c'})
.AsString();
auto decoded = DecodeOptionalBlobs(encoded, 3);
EXPECT_THAT(decoded, ElementsAre("a", std::nullopt, "c"));
}
TEST(OptionalBlobDecoding, NoBlobsPresent) {
auto decoded = DecodeOptionalBlobs("", 3);
EXPECT_THAT(decoded, ElementsAre(std::nullopt, std::nullopt, std::nullopt));
}
TEST(OptionalBlobDecoding, EmptyBlobsPresent) {
std::string encoded = BitBuilder()
.Bit(0)
.Bit(0)
.Bit(1)
.Bit(0)
.ByteAlign()
.Bytes({0x0})
.AsString();
auto decoded = DecodeOptionalBlobs(encoded, 3);
EXPECT_THAT(decoded, ElementsAre(std::nullopt, "", std::nullopt));
}
TEST(OptionalBlobDecoding, ZeroBlobs) {
std::string encoded;
auto decoded = DecodeOptionalBlobs(encoded, 0);
EXPECT_THAT(decoded, IsEmpty());
}
TEST(OptionalBlobDecoding, LongBlobs) {
std::string medium_string(100, 'a');
std::string long_string(200, 'b');
std::string encoded = BitBuilder()
.Bit(1)
.ByteAlign()
.Bytes({0x64})
.Bytes({medium_string.begin(), medium_string.end()})
.Bytes({0xC8, 0x01})
.Bytes({long_string.begin(), long_string.end()})
.AsString();
auto decoded = DecodeOptionalBlobs(encoded, 2);
EXPECT_THAT(decoded, ElementsAre(medium_string, long_string));
}
TEST(OptionalBlobDecoding, TooShortEncodedBlobLength) {
std::string encoded =
BitBuilder().Bit(1).ByteAlign().Bytes({0x01, 'a', 'b'}).AsString();
auto decoded = DecodeOptionalBlobs(encoded, 1);
EXPECT_THAT(decoded, IsEmpty());
}
TEST(OptionalBlobDecoding, TooLongEncodedBlobLength) {
std::string encoded =
BitBuilder().Bit(1).ByteAlign().Bytes({0x03, 'a', 'b'}).AsString();
auto decoded = DecodeOptionalBlobs(encoded, 1);
EXPECT_THAT(decoded, IsEmpty());
}
TEST(OptionalBlobDecoding, TooLongEncodedBufferLength) {
std::string encoded = BitBuilder().Bytes({0x00, 0x00, 0x00}).AsString();
auto decoded = DecodeOptionalBlobs(encoded, 8);
EXPECT_THAT(decoded, IsEmpty());
}
} // namespace
} // namespace webrtc