diff --git a/net/dcsctp/common/sequence_numbers.h b/net/dcsctp/common/sequence_numbers.h index 919fc5014a..c3422c2ccd 100644 --- a/net/dcsctp/common/sequence_numbers.h +++ b/net/dcsctp/common/sequence_numbers.h @@ -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_; } diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index fa60656541..8ac6e4102c 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -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", ] diff --git a/rtc_base/DEPS b/rtc_base/DEPS index 3a77b5502a..3882f5acb5 100644 --- a/rtc_base/DEPS +++ b/rtc_base/DEPS @@ -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", + ], } diff --git a/rtc_base/numerics/sequence_numbers_conformance_test.cc b/rtc_base/numerics/sequence_numbers_conformance_test.cc new file mode 100644 index 0000000000..8c5bc62e56 --- /dev/null +++ b/rtc_base/numerics/sequence_numbers_conformance_test.cc @@ -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 +#include +#include + +#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; +using TestSequence = UnwrappedSequenceNumber; + +template +class UnwrapperHelper; + +template <> +class UnwrapperHelper { + 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 ::max()> +struct FixtureParams { + using Unwrapper = U; + static constexpr int64_t kMaxVal = MaxVal; +}; + +template +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 + static constexpr bool UnwrapperIs() { + return std::is_same(); + } + + 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() || + std::is_same()) { + 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()) { + 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() || + std::is_same>() || + std::is_same()) { + 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, + FixtureParams, + FixtureParams>, + FixtureParams>, + // SeqNumUnwrapper supports arbitrary limits. + FixtureParams, k15BitMax>>; + +class TestNames { + public: + template + static std::string GetName(int) { + if constexpr (std::is_same()) + return "TimestampWrapAroundHandler"; + if constexpr (std::is_same()) + return "TimestampUnwrapper"; + if constexpr (std::is_same>()) + return "SeqNumUnwrapper"; + if constexpr (std::is_same< + typename T::Unwrapper, + webrtc::SeqNumUnwrapper>()) + return "SeqNumUnwrapper15bit"; + if constexpr (std::is_same>()) + return "UnwrappedSequenceNumber"; + } +}; + +INSTANTIATE_TYPED_TEST_SUITE_P(UnwrapperConformanceTest, + UnwrapperConformanceFixture, + UnwrapperTypes, + TestNames); + +} // namespace +} // namespace webrtc