tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

delta_encoding_unittest.cc (23942B)


      1 /*
      2 *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "logging/rtc_event_log/encoder/delta_encoding.h"
     12 
     13 #include <algorithm>
     14 #include <cstddef>
     15 #include <cstdint>
     16 #include <cstring>
     17 #include <limits>
     18 #include <numeric>
     19 #include <optional>
     20 #include <string>
     21 #include <tuple>
     22 #include <vector>
     23 
     24 #include "rtc_base/checks.h"
     25 #include "rtc_base/random.h"
     26 #include "test/gmock.h"
     27 #include "test/gtest.h"
     28 
     29 namespace webrtc {
     30 
     31 void SetFixedLengthEncoderDeltaSignednessForTesting(bool signedness);
     32 void UnsetFixedLengthEncoderDeltaSignednessForTesting();
     33 
     34 namespace {
     35 
     36 using ::testing::Each;
     37 using ::testing::Eq;
     38 
     39 enum class DeltaSignedness { kNoOverride, kForceUnsigned, kForceSigned };
     40 
     41 void MaybeSetSignedness(DeltaSignedness signedness) {
     42  switch (signedness) {
     43    case DeltaSignedness::kNoOverride:
     44      UnsetFixedLengthEncoderDeltaSignednessForTesting();
     45      return;
     46    case DeltaSignedness::kForceUnsigned:
     47      SetFixedLengthEncoderDeltaSignednessForTesting(false);
     48      return;
     49    case DeltaSignedness::kForceSigned:
     50      SetFixedLengthEncoderDeltaSignednessForTesting(true);
     51      return;
     52  }
     53  RTC_DCHECK_NOTREACHED();
     54 }
     55 
     56 uint64_t RandomWithMaxBitWidth(Random* prng, uint64_t max_width) {
     57  RTC_DCHECK_GE(max_width, 1u);
     58  RTC_DCHECK_LE(max_width, 64u);
     59 
     60  const uint64_t low = prng->Rand(std::numeric_limits<uint32_t>::max());
     61  const uint64_t high =
     62      max_width > 32u ? prng->Rand(std::numeric_limits<uint32_t>::max()) : 0u;
     63 
     64  const uint64_t random_before_mask = (high << 32) | low;
     65 
     66  if (max_width < 64) {
     67    return random_before_mask & ((static_cast<uint64_t>(1) << max_width) - 1);
     68  } else {
     69    return random_before_mask;
     70  }
     71 }
     72 
     73 // Encodes `values` based on `base`, then decodes the result and makes sure
     74 // that it is equal to the original input.
     75 // If `encoded_string` is non-null, the encoded result will also be written
     76 // into it.
     77 void TestEncodingAndDecoding(std::optional<uint64_t> base,
     78                             const std::vector<std::optional<uint64_t>>& values,
     79                             std::string* encoded_string = nullptr) {
     80  const std::string encoded = EncodeDeltas(base, values);
     81  if (encoded_string) {
     82    *encoded_string = encoded;
     83  }
     84 
     85  const std::vector<std::optional<uint64_t>> decoded =
     86      DecodeDeltas(encoded, base, values.size());
     87 
     88  EXPECT_EQ(decoded, values);
     89 }
     90 
     91 std::vector<std::optional<uint64_t>> CreateSequenceByFirstValue(
     92    uint64_t first,
     93    size_t sequence_length) {
     94  std::vector<std::optional<uint64_t>> sequence(sequence_length);
     95  std::iota(sequence.begin(), sequence.end(), first);
     96  return sequence;
     97 }
     98 
     99 std::vector<std::optional<uint64_t>> CreateSequenceByLastValue(
    100    uint64_t last,
    101    size_t num_values) {
    102  const uint64_t first = last - num_values + 1;
    103  std::vector<std::optional<uint64_t>> result(num_values);
    104  std::iota(result.begin(), result.end(), first);
    105  return result;
    106 }
    107 
    108 // If `sequence_length` is greater than the number of deltas, the sequence of
    109 // deltas will wrap around.
    110 std::vector<std::optional<uint64_t>> CreateSequenceByOptionalDeltas(
    111    uint64_t first,
    112    const std::vector<std::optional<uint64_t>>& deltas,
    113    size_t sequence_length) {
    114  RTC_DCHECK_GE(sequence_length, 1);
    115 
    116  std::vector<std::optional<uint64_t>> sequence(sequence_length);
    117 
    118  uint64_t previous = first;
    119  for (size_t i = 0, next_delta_index = 0; i < sequence.size(); ++i) {
    120    if (deltas[next_delta_index].has_value()) {
    121      sequence[i] =
    122          std::optional<uint64_t>(previous + deltas[next_delta_index].value());
    123      previous = sequence[i].value();
    124    }
    125    next_delta_index = (next_delta_index + 1) % deltas.size();
    126  }
    127 
    128  return sequence;
    129 }
    130 
    131 size_t EncodingLengthUpperBound(size_t delta_max_bit_width,
    132                                size_t num_of_deltas,
    133                                DeltaSignedness signedness_override) {
    134  std::optional<size_t> smallest_header_size_bytes;
    135  switch (signedness_override) {
    136    case DeltaSignedness::kNoOverride:
    137    case DeltaSignedness::kForceUnsigned:
    138      smallest_header_size_bytes = 1;
    139      break;
    140    case DeltaSignedness::kForceSigned:
    141      smallest_header_size_bytes = 2;
    142      break;
    143  }
    144  RTC_DCHECK(smallest_header_size_bytes);
    145 
    146  return delta_max_bit_width * num_of_deltas + *smallest_header_size_bytes;
    147 }
    148 
    149 // If `sequence_length` is greater than the number of deltas, the sequence of
    150 // deltas will wrap around.
    151 std::vector<std::optional<uint64_t>> CreateSequenceByDeltas(
    152    uint64_t first,
    153    const std::vector<uint64_t>& deltas,
    154    size_t sequence_length) {
    155  RTC_DCHECK(!deltas.empty());
    156  std::vector<std::optional<uint64_t>> optional_deltas(deltas.size());
    157  for (size_t i = 0; i < deltas.size(); ++i) {
    158    optional_deltas[i] = std::optional<uint64_t>(deltas[i]);
    159  }
    160  return CreateSequenceByOptionalDeltas(first, optional_deltas,
    161                                        sequence_length);
    162 }
    163 
    164 // Tests of the delta encoding, parameterized by the number of values
    165 // in the sequence created by the test.
    166 class DeltaEncodingTest
    167    : public ::testing::TestWithParam<
    168          std::tuple<DeltaSignedness, size_t, bool, uint64_t>> {
    169 public:
    170  DeltaEncodingTest()
    171      : signedness_(std::get<0>(GetParam())),
    172        num_of_values_(std::get<1>(GetParam())),
    173        optional_values_(std::get<2>(GetParam())),
    174        partial_random_seed_(std::get<3>(GetParam())) {
    175    MaybeSetSignedness(signedness_);
    176  }
    177 
    178  ~DeltaEncodingTest() override = default;
    179 
    180  // Running with the same seed for all variants would make all tests start
    181  // with the same sequence; avoid this by making the seed different.
    182  uint64_t Seed() const {
    183    // Multiply everything but by different primes to produce unique results.
    184    return 2 * static_cast<uint64_t>(signedness_) + 3 * num_of_values_ +
    185           5 * optional_values_ + 7 * partial_random_seed_;
    186  }
    187 
    188  const DeltaSignedness signedness_;
    189  const uint64_t num_of_values_;
    190  const bool optional_values_;
    191  const uint64_t partial_random_seed_;  // Explained where it's used.
    192 };
    193 
    194 TEST_P(DeltaEncodingTest, AllValuesEqualToExistentBaseValue) {
    195  const std::optional<uint64_t> base(3432);
    196  std::vector<std::optional<uint64_t>> values(num_of_values_);
    197  std::fill(values.begin(), values.end(), base);
    198  std::string encoded;
    199  TestEncodingAndDecoding(base, values, &encoded);
    200 
    201  // Additional requirement - the encoding should be efficient in this
    202  // case - the empty string will be used.
    203  EXPECT_TRUE(encoded.empty());
    204 }
    205 
    206 TEST_P(DeltaEncodingTest, AllValuesEqualToNonExistentBaseValue) {
    207  if (!optional_values_) {
    208    return;  // Test irrelevant for this case.
    209  }
    210 
    211  const std::optional<uint64_t> base;
    212  std::vector<std::optional<uint64_t>> values(num_of_values_);
    213  std::fill(values.begin(), values.end(), base);
    214  std::string encoded;
    215  TestEncodingAndDecoding(base, values, &encoded);
    216 
    217  // Additional requirement - the encoding should be efficient in this
    218  // case - the empty string will be used.
    219  EXPECT_TRUE(encoded.empty());
    220 }
    221 
    222 TEST_P(DeltaEncodingTest, BaseNonExistentButSomeOtherValuesExist) {
    223  if (!optional_values_) {
    224    return;  // Test irrelevant for this case.
    225  }
    226 
    227  const std::optional<uint64_t> base;
    228  std::vector<std::optional<uint64_t>> values(num_of_values_);
    229 
    230  Random prng(Seed());
    231 
    232  const uint64_t max_bit_width = 1 + prng.Rand(63);  // [1, 64]
    233 
    234  for (size_t i = 0; i < values.size();) {
    235    // Leave a random number of values as non-existent.
    236    const size_t non_existent_count = prng.Rand(values.size() - i - 1);
    237    i += non_existent_count;
    238 
    239    // Assign random values to a random number of values. (At least one, to
    240    // prevent this iteration of the outer loop from being a no-op.)
    241    const size_t existent_count =
    242        std::max<size_t>(prng.Rand(values.size() - i - 1), 1);
    243    for (size_t j = 0; j < existent_count; ++j) {
    244      values[i + j] = RandomWithMaxBitWidth(&prng, max_bit_width);
    245    }
    246    i += existent_count;
    247  }
    248 
    249  TestEncodingAndDecoding(base, values);
    250 }
    251 
    252 TEST_P(DeltaEncodingTest, MinDeltaNoWrapAround) {
    253  const std::optional<uint64_t> base(3432);
    254 
    255  auto values = CreateSequenceByFirstValue(base.value() + 1, num_of_values_);
    256  ASSERT_GT(values[values.size() - 1], base) << "Sanity; must not wrap around";
    257 
    258  if (optional_values_) {
    259    // Arbitrarily make one of the values non-existent, to force
    260    // optional-supporting encoding.
    261    values[0] = std::optional<uint64_t>();
    262  }
    263 
    264  TestEncodingAndDecoding(base, values);
    265 }
    266 
    267 TEST_P(DeltaEncodingTest, BigDeltaNoWrapAround) {
    268  const uint64_t kBigDelta = 132828;
    269  const std::optional<uint64_t> base(3432);
    270 
    271  auto values =
    272      CreateSequenceByFirstValue(base.value() + kBigDelta, num_of_values_);
    273  ASSERT_GT(values[values.size() - 1], base) << "Sanity; must not wrap around";
    274 
    275  if (optional_values_) {
    276    // Arbitrarily make one of the values non-existent, to force
    277    // optional-supporting encoding.
    278    values[0] = std::optional<uint64_t>();
    279  }
    280 
    281  TestEncodingAndDecoding(base, values);
    282 }
    283 
    284 TEST_P(DeltaEncodingTest, MaxDeltaNoWrapAround) {
    285  const std::optional<uint64_t> base(3432);
    286 
    287  auto values = CreateSequenceByLastValue(std::numeric_limits<uint64_t>::max(),
    288                                          num_of_values_);
    289  ASSERT_GT(values[values.size() - 1], base) << "Sanity; must not wrap around";
    290 
    291  if (optional_values_) {
    292    // Arbitrarily make one of the values non-existent, to force
    293    // optional-supporting encoding.
    294    values[0] = std::optional<uint64_t>();
    295  }
    296 
    297  TestEncodingAndDecoding(base, values);
    298 }
    299 
    300 TEST_P(DeltaEncodingTest, SmallDeltaWithWrapAroundComparedToBase) {
    301  if (optional_values_ && num_of_values_ == 1) {
    302    return;  // Inapplicable
    303  }
    304 
    305  const std::optional<uint64_t> base(std::numeric_limits<uint64_t>::max());
    306 
    307  auto values = CreateSequenceByDeltas(*base, {1, 10, 3}, num_of_values_);
    308  ASSERT_LT(values[0], base) << "Sanity; must wrap around";
    309 
    310  if (optional_values_) {
    311    // Arbitrarily make one of the values non-existent, to force
    312    // optional-supporting encoding.
    313    values[1] = std::optional<uint64_t>();
    314  }
    315 
    316  TestEncodingAndDecoding(base, values);
    317 }
    318 
    319 TEST_P(DeltaEncodingTest, SmallDeltaWithWrapAroundInValueSequence) {
    320  if (num_of_values_ == 1 || (optional_values_ && num_of_values_ < 3)) {
    321    return;  // Inapplicable.
    322  }
    323 
    324  const std::optional<uint64_t> base(std::numeric_limits<uint64_t>::max() - 2);
    325 
    326  auto values = CreateSequenceByDeltas(*base, {1, 10, 3}, num_of_values_);
    327  ASSERT_LT(values[values.size() - 1], values[0]) << "Sanity; must wrap around";
    328 
    329  if (optional_values_) {
    330    // Arbitrarily make one of the values non-existent, to force
    331    // optional-supporting encoding.
    332    RTC_DCHECK_GT(values.size() - 1, 1u);  // Wrap around not cancelled.
    333    values[1] = std::optional<uint64_t>();
    334  }
    335 
    336  TestEncodingAndDecoding(base, values);
    337 }
    338 
    339 // Suppress "integral constant overflow" warning; this is the test's focus.
    340 #ifdef _MSC_VER
    341 #pragma warning(push)
    342 #pragma warning(disable : 4307)
    343 #endif
    344 TEST_P(DeltaEncodingTest, BigDeltaWithWrapAroundComparedToBase) {
    345  if (optional_values_ && num_of_values_ == 1) {
    346    return;  // Inapplicable
    347  }
    348 
    349  const uint64_t kBigDelta = 132828;
    350  const std::optional<uint64_t> base(std::numeric_limits<uint64_t>::max() -
    351                                     kBigDelta + 3);
    352 
    353  auto values =
    354      CreateSequenceByFirstValue(base.value() + kBigDelta, num_of_values_);
    355  ASSERT_LT(values[0], base.value()) << "Sanity; must wrap around";
    356 
    357  if (optional_values_) {
    358    // Arbitrarily make one of the values non-existent, to force
    359    // optional-supporting encoding.
    360    values[1] = std::optional<uint64_t>();
    361  }
    362 
    363  TestEncodingAndDecoding(base, values);
    364 }
    365 
    366 TEST_P(DeltaEncodingTest, BigDeltaWithWrapAroundInValueSequence) {
    367  if (num_of_values_ == 1 || (optional_values_ && num_of_values_ < 3)) {
    368    return;  // Inapplicable.
    369  }
    370 
    371  const uint64_t kBigDelta = 132828;
    372  const std::optional<uint64_t> base(std::numeric_limits<uint64_t>::max() -
    373                                     kBigDelta + 3);
    374 
    375  auto values = CreateSequenceByFirstValue(std::numeric_limits<uint64_t>::max(),
    376                                           num_of_values_);
    377  ASSERT_LT(values[values.size() - 1], values[0]) << "Sanity; must wrap around";
    378 
    379  if (optional_values_) {
    380    // Arbitrarily make one of the values non-existent, to force
    381    // optional-supporting encoding.
    382    RTC_DCHECK_GT(values.size() - 1, 1u);  // Wrap around not cancelled.
    383    values[1] = std::optional<uint64_t>();
    384  }
    385 
    386  TestEncodingAndDecoding(base, values);
    387 }
    388 #ifdef _MSC_VER
    389 #pragma warning(pop)
    390 #endif
    391 
    392 TEST_P(DeltaEncodingTest, MaxDeltaWithWrapAroundComparedToBase) {
    393  if (optional_values_ && num_of_values_ == 1) {
    394    return;  // Inapplicable
    395  }
    396 
    397  const std::optional<uint64_t> base(3432);
    398  auto values = CreateSequenceByFirstValue(*base - 1, num_of_values_);
    399 
    400  if (optional_values_) {
    401    // Arbitrarily make one of the values non-existent, to force
    402    // optional-supporting encoding.
    403    values[1] = std::optional<uint64_t>();
    404  }
    405 
    406  TestEncodingAndDecoding(base, values);
    407 }
    408 
    409 TEST_P(DeltaEncodingTest, MaxDeltaWithWrapAroundInValueSequence) {
    410  if (num_of_values_ == 1 || (optional_values_ && num_of_values_ < 3)) {
    411    return;  // Inapplicable.
    412  }
    413 
    414  const std::optional<uint64_t> base(3432);
    415 
    416  auto values = CreateSequenceByDeltas(
    417      *base, {0, std::numeric_limits<uint64_t>::max(), 3}, num_of_values_);
    418  // Wraps around continuously by virtue of being max(); will not ASSERT.
    419 
    420  if (optional_values_) {
    421    // Arbitrarily make one of the values non-existent, to force
    422    // optional-supporting encoding.
    423    RTC_DCHECK_GT(values.size() - 1, 1u);  // Wrap around not cancelled.
    424    values[1] = std::optional<uint64_t>();
    425  }
    426 
    427  TestEncodingAndDecoding(base, values);
    428 }
    429 
    430 // If num_of_values_ == 1, a zero delta will yield an empty string; that's
    431 // already covered by AllValuesEqualToExistentBaseValue, but it doesn't hurt to
    432 // test again. For all other cases, we have a new test.
    433 TEST_P(DeltaEncodingTest, ZeroDelta) {
    434  const std::optional<uint64_t> base(3432);
    435 
    436  // Arbitrary sequence of deltas with intentional zero deltas, as well as
    437  // consecutive zeros.
    438  const std::vector<uint64_t> deltas = {0,      312, 11, 1,  1, 0, 0, 12,
    439                                        400321, 3,   3,  12, 5, 0, 6};
    440  auto values = CreateSequenceByDeltas(base.value(), deltas, num_of_values_);
    441 
    442  if (optional_values_) {
    443    // Arbitrarily make one of the values non-existent, to force
    444    // optional-supporting encoding.
    445    values[0] = std::optional<uint64_t>();
    446  }
    447 
    448  TestEncodingAndDecoding(base, values);
    449 }
    450 
    451 INSTANTIATE_TEST_SUITE_P(
    452    SignednessOverrideAndNumberOfValuesInSequence,
    453    DeltaEncodingTest,
    454    ::testing::Combine(::testing::Values(DeltaSignedness::kNoOverride,
    455                                         DeltaSignedness::kForceUnsigned,
    456                                         DeltaSignedness::kForceSigned),
    457                       ::testing::Values(1, 2, 100, 10000),
    458                       ::testing::Bool(),
    459                       ::testing::Values(10, 20, 30)));
    460 
    461 // Tests over the quality of the compression (as opposed to its correctness).
    462 // Not to be confused with tests of runtime efficiency.
    463 class DeltaEncodingCompressionQualityTest
    464    : public ::testing::TestWithParam<
    465          std::tuple<DeltaSignedness, uint64_t, uint64_t, uint64_t>> {
    466 public:
    467  DeltaEncodingCompressionQualityTest()
    468      : signedness_(std::get<0>(GetParam())),
    469        delta_max_bit_width_(std::get<1>(GetParam())),
    470        num_of_values_(std::get<2>(GetParam())),
    471        partial_random_seed_(std::get<3>(GetParam())) {
    472    MaybeSetSignedness(signedness_);
    473  }
    474 
    475  ~DeltaEncodingCompressionQualityTest() override = default;
    476 
    477  // Running with the same seed for all variants would make all tests start
    478  // with the same sequence; avoid this by making the seed different.
    479  uint64_t Seed() const {
    480    // Multiply everything but by different primes to produce unique results.
    481    return 2 * static_cast<uint64_t>(signedness_) + 3 * delta_max_bit_width_ +
    482           5 * delta_max_bit_width_ + 7 * num_of_values_ +
    483           11 * partial_random_seed_;
    484  }
    485 
    486  const DeltaSignedness signedness_;
    487  const uint64_t delta_max_bit_width_;
    488  const uint64_t num_of_values_;
    489  const uint64_t partial_random_seed_;  // Explained where it's used.
    490 };
    491 
    492 // If no wrap-around occurs in the stream, the width of the values does not
    493 // matter to compression performance; only the deltas matter.
    494 TEST_P(DeltaEncodingCompressionQualityTest, BaseDoesNotAffectEfficiency) {
    495  // 1. Bases which will not produce a wrap-around.
    496  // 2. The last base - 0xffffffffffffffff - does cause a wrap-around, but
    497  //    that still works, because the width is 64 anyway, and does not
    498  //    need to be conveyed explicitly in the encoding header.
    499  const uint64_t bases[] = {0, 0x55, 0xffffffff,
    500                            std::numeric_limits<uint64_t>::max()};
    501 
    502  std::vector<uint64_t> deltas(num_of_values_);
    503 
    504  // Allows us to make sure that the deltas do not produce a wrap-around.
    505  uint64_t last_element[std::size(bases)];
    506  memcpy(last_element, bases, sizeof(bases));
    507 
    508  // Avoid empty `deltas` due to first element causing wrap-around.
    509  deltas[0] = 1;
    510  for (uint64_t& element : last_element) {
    511    ++element;
    512  }
    513 
    514  Random prng(Seed());
    515 
    516  for (size_t i = 1; i < deltas.size(); ++i) {
    517    const uint64_t delta = RandomWithMaxBitWidth(&prng, delta_max_bit_width_);
    518 
    519    bool wrap_around = false;
    520    for (size_t j = 0; j < std::size(last_element); ++j) {
    521      last_element[j] += delta;
    522      if (last_element[j] < bases[j]) {
    523        wrap_around = true;
    524        break;
    525      }
    526    }
    527 
    528    if (wrap_around) {
    529      deltas.resize(i);
    530      break;
    531    }
    532 
    533    deltas[i] = delta;
    534  }
    535 
    536  std::string encodings[std::size(bases)];
    537 
    538  for (size_t i = 0; i < std::size(bases); ++i) {
    539    const auto values =
    540        CreateSequenceByDeltas(bases[i], deltas, num_of_values_);
    541    // Produce the encoding and write it to encodings[i].
    542    // By using TestEncodingAndDecoding() to do this, we also sanity-test
    543    // the encoding/decoding, though that is not the test's focus.
    544    TestEncodingAndDecoding(bases[i], values, &encodings[i]);
    545    EXPECT_LE(encodings[i].length(),
    546              EncodingLengthUpperBound(delta_max_bit_width_, num_of_values_,
    547                                       signedness_));
    548  }
    549 
    550  // Test focus - all of the encodings should be the same, as they are based
    551  // on the same delta sequence, and do not contain a wrap-around.
    552  EXPECT_THAT(encodings, Each(Eq(encodings[0])));
    553 }
    554 
    555 INSTANTIATE_TEST_SUITE_P(
    556    SignednessOverrideAndDeltaMaxBitWidthAndNumberOfValuesInSequence,
    557    DeltaEncodingCompressionQualityTest,
    558    ::testing::Combine(
    559        ::testing::Values(DeltaSignedness::kNoOverride,
    560                          DeltaSignedness::kForceUnsigned,
    561                          DeltaSignedness::kForceSigned),
    562        ::testing::Values(1, 4, 8, 15, 16, 17, 31, 32, 33, 63, 64),
    563        ::testing::Values(1, 2, 100, 10000),
    564        ::testing::Values(11, 12, 13)));
    565 
    566 // Similar to DeltaEncodingTest, but instead of semi-surgically producing
    567 // specific cases, produce large amount of semi-realistic inputs.
    568 class DeltaEncodingFuzzerLikeTest
    569    : public ::testing::TestWithParam<
    570          std::tuple<DeltaSignedness, uint64_t, uint64_t, bool, uint64_t>> {
    571 public:
    572  DeltaEncodingFuzzerLikeTest()
    573      : signedness_(std::get<0>(GetParam())),
    574        delta_max_bit_width_(std::get<1>(GetParam())),
    575        num_of_values_(std::get<2>(GetParam())),
    576        optional_values_(std::get<3>(GetParam())),
    577        partial_random_seed_(std::get<4>(GetParam())) {
    578    MaybeSetSignedness(signedness_);
    579  }
    580 
    581  ~DeltaEncodingFuzzerLikeTest() override = default;
    582 
    583  // Running with the same seed for all variants would make all tests start
    584  // with the same sequence; avoid this by making the seed different.
    585  uint64_t Seed() const {
    586    // Multiply everything but by different primes to produce unique results.
    587    return 2 * static_cast<uint64_t>(signedness_) + 3 * delta_max_bit_width_ +
    588           5 * delta_max_bit_width_ + 7 * num_of_values_ +
    589           11 * static_cast<uint64_t>(optional_values_) +
    590           13 * partial_random_seed_;
    591  }
    592 
    593  const DeltaSignedness signedness_;
    594  const uint64_t delta_max_bit_width_;
    595  const uint64_t num_of_values_;
    596  const bool optional_values_;
    597  const uint64_t partial_random_seed_;  // Explained where it's used.
    598 };
    599 
    600 TEST_P(DeltaEncodingFuzzerLikeTest, Test) {
    601  const std::optional<uint64_t> base(3432);
    602 
    603  Random prng(Seed());
    604  std::vector<std::optional<uint64_t>> deltas(num_of_values_);
    605  for (size_t i = 0; i < deltas.size(); ++i) {
    606    if (!optional_values_ || prng.Rand<bool>()) {
    607      deltas[i] = RandomWithMaxBitWidth(&prng, delta_max_bit_width_);
    608    }
    609  }
    610  const auto values =
    611      CreateSequenceByOptionalDeltas(base.value(), deltas, num_of_values_);
    612 
    613  TestEncodingAndDecoding(base, values);
    614 }
    615 
    616 INSTANTIATE_TEST_SUITE_P(
    617    SignednessOverrideAndDeltaMaxBitWidthAndNumberOfValuesInSequence,
    618    DeltaEncodingFuzzerLikeTest,
    619    ::testing::Combine(
    620        ::testing::Values(DeltaSignedness::kNoOverride,
    621                          DeltaSignedness::kForceUnsigned,
    622                          DeltaSignedness::kForceSigned),
    623        ::testing::Values(1, 4, 8, 15, 16, 17, 31, 32, 33, 63, 64),
    624        ::testing::Values(1, 2, 100, 10000),
    625        ::testing::Bool(),
    626        ::testing::Values(21, 22, 23)));
    627 
    628 class DeltaEncodingSpecificEdgeCasesTest
    629    : public ::testing::TestWithParam<
    630          std::tuple<DeltaSignedness, uint64_t, bool>> {
    631 public:
    632  DeltaEncodingSpecificEdgeCasesTest() {
    633    UnsetFixedLengthEncoderDeltaSignednessForTesting();
    634  }
    635 
    636  ~DeltaEncodingSpecificEdgeCasesTest() override = default;
    637 };
    638 
    639 // This case is special because it produces identical forward/backward deltas.
    640 TEST_F(DeltaEncodingSpecificEdgeCasesTest, SignedDeltaWithOnlyTopBitOn) {
    641  MaybeSetSignedness(DeltaSignedness::kForceSigned);
    642 
    643  const std::optional<uint64_t> base(3432);
    644 
    645  const uint64_t delta = static_cast<uint64_t>(1) << 63;
    646  const std::vector<std::optional<uint64_t>> values = {base.value() + delta};
    647 
    648  TestEncodingAndDecoding(base, values);
    649 }
    650 
    651 TEST_F(DeltaEncodingSpecificEdgeCasesTest, MaximumUnsignedDelta) {
    652  MaybeSetSignedness(DeltaSignedness::kForceUnsigned);
    653 
    654  const std::optional<uint64_t> base((static_cast<uint64_t>(1) << 63) + 0x123);
    655 
    656  const std::vector<std::optional<uint64_t>> values = {base.value() - 1};
    657 
    658  TestEncodingAndDecoding(base, values);
    659 }
    660 
    661 // Check that, if all deltas are set to -1, things still work.
    662 TEST_P(DeltaEncodingSpecificEdgeCasesTest, ReverseSequence) {
    663  MaybeSetSignedness(std::get<0>(GetParam()));
    664  const uint64_t width = std::get<1>(GetParam());
    665  const bool wrap_around = std::get<2>(GetParam());
    666 
    667  const uint64_t value_mask = (width == 64)
    668                                  ? std::numeric_limits<uint64_t>::max()
    669                                  : ((static_cast<uint64_t>(1) << width) - 1);
    670 
    671  const uint64_t base = wrap_around ? 1u : (0xf82d3 & value_mask);
    672  const std::vector<std::optional<uint64_t>> values = {
    673      (base - 1u) & value_mask, (base - 2u) & value_mask,
    674      (base - 3u) & value_mask};
    675 
    676  TestEncodingAndDecoding(base, values);
    677 }
    678 
    679 INSTANTIATE_TEST_SUITE_P(
    680    _,
    681    DeltaEncodingSpecificEdgeCasesTest,
    682    ::testing::Combine(
    683        ::testing::Values(DeltaSignedness::kNoOverride,
    684                          DeltaSignedness::kForceUnsigned,
    685                          DeltaSignedness::kForceSigned),
    686        ::testing::Values(1, 4, 8, 15, 16, 17, 31, 32, 33, 63, 64),
    687        ::testing::Bool()));
    688 
    689 }  // namespace
    690 }  // namespace webrtc