1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "extended_accumulator_tests"
18
19 #include <mediautils/ExtendedAccumulator.h>
20
21 #include <type_traits>
22 #include <cstdint>
23 #include <limits.h>
24
25 #include <gtest/gtest.h>
26 #include <log/log.h>
27
28 using namespace android;
29 using namespace android::mediautils;
30
31 // Conditionally choose a base accumulating counter value in order to prevent
32 // unsigned underflow on the accumulator from aborting the tests.
33 template <typename TType, typename CType>
getBase()34 static constexpr CType getBase() {
35 static_assert(sizeof(TType) < sizeof(CType));
36 if constexpr (std::is_unsigned_v<CType>) {
37 return std::numeric_limits<TType>::max() + 1;
38 } else {
39 return 0;
40 }
41 }
42
43 // Since the entire state of this utility is the previous value, and the
44 // behavior is isomorphic mod the underlying type on the previous value, we can
45 // test combinations of the previous value of the underlying type and a
46 // hypothetical signed update to that type and ensure the accumulator moves
47 // correctly and reports overflow correctly.
48 template <typename TestUInt, typename CType>
testPair(TestUInt prevVal,std::make_signed_t<TestUInt> delta)49 void testPair(TestUInt prevVal, std::make_signed_t<TestUInt> delta) {
50 using TestDetect = ExtendedAccumulator<TestUInt, CType>;
51 using TestInt = typename TestDetect::SignedInt;
52 static_assert(std::is_same_v<typename TestDetect::UnsignedInt, TestUInt>);
53 static_assert(std::is_same_v<TestInt, std::make_signed_t<TestUInt>>);
54 static_assert(sizeof(TestUInt) < sizeof(CType));
55
56 // To safely detect underflow/overflow for testing
57 // Should be 0 mod TestUInt, max + 1 is convenient
58 static constexpr CType base = getBase<TestUInt, CType>();
59 const CType prev = base + prevVal;
60 TestDetect test{prev};
61 EXPECT_EQ(test.getValue(), prev);
62 // Prevent unsigned wraparound abort
63 CType next;
64 const auto err = __builtin_add_overflow(prev, delta, &next);
65 LOG_ALWAYS_FATAL_IF(err, "Unexpected wrap in tests");
66 const auto [result, status] = test.poll(static_cast<TestUInt>(next));
67 EXPECT_EQ(test.getValue(), next);
68 EXPECT_EQ(result, delta);
69
70 // Test overflow/underflow event reporting.
71 if (next < base) EXPECT_EQ(TestDetect::Wrap::Underflow, status);
72 else if (next > base + std::numeric_limits<TestUInt>::max())
73 EXPECT_EQ(TestDetect::Wrap::Overflow, status);
74 else EXPECT_EQ(TestDetect::Wrap::Normal, status);
75 }
76
77 // Test this utility on every combination of prior and update value for the
78 // type uint8_t, with an unsigned containing type.
TEST(wraparound_tests,cover_u8_u64)79 TEST(wraparound_tests, cover_u8_u64) {
80 using TType = uint8_t;
81 using CType = uint64_t;
82 static constexpr CType max = std::numeric_limits<TType>::max();
83 for (CType i = 0; i <= max; i++) {
84 for (CType j = 0; j <= max; j++) {
85 testPair<TType, CType>(i, static_cast<int64_t>(j));
86 }
87 }
88 }
89
90 // Test this utility on every combination of prior and update value for the
91 // type uint8_t, with a signed containing type.
TEST(wraparound_tests,cover_u8_s64)92 TEST(wraparound_tests, cover_u8_s64) {
93 using TType = uint8_t;
94 using CType = int64_t;
95 static constexpr CType max = std::numeric_limits<TType>::max();
96 for (CType i = 0; i <= max; i++) {
97 for (CType j = 0; j <= max; j++) {
98 testPair<TType, CType>(i, static_cast<int64_t>(j));
99 }
100 }
101 }
102