//===- SequenceTest.cpp - Unit tests for a sequence abstraciton -----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/Sequence.h" #include "llvm/ADT/STLExtras.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include using namespace llvm; using testing::ElementsAre; using testing::IsEmpty; namespace { using detail::canTypeFitValue; using detail::CheckedInt; using IntegralTypes = testing::Types; template class StrongIntTest : public testing::Test {}; TYPED_TEST_SUITE(StrongIntTest, IntegralTypes, ); TYPED_TEST(StrongIntTest, Operations) { using T = TypeParam; auto Max = std::numeric_limits::max(); auto Min = std::numeric_limits::min(); // We bail out for types that are not entirely representable within intmax_t. if (!canTypeFitValue(Max) || !canTypeFitValue(Min)) return; // All representable values convert back and forth. EXPECT_EQ(CheckedInt::from(Min).template to(), Min); EXPECT_EQ(CheckedInt::from(Max).template to(), Max); // Addition -2, -1, 0, 1, 2. const T Expected = Max / 2; const CheckedInt Actual = CheckedInt::from(Expected); EXPECT_EQ((Actual + -2).template to(), Expected - 2); EXPECT_EQ((Actual + -1).template to(), Expected - 1); EXPECT_EQ((Actual + 0).template to(), Expected); EXPECT_EQ((Actual + 1).template to(), Expected + 1); EXPECT_EQ((Actual + 2).template to(), Expected + 2); // EQ/NEQ EXPECT_EQ(Actual, Actual); EXPECT_NE(Actual, Actual + 1); // Difference EXPECT_EQ(Actual - Actual, 0); EXPECT_EQ((Actual + 1) - Actual, 1); EXPECT_EQ(Actual - (Actual + 2), -2); } #if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) TEST(StrongIntDeathTest, OutOfBounds) { // Values above 'INTMAX_MAX' are not representable. EXPECT_DEATH(CheckedInt::from(INTMAX_MAX + 1ULL), "Out of bounds"); EXPECT_DEATH(CheckedInt::from(UINTMAX_MAX), "Out of bounds"); // Casting to narrower type asserts when out of bounds. EXPECT_DEATH(CheckedInt::from(-1).to(), "Out of bounds"); EXPECT_DEATH(CheckedInt::from(256).to(), "Out of bounds"); // Operations leading to intmax_t overflow assert. EXPECT_DEATH(CheckedInt::from(INTMAX_MAX) + 1, "Out of bounds"); EXPECT_DEATH(CheckedInt::from(INTMAX_MIN) + -1, "Out of bounds"); EXPECT_DEATH(CheckedInt::from(INTMAX_MIN) - CheckedInt::from(INTMAX_MAX), "Out of bounds"); } #endif TEST(SafeIntIteratorTest, Operations) { detail::SafeIntIterator Forward(0); detail::SafeIntIterator Reverse(0); const auto SetToZero = [&]() { Forward = detail::SafeIntIterator(0); Reverse = detail::SafeIntIterator(0); }; // Equality / Comparisons SetToZero(); EXPECT_EQ(Forward, Forward); EXPECT_LT(Forward - 1, Forward); EXPECT_LE(Forward, Forward); EXPECT_LE(Forward - 1, Forward); EXPECT_GT(Forward + 1, Forward); EXPECT_GE(Forward, Forward); EXPECT_GE(Forward + 1, Forward); EXPECT_EQ(Reverse, Reverse); EXPECT_LT(Reverse - 1, Reverse); EXPECT_LE(Reverse, Reverse); EXPECT_LE(Reverse - 1, Reverse); EXPECT_GT(Reverse + 1, Reverse); EXPECT_GE(Reverse, Reverse); EXPECT_GE(Reverse + 1, Reverse); // Dereference SetToZero(); EXPECT_EQ(*Forward, 0); EXPECT_EQ(*Reverse, 0); // Indexing SetToZero(); EXPECT_EQ(Forward[2], 2); EXPECT_EQ(Reverse[2], -2); // Pre-increment SetToZero(); ++Forward; EXPECT_EQ(*Forward, 1); ++Reverse; EXPECT_EQ(*Reverse, -1); // Pre-decrement SetToZero(); --Forward; EXPECT_EQ(*Forward, -1); --Reverse; EXPECT_EQ(*Reverse, 1); // Post-increment SetToZero(); EXPECT_EQ(*(Forward++), 0); EXPECT_EQ(*Forward, 1); EXPECT_EQ(*(Reverse++), 0); EXPECT_EQ(*Reverse, -1); // Post-decrement SetToZero(); EXPECT_EQ(*(Forward--), 0); EXPECT_EQ(*Forward, -1); EXPECT_EQ(*(Reverse--), 0); EXPECT_EQ(*Reverse, 1); // Compound assignment operators SetToZero(); Forward += 1; EXPECT_EQ(*Forward, 1); Reverse += 1; EXPECT_EQ(*Reverse, -1); SetToZero(); Forward -= 2; EXPECT_EQ(*Forward, -2); Reverse -= 2; EXPECT_EQ(*Reverse, 2); // Arithmetic SetToZero(); EXPECT_EQ(*(Forward + 3), 3); EXPECT_EQ(*(Reverse + 3), -3); SetToZero(); EXPECT_EQ(*(Forward - 4), -4); EXPECT_EQ(*(Reverse - 4), 4); // Difference SetToZero(); EXPECT_EQ(Forward - Forward, 0); EXPECT_EQ(Reverse - Reverse, 0); EXPECT_EQ((Forward + 1) - Forward, 1); EXPECT_EQ(Forward - (Forward + 1), -1); EXPECT_EQ((Reverse + 1) - Reverse, 1); EXPECT_EQ(Reverse - (Reverse + 1), -1); } TEST(SequenceTest, Iteration) { EXPECT_THAT(seq(5), ElementsAre(0, 1, 2, 3, 4)); EXPECT_THAT(seq(-4, 5), ElementsAre(-4, -3, -2, -1, 0, 1, 2, 3, 4)); EXPECT_THAT(reverse(seq(-4, 5)), ElementsAre(4, 3, 2, 1, 0, -1, -2, -3, -4)); EXPECT_THAT(seq_inclusive(-4, 5), ElementsAre(-4, -3, -2, -1, 0, 1, 2, 3, 4, 5)); EXPECT_THAT(reverse(seq_inclusive(-4, 5)), ElementsAre(5, 4, 3, 2, 1, 0, -1, -2, -3, -4)); } TEST(SequenceTest, Distance) { const auto Forward = seq(0, 10); EXPECT_EQ(std::distance(Forward.begin(), Forward.end()), 10); EXPECT_EQ(std::distance(Forward.rbegin(), Forward.rend()), 10); } TEST(SequenceTest, Dereference) { const auto Forward = seq(0, 10).begin(); EXPECT_EQ(Forward[0], 0); EXPECT_EQ(Forward[2], 2); const auto Backward = seq(0, 10).rbegin(); EXPECT_EQ(Backward[0], 9); EXPECT_EQ(Backward[2], 7); } enum UntypedEnum { A = 3 }; enum TypedEnum : uint32_t { B = 3 }; namespace X { enum class ScopedEnum : uint16_t { C = 3 }; } // namespace X struct S { enum NestedEnum { D = 4 }; enum NestedEnum2 { E = 5 }; private: enum NestedEnum3 { F = 6 }; friend struct llvm::enum_iteration_traits; public: static auto getNestedEnum3() { return NestedEnum3::F; } }; } // namespace namespace llvm { template <> struct enum_iteration_traits { static constexpr bool is_iterable = true; }; template <> struct enum_iteration_traits { static constexpr bool is_iterable = true; }; template <> struct enum_iteration_traits { static constexpr bool is_iterable = true; }; template <> struct enum_iteration_traits { static constexpr bool is_iterable = true; }; template <> struct enum_iteration_traits { static constexpr bool is_iterable = true; }; } // namespace llvm namespace { TEST(StrongIntTest, Enums) { EXPECT_EQ(CheckedInt::from(A).to(), A); EXPECT_EQ(CheckedInt::from(B).to(), B); EXPECT_EQ(CheckedInt::from(X::ScopedEnum::C).to(), X::ScopedEnum::C); } TEST(SequenceTest, IterableEnums) { EXPECT_THAT(enum_seq(UntypedEnum::A, UntypedEnum::A), IsEmpty()); EXPECT_THAT(enum_seq_inclusive(UntypedEnum::A, UntypedEnum::A), ElementsAre(UntypedEnum::A)); EXPECT_THAT(enum_seq(TypedEnum::B, TypedEnum::B), IsEmpty()); EXPECT_THAT(enum_seq_inclusive(TypedEnum::B, TypedEnum::B), ElementsAre(TypedEnum::B)); EXPECT_THAT(enum_seq(X::ScopedEnum::C, X::ScopedEnum::C), IsEmpty()); EXPECT_THAT(enum_seq_inclusive(X::ScopedEnum::C, X::ScopedEnum::C), ElementsAre(X::ScopedEnum::C)); EXPECT_THAT(enum_seq_inclusive(S::NestedEnum::D, S::NestedEnum::D), ElementsAre(S::NestedEnum::D)); EXPECT_THAT(enum_seq_inclusive(S::getNestedEnum3(), S::getNestedEnum3()), ElementsAre(S::getNestedEnum3())); } TEST(SequenceTest, NonIterableEnums) { EXPECT_THAT(enum_seq(S::NestedEnum2::E, S::NestedEnum2::E, force_iteration_on_noniterable_enum), IsEmpty()); EXPECT_THAT(enum_seq_inclusive(S::NestedEnum2::E, S::NestedEnum2::E, force_iteration_on_noniterable_enum), ElementsAre(S::NestedEnum2::E)); // Check that this also works with enums marked as iterable. EXPECT_THAT(enum_seq(UntypedEnum::A, UntypedEnum::A, force_iteration_on_noniterable_enum), IsEmpty()); EXPECT_THAT(enum_seq_inclusive(UntypedEnum::A, UntypedEnum::A, force_iteration_on_noniterable_enum), ElementsAre(UntypedEnum::A)); } // Reproducer for https://github.com/llvm/llvm-project/issues/61122 TEST(SequenceTest, CorrectReferenceType) { std::vector vals = {1, 2, 3}; detail::SafeIntIterator begin(4); detail::SafeIntIterator end(6); vals.insert(vals.end(), begin, end); EXPECT_THAT(vals, ElementsAre(1, 2, 3, 4, 5)); } } // namespace