// SPDX-License-Identifier: GPL-2.0-or-later #include #include namespace si = Safe::Internal; /*! * struct that contains test numbers for Safe::add. Since different test values * are required for signed and unsigned types, this struct is specialized via * SFINAE. * * Each overload has two arrays: * T summand[] - the test numbers * bool overflow[][] - overflow[i][j] indicates whether summand[i] + summand[j] * is expected to overflow * * The numbers in summand are chosen accordingly to test all "interesting" cases * and cause some overflows. The actual test should simply check all possible * sums of summand against the value in overflow[][] */ template struct AdditionTestValues; /*! * Overload for unsigned types. */ template struct AdditionTestValues::VALUE>::type> { static const size_t case_count = 5; static const T summand[case_count]; static const bool overflow[case_count][case_count]; }; template const T AdditionTestValues::VALUE>::type>::summand[] = { 0, 1, 2, static_cast(std::numeric_limits::max() - 1), std::numeric_limits::max()}; template const bool AdditionTestValues::VALUE>::type>::overflow[case_count][case_count] = { // 0 {false, false, false, false, false}, // 1 {false, false, false, false, true}, // 2 {false, false, false, true, true}, // max - 1 {false, false, true, true, true}, // max {false, true, true, true, true}}; /*! * Overload for signed integers */ template struct AdditionTestValues::VALUE>::type> { static const size_t case_count = 8; static const T summand[case_count]; static const bool overflow[case_count][case_count]; }; template const T AdditionTestValues::VALUE>::type>::summand[] = { std::numeric_limits::min(), static_cast(std::numeric_limits::min() + 1), -1, 0, 1, 2, static_cast(std::numeric_limits::max() - 1), std::numeric_limits::max()}; template const bool AdditionTestValues::VALUE>::type>::overflow[case_count][case_count] = { // min {true, true, true, false, false, false, false, false}, // min + 1 {true, true, false, false, false, false, false, false}, // -1 {true, false, false, false, false, false, false, false}, // 0 {false, false, false, false, false, false, false, false}, // 1 {false, false, false, false, false, false, false, true}, // 2 {false, false, false, false, false, false, true, true}, // max - 1 {false, false, false, false, false, true, true, true}, // max {false, false, false, false, true, true, true, true}}; /*! * Test the addition of all combinations of AdditionTestValues::summand[i], * AdditionTestValues::summand[j] using fallback_add_overflow::add and * builtin_add_overflow::add. The return value is checked against * AdditionTestValues::overflow[i][j]. If no overflow occurs, then the result * is checked too. */ template void test_add() { using TestValues = AdditionTestValues; #define TEST_ADD(func) \ for (size_t i = 0; i < TestValues::case_count; ++i) { \ for (size_t j = 0; j < TestValues::case_count; ++j) { \ T res = 0; \ ASSERT_EQ(func(TestValues::summand[i], TestValues::summand[j], res), TestValues::overflow[i][j]); \ if (!TestValues::overflow[i][j]) { \ ASSERT_EQ(res, TestValues::summand[i] + TestValues::summand[j]); \ } \ } \ } TEST_ADD(si::fallback_add_overflow) TEST_ADD(si::builtin_add_overflow) #undef TEST_ADD } /*! * Test the addition of all combinations of AdditionTestValues::summand[i], * AdditionTestValues::summand[j] using Safe::add. * * It is checked whether an exception is thrown when * AdditionTestValues::overflow[i][j] is true, otherwise the result is * checked. */ template void test_safe_add() { using TestValues = AdditionTestValues; for (size_t i = 0; i < TestValues::case_count; ++i) { for (size_t j = 0; j < TestValues::case_count; ++j) { if (TestValues::overflow[i][j]) { ASSERT_THROW(Safe::add(TestValues::summand[i], TestValues::summand[j]), std::overflow_error); } else { ASSERT_EQ(Safe::add(TestValues::summand[i], TestValues::summand[j]), TestValues::summand[i] + TestValues::summand[j]); } } } } TEST(lowLevelAddOverflow, checkUnsignedOverflow) { test_add(); test_add(); test_add(); test_add(); test_add(); } TEST(lowLevelAddOverflow, checkSignedOverflow) { test_add(); test_add(); test_add(); test_add(); test_add(); } TEST(safeAdd, checkUnsignedOverflow) { test_safe_add(); test_safe_add(); test_safe_add(); test_safe_add(); test_safe_add(); } TEST(safeAdd, checkSignedOverflow) { test_safe_add(); test_safe_add(); test_safe_add(); test_safe_add(); test_safe_add(); } TEST(safeAbs, checkValues) { static const int values[] = {-1, 1, std::numeric_limits::max(), std::numeric_limits::min() + 1}; for (int value : values) { ASSERT_EQ(Safe::abs(value), abs(value)); } ASSERT_EQ(Safe::abs(std::numeric_limits::min()), std::numeric_limits::max()); }