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