Skip to content

Commit

Permalink
Add conformance tests for integer unwrapping.
Browse files Browse the repository at this point in the history
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}
  • Loading branch information
Evan Shrubsole authored and WebRTC LUCI CQ committed Dec 15, 2022
1 parent 4a680f1 commit aa5897d
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 0 deletions.
8 changes: 8 additions & 0 deletions net/dcsctp/common/sequence_numbers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_; }

Expand Down
5 changes: 5 additions & 0 deletions rtc_base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
Expand Down
3 changes: 3 additions & 0 deletions rtc_base/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
}
202 changes: 202 additions & 0 deletions rtc_base/numerics/sequence_numbers_conformance_test.cc
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit aa5897d

Please sign in to comment.