blob: 62f7eb7b1d2e5c73580cd10f77d8e2618c8b4c47 [file] [log] [blame]
/*
* 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_unwrapper.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 = 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_);
// TimestampUnwrapper known to not handle negative numbers.
// rtc::TimestampWrapAroundHandler does not wrap around correctly.
if constexpr (std::is_same<UnwrapperT, 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_);
// TimestampUnwrapper known to not handle negative numbers.
// SequenceNumberUnwrapper can only wrap negative once.
// rtc::TimestampWrapAroundHandler does not wrap around correctly.
if constexpr (std::is_same<UnwrapperT, 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<TimestampUnwrapper>,
FixtureParams<RtpTimestampUnwrapper>,
FixtureParams<UnwrapperHelper<TestSequence::Unwrapper>>,
// SeqNumUnwrapper supports arbitrary limits.
FixtureParams<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, TimestampUnwrapper>())
return "TimestampUnwrapper";
if constexpr (std::is_same<typename T::Unwrapper,
SeqNumUnwrapper<uint32_t>>())
return "SeqNumUnwrapper";
if constexpr (std::is_same<typename T::Unwrapper,
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