Add conformance tests for integer unwrapping.

This tests all of the known integer unwrappers in WebRTC
* rtc::TimestampWrapAroundHandler
* webrtc::Unwrapper<T>
* webrtc::SequenceNumberUnwrapper<T>
* dcsctp::UnwrappedSequenceNumber

The conformance tests show differences between in behavior between the
unwrappers.

This change also adds const accessors for the internal values of dcsctp::UnwrappedSequenceNumber.

Bug: webrtc:13982
Change-Id: Ia1b483d7ef5ceb43fa4d93013a76cd0251a58c22
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/287620
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Evan Shrubsole <eshr@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38900}
This commit is contained in:
Evan Shrubsole 2022-12-15 09:30:52 +00:00 committed by WebRTC LUCI CQ
parent 4a680f11ae
commit aa5897dcc5
4 changed files with 218 additions and 0 deletions

View File

@ -119,6 +119,14 @@ class UnwrappedSequenceNumber {
return value_ <= other.value_;
}
// Const accessors for underlying value.
constexpr const int64_t* operator->() const { return &value_; }
constexpr const int64_t& operator*() const& { return value_; }
constexpr const int64_t&& operator*() const&& { return std::move(value_); }
constexpr const int64_t& value() const& { return value_; }
constexpr const int64_t&& value() const&& { return std::move(value_); }
constexpr explicit operator const int64_t&() const& { return value_; }
// Increments the value.
void Increment() { ++value_; }

View File

@ -1682,9 +1682,14 @@ if (rtc_include_tests) {
"numerics/percentile_filter_unittest.cc",
"numerics/running_statistics_unittest.cc",
"numerics/sequence_number_util_unittest.cc",
"numerics/sequence_numbers_conformance_test.cc",
]
deps = [
":rtc_numerics",
":strong_alias",
":timeutils",
"../modules:module_api_public",
"../net/dcsctp/common:sequence_numbers",
"../test:test_main",
"../test:test_support",
]

View File

@ -12,4 +12,7 @@ specific_include_rules = {
"gunit\.h": [
"+testing/base/public/gunit.h"
],
"sequence_numbers_conformance_test\.cc": [
"+net/dcsctp/common/sequence_numbers.h",
],
}

View File

@ -0,0 +1,202 @@
/*
* Copyright (c) 2022 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 <cstdint>
#include <limits>
#include <type_traits>
#include "modules/include/module_common_types_public.h"
#include "net/dcsctp/common/sequence_numbers.h"
#include "rtc_base/numerics/sequence_number_util.h"
#include "rtc_base/strong_alias.h"
#include "rtc_base/time_utils.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
using ::testing::Test;
using dcsctp::UnwrappedSequenceNumber;
using Wrapped = webrtc::StrongAlias<class WrappedTag, uint32_t>;
using TestSequence = UnwrappedSequenceNumber<Wrapped>;
template <typename T>
class UnwrapperHelper;
template <>
class UnwrapperHelper<TestSequence::Unwrapper> {
public:
int64_t Unwrap(uint32_t val) {
TestSequence s = unwrapper_.Unwrap(Wrapped(val));
// UnwrappedSequenceNumber starts counting at 2^32.
constexpr int64_t kDcsctpUnwrapStart = int64_t{1} << 32;
return s.value() - kDcsctpUnwrapStart;
}
private:
TestSequence::Unwrapper unwrapper_;
};
// MaxVal is the max of the wrapped space, ie MaxVal + 1 = 0 when wrapped.
template <typename U, int64_t MaxVal = std::numeric_limits<uint32_t>::max()>
struct FixtureParams {
using Unwrapper = U;
static constexpr int64_t kMaxVal = MaxVal;
};
template <typename F>
class UnwrapperConformanceFixture : public Test {
public:
static constexpr int64_t kMaxVal = F::kMaxVal;
static constexpr int64_t kMaxIncrease = kMaxVal / 2;
static constexpr int64_t kMaxBackwardsIncrease = kMaxVal - kMaxIncrease + 1;
template <typename U>
static constexpr bool UnwrapperIs() {
return std::is_same<typename F::Unwrapper, U>();
}
typename F::Unwrapper ref_unwrapper_;
};
TYPED_TEST_SUITE_P(UnwrapperConformanceFixture);
TYPED_TEST_P(UnwrapperConformanceFixture, PositiveWrapAround) {
EXPECT_EQ(0, this->ref_unwrapper_.Unwrap(0));
EXPECT_EQ(TestFixture::kMaxIncrease,
this->ref_unwrapper_.Unwrap(TestFixture::kMaxIncrease));
EXPECT_EQ(2 * TestFixture::kMaxIncrease,
this->ref_unwrapper_.Unwrap(2 * TestFixture::kMaxIncrease));
// Now unwrapping 0 should wrap around to be kMaxVal + 1.
EXPECT_EQ(TestFixture::kMaxVal + 1, this->ref_unwrapper_.Unwrap(0));
EXPECT_EQ(TestFixture::kMaxVal + 1 + TestFixture::kMaxIncrease,
this->ref_unwrapper_.Unwrap(TestFixture::kMaxIncrease));
}
TYPED_TEST_P(UnwrapperConformanceFixture, NegativeUnwrap) {
using UnwrapperT = decltype(this->ref_unwrapper_);
// webrtc::TimestampUnwrapper known to not handle negative numbers.
// rtc::TimestampWrapAroundHandler does not wrap around correctly.
if constexpr (std::is_same<UnwrapperT, webrtc::TimestampUnwrapper>() ||
std::is_same<UnwrapperT, rtc::TimestampWrapAroundHandler>()) {
return;
}
EXPECT_EQ(0, this->ref_unwrapper_.Unwrap(0));
// Max backwards wrap is negative.
EXPECT_EQ(-TestFixture::kMaxIncrease,
this->ref_unwrapper_.Unwrap(this->kMaxBackwardsIncrease));
// Increase to a larger negative number.
EXPECT_EQ(-2, this->ref_unwrapper_.Unwrap(TestFixture::kMaxVal - 1));
// Increase back positive.
EXPECT_EQ(1, this->ref_unwrapper_.Unwrap(1));
}
TYPED_TEST_P(UnwrapperConformanceFixture, BackwardUnwrap) {
EXPECT_EQ(127, this->ref_unwrapper_.Unwrap(127));
EXPECT_EQ(128, this->ref_unwrapper_.Unwrap(128));
EXPECT_EQ(127, this->ref_unwrapper_.Unwrap(127));
}
TYPED_TEST_P(UnwrapperConformanceFixture, MultiplePositiveWrapArounds) {
using UnwrapperT = decltype(this->ref_unwrapper_);
// rtc::TimestampWrapAroundHandler does not wrap around correctly.
if constexpr (std::is_same<UnwrapperT, rtc::TimestampWrapAroundHandler>()) {
return;
}
int64_t val = 0;
uint32_t wrapped_val = 0;
for (int i = 0; i < 16; ++i) {
EXPECT_EQ(val, this->ref_unwrapper_.Unwrap(wrapped_val));
val += TestFixture::kMaxIncrease;
wrapped_val =
(wrapped_val + TestFixture::kMaxIncrease) % (TestFixture::kMaxVal + 1);
}
}
TYPED_TEST_P(UnwrapperConformanceFixture, WrapBoundaries) {
EXPECT_EQ(0, this->ref_unwrapper_.Unwrap(0));
EXPECT_EQ(TestFixture::kMaxIncrease,
this->ref_unwrapper_.Unwrap(TestFixture::kMaxIncrease));
// Increases by more than TestFixture::kMaxIncrease which indicates a negative
// rollback.
EXPECT_EQ(0, this->ref_unwrapper_.Unwrap(0));
EXPECT_EQ(10, this->ref_unwrapper_.Unwrap(10));
}
TYPED_TEST_P(UnwrapperConformanceFixture, MultipleNegativeWrapArounds) {
using UnwrapperT = decltype(this->ref_unwrapper_);
// webrtc::TimestampUnwrapper known to not handle negative numbers.
// webrtc::SequenceNumberUnwrapper can only wrap negative once.
// rtc::TimestampWrapAroundHandler does not wrap around correctly.
if constexpr (std::is_same<UnwrapperT, webrtc::TimestampUnwrapper>() ||
std::is_same<UnwrapperT,
UnwrapperHelper<TestSequence::Unwrapper>>() ||
std::is_same<UnwrapperT, rtc::TimestampWrapAroundHandler>()) {
return;
}
int64_t val = 0;
uint32_t wrapped_val = 0;
for (int i = 0; i < 16; ++i) {
EXPECT_EQ(val, this->ref_unwrapper_.Unwrap(wrapped_val));
val -= TestFixture::kMaxIncrease;
wrapped_val = (wrapped_val + this->kMaxBackwardsIncrease) %
(TestFixture::kMaxVal + 1);
}
}
REGISTER_TYPED_TEST_SUITE_P(UnwrapperConformanceFixture,
NegativeUnwrap,
PositiveWrapAround,
BackwardUnwrap,
WrapBoundaries,
MultiplePositiveWrapArounds,
MultipleNegativeWrapArounds);
constexpr int64_t k15BitMax = (int64_t{1} << 15) - 1;
using UnwrapperTypes = ::testing::Types<
FixtureParams<rtc::TimestampWrapAroundHandler>,
FixtureParams<webrtc::TimestampUnwrapper>,
FixtureParams<webrtc::SeqNumUnwrapper<uint32_t>>,
FixtureParams<UnwrapperHelper<TestSequence::Unwrapper>>,
// SeqNumUnwrapper supports arbitrary limits.
FixtureParams<webrtc::SeqNumUnwrapper<uint32_t, k15BitMax + 1>, k15BitMax>>;
class TestNames {
public:
template <typename T>
static std::string GetName(int) {
if constexpr (std::is_same<typename T::Unwrapper,
rtc::TimestampWrapAroundHandler>())
return "TimestampWrapAroundHandler";
if constexpr (std::is_same<typename T::Unwrapper,
webrtc::TimestampUnwrapper>())
return "TimestampUnwrapper";
if constexpr (std::is_same<typename T::Unwrapper,
webrtc::SeqNumUnwrapper<uint32_t>>())
return "SeqNumUnwrapper";
if constexpr (std::is_same<
typename T::Unwrapper,
webrtc::SeqNumUnwrapper<uint32_t, k15BitMax + 1>>())
return "SeqNumUnwrapper15bit";
if constexpr (std::is_same<typename T::Unwrapper,
UnwrapperHelper<TestSequence::Unwrapper>>())
return "UnwrappedSequenceNumber";
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(UnwrapperConformanceTest,
UnwrapperConformanceFixture,
UnwrapperTypes,
TestNames);
} // namespace
} // namespace webrtc