Files
wehub-resource-sync 8a852e4b4e
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:14:16 +08:00

738 lines
22 KiB
C++

/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/util.h"
#include <stddef.h>
#include <stdlib.h>
#include <algorithm>
#include <cstdint>
#include <limits>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace tflite {
namespace {
using testing::ElementsAreArray;
TEST(ConvertVectorToTfLiteIntArray, TestWithVector) {
std::vector<int> input = {1, 2};
TfLiteIntArray* output = ConvertVectorToTfLiteIntArray(input);
ASSERT_NE(output, nullptr);
EXPECT_EQ(output->size, 2);
EXPECT_EQ(output->data[0], 1);
EXPECT_EQ(output->data[1], 2);
TfLiteIntArrayFree(output);
}
TEST(ConvertVectorToTfLiteIntArray, TestWithEmptyVector) {
std::vector<int> input;
TfLiteIntArray* output = ConvertVectorToTfLiteIntArray(input);
ASSERT_NE(output, nullptr);
EXPECT_EQ(output->size, 0);
TfLiteIntArrayFree(output);
}
TEST(UtilTest, IsFlexOp) {
EXPECT_TRUE(IsFlexOp("Flex"));
EXPECT_TRUE(IsFlexOp("FlexOp"));
EXPECT_FALSE(IsFlexOp("flex"));
EXPECT_FALSE(IsFlexOp("Fle"));
EXPECT_FALSE(IsFlexOp("OpFlex"));
EXPECT_FALSE(IsFlexOp(nullptr));
EXPECT_FALSE(IsFlexOp(""));
}
TEST(EqualArrayAndTfLiteIntArray, TestWithTFLiteArrayEmpty) {
int input[] = {1, 2, 3, 4};
EXPECT_FALSE(EqualArrayAndTfLiteIntArray(nullptr, 4, input));
}
TEST(EqualArrayAndTfLiteIntArray, TestWithTFLiteArrayWrongSize) {
int input[] = {1, 2, 3, 4};
TfLiteIntArray* output = ConvertArrayToTfLiteIntArray(4, input);
EXPECT_FALSE(EqualArrayAndTfLiteIntArray(output, 3, input));
free(output);
}
TEST(EqualArrayAndTfLiteIntArray, TestMismatch) {
int input[] = {1, 2, 3, 4};
TfLiteIntArray* output = ConvertVectorToTfLiteIntArray({1, 2, 2, 4});
EXPECT_FALSE(EqualArrayAndTfLiteIntArray(output, 4, input));
free(output);
}
TEST(EqualArrayAndTfLiteIntArray, TestMatch) {
int input[] = {1, 2, 3, 4};
TfLiteIntArray* output = ConvertArrayToTfLiteIntArray(4, input);
EXPECT_TRUE(EqualArrayAndTfLiteIntArray(output, 4, input));
free(output);
}
TEST(CombineHashes, TestHashOutputsEquals) {
size_t output1 = CombineHashes({1, 2, 3, 4});
size_t output2 = CombineHashes({1, 2, 3, 4});
EXPECT_EQ(output1, output2);
}
TEST(CombineHashes, TestHashOutputsDifferent) {
size_t output1 = CombineHashes({1, 2, 3, 4});
size_t output2 = CombineHashes({1, 2, 2, 4});
EXPECT_NE(output1, output2);
}
TEST(GetOpNameByRegistration, ValidBuiltinCode) {
TfLiteRegistration registration{};
registration.builtin_code = tflite::BuiltinOperator_ADD;
const auto op_name = GetOpNameByRegistration(registration);
EXPECT_EQ("ADD", op_name);
}
TEST(GetOpNameByRegistration, InvalidBuiltinCode) {
TfLiteRegistration registration{};
registration.builtin_code = -1;
const auto op_name = GetOpNameByRegistration(registration);
EXPECT_EQ("", op_name);
}
TEST(GetOpNameByRegistration, CustomName) {
TfLiteRegistration registration{};
registration.builtin_code = tflite::BuiltinOperator_CUSTOM;
registration.custom_name = "TestOp";
auto op_name = GetOpNameByRegistration(registration);
EXPECT_EQ("CUSTOM TestOp", op_name);
registration.builtin_code = tflite::BuiltinOperator_DELEGATE;
registration.custom_name = "TestDelegate";
op_name = GetOpNameByRegistration(registration);
EXPECT_EQ("DELEGATE TestDelegate", op_name);
}
TEST(ValidationSubgraph, NameIsDetected) {
EXPECT_FALSE(IsValidationSubgraph(nullptr));
EXPECT_FALSE(IsValidationSubgraph(""));
EXPECT_FALSE(IsValidationSubgraph("a name"));
EXPECT_FALSE(IsValidationSubgraph("VALIDATIONfoo"));
EXPECT_TRUE(IsValidationSubgraph("VALIDATION:"));
EXPECT_TRUE(IsValidationSubgraph("VALIDATION:main"));
}
TEST(MultiplyAndCheckOverflow, Validate) {
size_t res = 0;
EXPECT_TRUE(MultiplyAndCheckOverflow(1, 2, &res) == kTfLiteOk);
EXPECT_FALSE(MultiplyAndCheckOverflow(static_cast<size_t>(123456789023),
1223423425, &res) == kTfLiteOk);
}
TEST(CheckedNumElements, Validate) {
std::vector<int> dims = {2, 3, 4};
size_t count = 0;
EXPECT_EQ(CheckedNumElements(dims, count), kTfLiteOk);
EXPECT_EQ(count, 24);
}
TEST(CheckedNumElements, RejectsNegativeDimension) {
std::vector<int> dims = {2, -1, 4};
size_t count = 0;
EXPECT_EQ(CheckedNumElements(dims, count), kTfLiteError);
}
TEST(CheckedNumElements, RejectsOverflow) {
std::vector<int> dims = {std::numeric_limits<int>::max(),
std::numeric_limits<int>::max(),
std::numeric_limits<int>::max()};
size_t count = 0;
EXPECT_EQ(CheckedNumElements(dims, count), kTfLiteError);
}
TEST(CheckedNumElementsToInt, Validate) {
std::vector<int> dims = {2, 3, 4};
int count = 0;
EXPECT_EQ(CheckedNumElements(dims, count), kTfLiteOk);
EXPECT_EQ(count, 24);
}
TEST(CheckedNumElementsToInt, RejectsOverflow) {
std::vector<int> dims = {std::numeric_limits<int>::max(),
std::numeric_limits<int>::max()};
int count = 0;
EXPECT_EQ(CheckedNumElements(dims, count), kTfLiteError);
}
TEST(FourBitTest, BytesRequiredEven) {
TfLiteContext context;
int dims[] = {2, 3, 1, 5};
const int* dims_ptr = &dims[0];
size_t dims_size = 4;
size_t required_bytes_four_bit;
tflite::BytesRequired(kTfLiteInt4, dims_ptr, dims_size,
&required_bytes_four_bit, &context);
ASSERT_EQ(required_bytes_four_bit, 15);
}
TEST(FourBitTest, BytesRequiredOdd) {
TfLiteContext context;
int dims[] = {5, 1, 1, 1};
const int* dims_ptr = &dims[0];
size_t dims_size = 2;
size_t required_bytes_four_bit;
tflite::BytesRequired(kTfLiteInt4, dims_ptr, dims_size,
&required_bytes_four_bit, &context);
ASSERT_EQ(required_bytes_four_bit, 3);
}
TEST(TestMakeUniqueTensor, Valid) {
TensorUniquePtr t = BuildTfLiteTensor(kTfLiteInt32, {2, 3}, kTfLiteDynamic);
ASSERT_NE(t.get(), nullptr);
ASSERT_EQ(t->buffer_handle, kTfLiteNullBufferHandle);
EXPECT_THAT(t.get(), DimsAre({2, 3}));
EXPECT_EQ(t->bytes, 24);
EXPECT_EQ(t->type, kTfLiteInt32);
EXPECT_EQ(t->allocation_type, kTfLiteDynamic);
// Check memory has been properly allocated.
int* data = t->data.i32;
std::fill_n(data, 6, 0);
ASSERT_NE(data, nullptr);
ASSERT_THAT(std::vector<int>(data, data + 6),
ElementsAreArray({0, 0, 0, 0, 0, 0}));
}
TEST(TestMakeUniqueTensor, NullDimsReturnsNull) {
TensorUniquePtr t = BuildTfLiteTensor(kTfLiteInt32, nullptr, kTfLiteDynamic);
ASSERT_EQ(t.get(), nullptr);
}
template <typename T>
class CheckedIntTypedTest : public ::testing::Test {};
using TestTypes = ::testing::Types<
std::pair<int8_t, int8_t>, std::pair<int8_t, int16_t>,
std::pair<int8_t, int32_t>, std::pair<int8_t, int64_t>,
std::pair<int8_t, uint8_t>, std::pair<int8_t, uint16_t>,
std::pair<int8_t, uint32_t>, std::pair<int8_t, uint64_t>,
std::pair<int16_t, int8_t>, std::pair<int16_t, int16_t>,
std::pair<int16_t, int32_t>, std::pair<int16_t, int64_t>,
std::pair<int16_t, uint8_t>, std::pair<int16_t, uint16_t>,
std::pair<int16_t, uint32_t>, std::pair<int16_t, uint64_t>,
std::pair<int32_t, int8_t>, std::pair<int32_t, int16_t>,
std::pair<int32_t, int32_t>, std::pair<int32_t, int64_t>,
std::pair<int32_t, uint8_t>, std::pair<int32_t, uint16_t>,
std::pair<int32_t, uint32_t>, std::pair<int32_t, uint64_t>,
std::pair<int64_t, int8_t>, std::pair<int64_t, int16_t>,
std::pair<int64_t, int32_t>, std::pair<int64_t, int64_t>,
std::pair<int64_t, uint8_t>, std::pair<int64_t, uint16_t>,
std::pair<int64_t, uint32_t>, std::pair<int64_t, uint64_t>,
std::pair<uint8_t, int8_t>, std::pair<uint8_t, int16_t>,
std::pair<uint8_t, int32_t>, std::pair<uint8_t, int64_t>,
std::pair<uint8_t, uint8_t>, std::pair<uint8_t, uint16_t>,
std::pair<uint8_t, uint32_t>, std::pair<uint8_t, uint64_t>,
std::pair<uint16_t, int8_t>, std::pair<uint16_t, int16_t>,
std::pair<uint16_t, int32_t>, std::pair<uint16_t, int64_t>,
std::pair<uint16_t, uint8_t>, std::pair<uint16_t, uint16_t>,
std::pair<uint16_t, uint32_t>, std::pair<uint16_t, uint64_t>,
std::pair<uint32_t, int8_t>, std::pair<uint32_t, int16_t>,
std::pair<uint32_t, int32_t>, std::pair<uint32_t, int64_t>,
std::pair<uint32_t, uint8_t>, std::pair<uint32_t, uint16_t>,
std::pair<uint32_t, uint32_t>, std::pair<uint32_t, uint64_t>,
std::pair<uint64_t, int8_t>, std::pair<uint64_t, int16_t>,
std::pair<uint64_t, int32_t>, std::pair<uint64_t, int64_t>,
std::pair<uint64_t, uint8_t>, std::pair<uint64_t, uint16_t>,
std::pair<uint64_t, uint32_t>, std::pair<uint64_t, uint64_t>>;
TYPED_TEST_SUITE(CheckedIntTypedTest, TestTypes);
TYPED_TEST(CheckedIntTypedTest, ConstructorFromOtherTypeBoundsCheck) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
T2 x = std::numeric_limits<T2>::lowest();
T2 y = std::numeric_limits<T2>::max();
CheckedInt<T1> a(x);
CheckedInt<T1> b(y);
if constexpr (sizeof(T1) > sizeof(T2)) {
if constexpr (std::is_signed_v<T1> || std::is_unsigned_v<T2>) {
EXPECT_EQ(a.Value(), x);
EXPECT_FALSE(a.Overflow());
} else {
EXPECT_TRUE(a.Overflow());
}
EXPECT_EQ(b.Value(), y);
EXPECT_FALSE(b.Overflow());
} else if constexpr (sizeof(T1) == sizeof(T2)) {
if constexpr (std::is_signed_v<T1> == std::is_signed_v<T2>) {
EXPECT_EQ(a.Value(), x);
EXPECT_FALSE(a.Overflow());
EXPECT_EQ(b.Value(), y);
EXPECT_FALSE(b.Overflow());
} else if constexpr (std::is_unsigned_v<T1>) {
EXPECT_TRUE(a.Overflow());
EXPECT_EQ(b.Value(), y);
EXPECT_FALSE(b.Overflow());
} else { // signed T1, unsigned T2
EXPECT_EQ(a.Value(), x);
EXPECT_FALSE(a.Overflow());
EXPECT_TRUE(b.Overflow());
}
} else { // sizeof(T1) < sizeof(T2)
if constexpr (std::is_signed_v<T1> && std::is_signed_v<T2>) {
EXPECT_TRUE(a.Overflow());
EXPECT_TRUE(b.Overflow());
} else if constexpr (std::is_unsigned_v<T1> && std::is_unsigned_v<T2>) {
EXPECT_EQ(a.Value(), x);
EXPECT_FALSE(a.Overflow());
EXPECT_TRUE(b.Overflow());
} else if constexpr (std::is_unsigned_v<T1>) {
EXPECT_TRUE(a.Overflow());
EXPECT_TRUE(b.Overflow());
} else { // signed T1, unsigned T2
EXPECT_EQ(a.Value(), x);
EXPECT_FALSE(a.Overflow());
EXPECT_TRUE(b.Overflow());
}
}
}
TYPED_TEST(CheckedIntTypedTest, BasicArithmetic) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
CheckedInt<T1> a(10);
CheckedInt<T2> b(2);
CheckedInt add = a + b;
EXPECT_EQ(add.Value(), 12);
EXPECT_FALSE(add.Overflow());
CheckedInt sub = a - b;
EXPECT_EQ(sub.Value(), 8);
EXPECT_FALSE(sub.Overflow());
CheckedInt mul = a * b;
EXPECT_EQ(mul.Value(), 20);
EXPECT_FALSE(mul.Overflow());
CheckedInt div = a / b;
EXPECT_EQ(div.Value(), 5);
EXPECT_FALSE(div.Overflow());
}
TYPED_TEST(CheckedIntTypedTest, WorkingMultiplication) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
if (std::is_signed_v<T1> && std::is_signed_v<T2>) {
{
CheckedInt<T1> a(-1);
CheckedInt<T2> b(5);
CheckedInt c = a * b;
EXPECT_EQ(c.Value(), -5);
EXPECT_FALSE(c.Overflow());
}
{
CheckedInt<T1> a(-1);
CheckedInt<T2> b(-5);
CheckedInt c = a * b;
EXPECT_EQ(c.Value(), 5);
EXPECT_FALSE(c.Overflow());
}
{
CheckedInt<T1> a(1);
CheckedInt<T2> b(-5);
CheckedInt c = a * b;
EXPECT_EQ(c.Value(), -5);
EXPECT_FALSE(c.Overflow());
}
}
}
TYPED_TEST(CheckedIntTypedTest, OverflowPropagation) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
// Check overflow propagation from lhs.
CheckedInt<T1> a_base(std::numeric_limits<T1>::max());
CheckedInt<T1> a_add(1);
CheckedInt a = a_base + a_add;
ASSERT_TRUE(a.Overflow());
CheckedInt<T2> b(2);
EXPECT_TRUE((a + b).Overflow());
EXPECT_TRUE((a - b).Overflow());
EXPECT_TRUE((a * b).Overflow());
EXPECT_TRUE((a / b).Overflow());
// Check overflow propagation from rhs.
CheckedInt<T1> c(10);
CheckedInt<T2> d_base(std::numeric_limits<T2>::max());
CheckedInt<T2> d_add(1);
CheckedInt d = d_base + d_add;
ASSERT_TRUE(d.Overflow());
EXPECT_TRUE((c + d).Overflow());
EXPECT_TRUE((c - d).Overflow());
EXPECT_TRUE((c * d).Overflow());
EXPECT_TRUE((c / d).Overflow());
}
TYPED_TEST(CheckedIntTypedTest, AdditionOverflow) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
using C = std::common_type_t<T1, T2>;
CheckedInt<T1> a(std::numeric_limits<T1>::max());
CheckedInt<T2> b(std::numeric_limits<T2>::max());
EXPECT_EQ((a + b).Overflow(),
sizeof(C) == sizeof(T1) || sizeof(C) == sizeof(T2));
}
TYPED_TEST(CheckedIntTypedTest, AdditionUnderflow) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
using C = std::common_type_t<T1, T2>;
if constexpr ((std::is_signed_v<T1> && std::is_signed_v<T2> &&
(sizeof(C) == sizeof(T1) || sizeof(C) == sizeof(T2))) ||
(std::is_unsigned_v<C> &&
(std::is_signed_v<T1> || std::is_signed_v<T2>))) {
CheckedInt<T1> a(std::numeric_limits<T1>::lowest());
CheckedInt<T2> b(std::numeric_limits<T2>::lowest());
EXPECT_TRUE((a + b).Overflow()) << +a.Value() << " + " << +b.Value();
} else {
GTEST_SUCCEED();
}
}
TYPED_TEST(CheckedIntTypedTest, SubtractionUnderflow) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
using C = std::common_type_t<T1, T2>;
CheckedInt<T1> a(std::numeric_limits<T1>::lowest());
CheckedInt<T2> b(std::numeric_limits<T2>::max());
if (a.Value() == 0 && std::is_signed_v<C>) {
EXPECT_FALSE((a - b).Overflow());
} else {
EXPECT_EQ((a - b).Overflow(),
(sizeof(C) == sizeof(T1) || sizeof(C) == sizeof(T2)));
}
}
TYPED_TEST(CheckedIntTypedTest, SubtractionOverflow) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
using C = std::common_type_t<T1, T2>;
if constexpr (std::is_signed_v<T2> &&
(sizeof(C) == sizeof(T1) || sizeof(C) == sizeof(T2))) {
CheckedInt<T1> a(std::numeric_limits<T1>::max());
CheckedInt<T2> b(std::numeric_limits<T2>::lowest());
EXPECT_TRUE((a - b).Overflow()) << +a.Value() << " - " << +b.Value();
} else {
GTEST_SUCCEED();
}
}
TYPED_TEST(CheckedIntTypedTest, MultiplicationOverflow) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
using C = std::common_type_t<T1, T2>;
CheckedInt<T1> a(std::numeric_limits<T1>::max());
CheckedInt<T2> b(std::numeric_limits<T2>::max());
EXPECT_EQ((a * b).Overflow(),
sizeof(C) == sizeof(T1) || sizeof(C) == sizeof(T2));
}
TYPED_TEST(CheckedIntTypedTest, MultiplicationUnderflowOppositeSign) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
using C = std::common_type_t<T1, T2>;
if constexpr (std::is_signed_v<T1>) {
CheckedInt<T1> a(std::numeric_limits<T1>::lowest());
CheckedInt<T2> b(std::numeric_limits<T2>::max());
EXPECT_EQ((a * b).Overflow(),
sizeof(C) == sizeof(T1) || sizeof(C) == sizeof(T2));
}
if constexpr (std::is_signed_v<T2>) {
CheckedInt<T1> c(std::numeric_limits<T1>::max());
CheckedInt<T2> d(std::numeric_limits<T2>::lowest());
EXPECT_EQ((c * d).Overflow(),
sizeof(C) == sizeof(T1) || sizeof(C) == sizeof(T2));
}
if constexpr (!std::is_signed_v<T1> && !std::is_signed_v<T2>) {
GTEST_SUCCEED();
}
}
TYPED_TEST(CheckedIntTypedTest, MultiplicationOverflowSameSignNegative) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
using C = std::common_type_t<T1, T2>;
if constexpr (std::is_signed_v<T1> && std::is_signed_v<T2>) {
CheckedInt<T1> a(std::numeric_limits<T1>::lowest());
CheckedInt<T2> b(std::numeric_limits<T2>::lowest());
EXPECT_EQ((a * b).Overflow(),
sizeof(C) == sizeof(T1) || sizeof(C) == sizeof(T2));
} else {
GTEST_SUCCEED();
}
}
TYPED_TEST(CheckedIntTypedTest, DivisionOverflowIsDetected) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
using C = std::common_type_t<T1, T2>;
if constexpr (std::is_signed_v<T1> && std::is_signed_v<T2> &&
std::is_same_v<T1, C>) {
CheckedInt<T1> a(std::numeric_limits<T1>::lowest());
CheckedInt<T2> b(-1);
EXPECT_TRUE((a / b).Overflow()) << +a.Value() << " / " << +b.Value();
} else {
GTEST_SUCCEED();
}
}
TYPED_TEST(CheckedIntTypedTest, MixedWithStandardIntegralTypesCompiles) {
using T1 = typename TypeParam::first_type;
CheckedInt<T1> a(10);
int b = 5;
auto add1 = a + b;
EXPECT_EQ(add1.Value(), 15);
EXPECT_FALSE(add1.Overflow());
auto add2 = b + a;
EXPECT_EQ(add2.Value(), 15);
EXPECT_FALSE(add2.Overflow());
auto sub1 = a - b;
EXPECT_EQ(sub1.Value(), 5);
EXPECT_FALSE(sub1.Overflow());
auto sub2 = b - a;
if constexpr (std::is_signed_v<std::common_type_t<int, T1>>) {
EXPECT_EQ(sub2.Value(), -5);
EXPECT_FALSE(sub2.Overflow());
} else {
EXPECT_TRUE(sub2.Overflow());
}
auto mul1 = a * b;
EXPECT_EQ(mul1.Value(), 50);
EXPECT_FALSE(mul1.Overflow());
auto mul2 = b * a;
EXPECT_EQ(mul2.Value(), 50);
EXPECT_FALSE(mul2.Overflow());
auto div1 = a / b;
EXPECT_EQ(div1.Value(), 2);
EXPECT_FALSE(div1.Overflow());
auto div2 = b / a;
EXPECT_EQ(div2.Value(), 0);
EXPECT_FALSE(div2.Overflow());
}
TYPED_TEST(CheckedIntTypedTest, CompoundAssignmentOperators) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
CheckedInt<T1> a(10);
CheckedInt<T2> b(2);
a += b;
EXPECT_EQ(a.Value(), 12);
EXPECT_FALSE(a.Overflow());
a -= b;
EXPECT_EQ(a.Value(), 10);
EXPECT_FALSE(a.Overflow());
a *= b;
EXPECT_EQ(a.Value(), 20);
EXPECT_FALSE(a.Overflow());
a /= b;
EXPECT_EQ(a.Value(), 10);
EXPECT_FALSE(a.Overflow());
int c = 2;
a += c;
EXPECT_EQ(a.Value(), 12);
EXPECT_FALSE(a.Overflow());
a -= c;
EXPECT_EQ(a.Value(), 10);
EXPECT_FALSE(a.Overflow());
a *= c;
EXPECT_EQ(a.Value(), 20);
EXPECT_FALSE(a.Overflow());
a /= c;
EXPECT_EQ(a.Value(), 10);
EXPECT_FALSE(a.Overflow());
}
TYPED_TEST(CheckedIntTypedTest, ComparisonOperators) {
using T1 = typename TypeParam::first_type;
using T2 = typename TypeParam::second_type;
CheckedInt<T1> a(10);
CheckedInt<T2> b(10);
CheckedInt<T2> c(12);
CheckedInt<T1> d(8);
EXPECT_TRUE(a == b);
EXPECT_FALSE(a == c);
EXPECT_TRUE(a != c);
EXPECT_FALSE(a != b);
EXPECT_TRUE(a < c);
EXPECT_FALSE(a < b);
EXPECT_FALSE(a < d);
EXPECT_TRUE(a <= c);
EXPECT_TRUE(a <= b);
EXPECT_FALSE(a <= d);
EXPECT_TRUE(c > a);
EXPECT_FALSE(b > a);
EXPECT_FALSE(d > a);
EXPECT_TRUE(c >= a);
EXPECT_TRUE(b >= a);
EXPECT_FALSE(d >= a);
// Mixed type comparisons
// NOLINTBEGIN(readability/check): We are testing the operators so we don't
// want to proxy the check through EXPECT_EQ/NE/GT/GE/LT/LE.
EXPECT_TRUE(a == 10);
EXPECT_TRUE(10 == a);
EXPECT_FALSE(a == 12);
EXPECT_TRUE(a != 12);
EXPECT_TRUE(a < 12);
EXPECT_TRUE(8 < a);
EXPECT_TRUE(a <= 10);
EXPECT_TRUE(10 <= a);
EXPECT_TRUE(a > 8);
EXPECT_TRUE(12 > a);
EXPECT_TRUE(a >= 10);
EXPECT_TRUE(10 >= a);
// NOLINTEND(readability/check)
}
TEST(CheckedIntSpecificTest, ConstructorMixedSignBoundsCheck) {
// Assigning a small unsigned value to a signed type should not overflow.
// Previously, this failed because unsigned `12` was compared to `INT_MIN`,
// which promoted `INT_MIN` to a huge unsigned value, causing `12 <
// INT_MIN_PROMOTED` to be true.
unsigned int u_val = 12;
CheckedInt<int> a(u_val);
EXPECT_FALSE(a.Overflow());
EXPECT_EQ(a.Value(), 12);
// Assigning a positive signed value to an unsigned type should not overflow.
int s_val = 12;
CheckedInt<unsigned int> b(s_val);
EXPECT_FALSE(b.Overflow());
EXPECT_EQ(b.Value(), 12u);
// Assigning a negative signed value to an unsigned type should overflow.
int s_neg_val = -1;
CheckedInt<unsigned int> c(s_neg_val);
EXPECT_TRUE(c.Overflow());
}
TEST(CheckedIntSpecificTest, MultiplicationEdgeCases) {
// These cases are edge cases for the fallback 64-bit multiplication
// overflow checks.
CheckedInt<int64_t> a_signed(0xFFFFFFFFLL);
CheckedInt<int64_t> b_signed(0x100000001LL);
EXPECT_TRUE((a_signed * b_signed).Overflow());
CheckedInt<uint64_t> a_unsigned(0xFFFFFFFFULL);
CheckedInt<uint64_t> b_unsigned(0x100000001ULL);
EXPECT_FALSE((a_unsigned * b_unsigned).Overflow());
EXPECT_EQ((a_unsigned * b_unsigned).Value(), 0xFFFFFFFFFFFFFFFFULL);
CheckedInt<int64_t> neg_one(-1);
CheckedInt<int64_t> five(5);
EXPECT_FALSE((neg_one * five).Overflow());
EXPECT_EQ((neg_one * five).Value(), -5);
EXPECT_FALSE((five * neg_one).Overflow());
EXPECT_EQ((five * neg_one).Value(), -5);
CheckedInt<int64_t> int32_min(-2147483648LL);
EXPECT_FALSE((int32_min * neg_one).Overflow());
EXPECT_EQ((int32_min * neg_one).Value(), 2147483648LL);
CheckedInt<int64_t> neg_two(-2);
EXPECT_FALSE((int32_min * neg_two).Overflow());
EXPECT_EQ((int32_min * neg_two).Value(), 4294967296LL);
CheckedInt<int64_t> int64_max(std::numeric_limits<int64_t>::max());
CheckedInt<int64_t> int64_min(std::numeric_limits<int64_t>::lowest());
CheckedInt<int64_t> one(1);
EXPECT_FALSE((int64_max * one).Overflow());
EXPECT_TRUE((int64_max * five).Overflow());
EXPECT_FALSE((int64_min * one).Overflow());
EXPECT_TRUE((int64_min * neg_one).Overflow());
EXPECT_TRUE((int64_min * five).Overflow());
}
TEST(CheckedIntSpecificTest, DivisionMixedSignOverflow) {
CheckedInt<int32_t> a(-10);
CheckedInt<uint32_t> b(2);
CheckedInt c = a / b;
EXPECT_TRUE(c.Overflow());
}
} // namespace
} // namespace tflite