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