| //===- llvm/unittest/ADT/OptionalTest.cpp - Optional unit tests -----------===// |
| // |
| // 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/Optional.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringMap.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "gtest/gtest-spi.h" |
| #include "gtest/gtest.h" |
| |
| #include <array> |
| |
| |
| using namespace llvm; |
| |
| static_assert(std::is_trivially_copyable<Optional<int>>::value, |
| "trivially copyable"); |
| |
| static_assert(std::is_trivially_copyable<Optional<std::array<int, 3>>>::value, |
| "trivially copyable"); |
| |
| void OptionalWorksInConstexpr() { |
| constexpr auto x1 = Optional<int>(); |
| constexpr Optional<int> x2{}; |
| static_assert(!x1.hasValue() && !x2.hasValue(), |
| "Default construction and hasValue() are contexpr"); |
| constexpr auto y1 = Optional<int>(3); |
| constexpr Optional<int> y2{3}; |
| static_assert(y1.getValue() == y2.getValue() && y1.getValue() == 3, |
| "Construction with value and getValue() are constexpr"); |
| static_assert(Optional<int>{3} >= 2 && Optional<int>{1} < Optional<int>{2}, |
| "Comparisons work in constexpr"); |
| } |
| |
| namespace { |
| |
| struct NonDefaultConstructible { |
| static unsigned CopyConstructions; |
| static unsigned Destructions; |
| static unsigned CopyAssignments; |
| explicit NonDefaultConstructible(int) { |
| } |
| NonDefaultConstructible(const NonDefaultConstructible&) { |
| ++CopyConstructions; |
| } |
| NonDefaultConstructible &operator=(const NonDefaultConstructible&) { |
| ++CopyAssignments; |
| return *this; |
| } |
| ~NonDefaultConstructible() { |
| ++Destructions; |
| } |
| static void ResetCounts() { |
| CopyConstructions = 0; |
| Destructions = 0; |
| CopyAssignments = 0; |
| } |
| }; |
| |
| unsigned NonDefaultConstructible::CopyConstructions = 0; |
| unsigned NonDefaultConstructible::Destructions = 0; |
| unsigned NonDefaultConstructible::CopyAssignments = 0; |
| |
| static_assert( |
| !std::is_trivially_copyable<Optional<NonDefaultConstructible>>::value, |
| "not trivially copyable"); |
| |
| TEST(OptionalTest, NonDefaultConstructibleTest) { |
| Optional<NonDefaultConstructible> O; |
| EXPECT_FALSE(O); |
| } |
| |
| TEST(OptionalTest, ResetTest) { |
| NonDefaultConstructible::ResetCounts(); |
| Optional<NonDefaultConstructible> O(NonDefaultConstructible(3)); |
| EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(1u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| O.reset(); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(1u, NonDefaultConstructible::Destructions); |
| } |
| |
| TEST(OptionalTest, InitializationLeakTest) { |
| NonDefaultConstructible::ResetCounts(); |
| Optional<NonDefaultConstructible>(NonDefaultConstructible(3)); |
| EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(2u, NonDefaultConstructible::Destructions); |
| } |
| |
| TEST(OptionalTest, CopyConstructionTest) { |
| NonDefaultConstructible::ResetCounts(); |
| { |
| Optional<NonDefaultConstructible> A(NonDefaultConstructible(3)); |
| EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(1u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| Optional<NonDefaultConstructible> B(A); |
| EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(0u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| } |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(2u, NonDefaultConstructible::Destructions); |
| } |
| |
| TEST(OptionalTest, ConstructingCopyAssignmentTest) { |
| NonDefaultConstructible::ResetCounts(); |
| { |
| Optional<NonDefaultConstructible> A(NonDefaultConstructible(3)); |
| Optional<NonDefaultConstructible> B; |
| EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(1u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| B = A; |
| EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(0u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| } |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(2u, NonDefaultConstructible::Destructions); |
| } |
| |
| TEST(OptionalTest, CopyingCopyAssignmentTest) { |
| NonDefaultConstructible::ResetCounts(); |
| { |
| Optional<NonDefaultConstructible> A(NonDefaultConstructible(3)); |
| Optional<NonDefaultConstructible> B(NonDefaultConstructible(4)); |
| EXPECT_EQ(2u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(2u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| B = A; |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(1u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(0u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| } |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(2u, NonDefaultConstructible::Destructions); |
| } |
| |
| TEST(OptionalTest, DeletingCopyAssignmentTest) { |
| NonDefaultConstructible::ResetCounts(); |
| { |
| Optional<NonDefaultConstructible> A; |
| Optional<NonDefaultConstructible> B(NonDefaultConstructible(3)); |
| EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(1u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| B = A; |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(1u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| } |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(0u, NonDefaultConstructible::Destructions); |
| } |
| |
| TEST(OptionalTest, NullCopyConstructionTest) { |
| NonDefaultConstructible::ResetCounts(); |
| { |
| Optional<NonDefaultConstructible> A; |
| Optional<NonDefaultConstructible> B; |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(0u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| B = A; |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(0u, NonDefaultConstructible::Destructions); |
| NonDefaultConstructible::ResetCounts(); |
| } |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(0u, NonDefaultConstructible::Destructions); |
| } |
| |
| TEST(OptionalTest, GetValueOr) { |
| Optional<int> A; |
| EXPECT_EQ(42, A.getValueOr(42)); |
| |
| A = 5; |
| EXPECT_EQ(5, A.getValueOr(42)); |
| } |
| |
| struct MultiArgConstructor { |
| int x, y; |
| MultiArgConstructor(int x, int y) : x(x), y(y) {} |
| explicit MultiArgConstructor(int x, bool positive) |
| : x(x), y(positive ? x : -x) {} |
| |
| MultiArgConstructor(const MultiArgConstructor &) = delete; |
| MultiArgConstructor(MultiArgConstructor &&) = delete; |
| MultiArgConstructor &operator=(const MultiArgConstructor &) = delete; |
| MultiArgConstructor &operator=(MultiArgConstructor &&) = delete; |
| |
| static unsigned Destructions; |
| ~MultiArgConstructor() { |
| ++Destructions; |
| } |
| static void ResetCounts() { |
| Destructions = 0; |
| } |
| }; |
| unsigned MultiArgConstructor::Destructions = 0; |
| |
| static_assert(!std::is_trivially_copyable<Optional<MultiArgConstructor>>::value, |
| "not trivially copyable"); |
| |
| TEST(OptionalTest, Emplace) { |
| MultiArgConstructor::ResetCounts(); |
| Optional<MultiArgConstructor> A; |
| |
| A.emplace(1, 2); |
| EXPECT_TRUE(A.hasValue()); |
| EXPECT_EQ(1, A->x); |
| EXPECT_EQ(2, A->y); |
| EXPECT_EQ(0u, MultiArgConstructor::Destructions); |
| |
| A.emplace(5, false); |
| EXPECT_TRUE(A.hasValue()); |
| EXPECT_EQ(5, A->x); |
| EXPECT_EQ(-5, A->y); |
| EXPECT_EQ(1u, MultiArgConstructor::Destructions); |
| } |
| |
| struct MoveOnly { |
| static unsigned MoveConstructions; |
| static unsigned Destructions; |
| static unsigned MoveAssignments; |
| int val; |
| explicit MoveOnly(int val) : val(val) { |
| } |
| MoveOnly(MoveOnly&& other) { |
| val = other.val; |
| ++MoveConstructions; |
| } |
| MoveOnly &operator=(MoveOnly&& other) { |
| val = other.val; |
| ++MoveAssignments; |
| return *this; |
| } |
| ~MoveOnly() { |
| ++Destructions; |
| } |
| static void ResetCounts() { |
| MoveConstructions = 0; |
| Destructions = 0; |
| MoveAssignments = 0; |
| } |
| }; |
| |
| unsigned MoveOnly::MoveConstructions = 0; |
| unsigned MoveOnly::Destructions = 0; |
| unsigned MoveOnly::MoveAssignments = 0; |
| |
| static_assert(!std::is_trivially_copyable<Optional<MoveOnly>>::value, |
| "not trivially copyable"); |
| |
| TEST(OptionalTest, MoveOnlyNull) { |
| MoveOnly::ResetCounts(); |
| Optional<MoveOnly> O; |
| EXPECT_EQ(0u, MoveOnly::MoveConstructions); |
| EXPECT_EQ(0u, MoveOnly::MoveAssignments); |
| EXPECT_EQ(0u, MoveOnly::Destructions); |
| } |
| |
| TEST(OptionalTest, MoveOnlyConstruction) { |
| MoveOnly::ResetCounts(); |
| Optional<MoveOnly> O(MoveOnly(3)); |
| EXPECT_TRUE((bool)O); |
| EXPECT_EQ(3, O->val); |
| EXPECT_EQ(1u, MoveOnly::MoveConstructions); |
| EXPECT_EQ(0u, MoveOnly::MoveAssignments); |
| EXPECT_EQ(1u, MoveOnly::Destructions); |
| } |
| |
| TEST(OptionalTest, MoveOnlyMoveConstruction) { |
| Optional<MoveOnly> A(MoveOnly(3)); |
| MoveOnly::ResetCounts(); |
| Optional<MoveOnly> B(std::move(A)); |
| EXPECT_TRUE((bool)A); |
| EXPECT_TRUE((bool)B); |
| EXPECT_EQ(3, B->val); |
| EXPECT_EQ(1u, MoveOnly::MoveConstructions); |
| EXPECT_EQ(0u, MoveOnly::MoveAssignments); |
| EXPECT_EQ(0u, MoveOnly::Destructions); |
| } |
| |
| TEST(OptionalTest, MoveOnlyAssignment) { |
| MoveOnly::ResetCounts(); |
| Optional<MoveOnly> O; |
| O = MoveOnly(3); |
| EXPECT_TRUE((bool)O); |
| EXPECT_EQ(3, O->val); |
| EXPECT_EQ(1u, MoveOnly::MoveConstructions); |
| EXPECT_EQ(0u, MoveOnly::MoveAssignments); |
| EXPECT_EQ(1u, MoveOnly::Destructions); |
| } |
| |
| TEST(OptionalTest, MoveOnlyInitializingAssignment) { |
| Optional<MoveOnly> A(MoveOnly(3)); |
| Optional<MoveOnly> B; |
| MoveOnly::ResetCounts(); |
| B = std::move(A); |
| EXPECT_TRUE((bool)A); |
| EXPECT_TRUE((bool)B); |
| EXPECT_EQ(3, B->val); |
| EXPECT_EQ(1u, MoveOnly::MoveConstructions); |
| EXPECT_EQ(0u, MoveOnly::MoveAssignments); |
| EXPECT_EQ(0u, MoveOnly::Destructions); |
| } |
| |
| TEST(OptionalTest, MoveOnlyNullingAssignment) { |
| Optional<MoveOnly> A; |
| Optional<MoveOnly> B(MoveOnly(3)); |
| MoveOnly::ResetCounts(); |
| B = std::move(A); |
| EXPECT_FALSE((bool)A); |
| EXPECT_FALSE((bool)B); |
| EXPECT_EQ(0u, MoveOnly::MoveConstructions); |
| EXPECT_EQ(0u, MoveOnly::MoveAssignments); |
| EXPECT_EQ(1u, MoveOnly::Destructions); |
| } |
| |
| TEST(OptionalTest, MoveOnlyAssigningAssignment) { |
| Optional<MoveOnly> A(MoveOnly(3)); |
| Optional<MoveOnly> B(MoveOnly(4)); |
| MoveOnly::ResetCounts(); |
| B = std::move(A); |
| EXPECT_TRUE((bool)A); |
| EXPECT_TRUE((bool)B); |
| EXPECT_EQ(3, B->val); |
| EXPECT_EQ(0u, MoveOnly::MoveConstructions); |
| EXPECT_EQ(1u, MoveOnly::MoveAssignments); |
| EXPECT_EQ(0u, MoveOnly::Destructions); |
| } |
| |
| struct Immovable { |
| static unsigned Constructions; |
| static unsigned Destructions; |
| int val; |
| explicit Immovable(int val) : val(val) { |
| ++Constructions; |
| } |
| ~Immovable() { |
| ++Destructions; |
| } |
| static void ResetCounts() { |
| Constructions = 0; |
| Destructions = 0; |
| } |
| private: |
| // This should disable all move/copy operations. |
| Immovable(Immovable&& other) = delete; |
| }; |
| |
| unsigned Immovable::Constructions = 0; |
| unsigned Immovable::Destructions = 0; |
| |
| static_assert(!std::is_trivially_copyable<Optional<Immovable>>::value, |
| "not trivially copyable"); |
| |
| TEST(OptionalTest, ImmovableEmplace) { |
| Optional<Immovable> A; |
| Immovable::ResetCounts(); |
| A.emplace(4); |
| EXPECT_TRUE((bool)A); |
| EXPECT_EQ(4, A->val); |
| EXPECT_EQ(1u, Immovable::Constructions); |
| EXPECT_EQ(0u, Immovable::Destructions); |
| } |
| |
| // Craft a class which is_trivially_copyable, but not |
| // is_trivially_copy_constructible. |
| struct NonTCopy { |
| NonTCopy() = default; |
| |
| // Delete the volatile copy constructor to engage the "rule of 3" and delete |
| // any unspecified copy assignment or constructor. |
| NonTCopy(volatile NonTCopy const &) = delete; |
| |
| // Leave the non-volatile default copy constructor unspecified (deleted by |
| // rule of 3) |
| |
| // This template can serve as the copy constructor, but isn't chosen |
| // by =default in a class with a 'NonTCopy' member. |
| template <typename Self = NonTCopy> |
| NonTCopy(Self const &Other) : Val(Other.Val) {} |
| |
| NonTCopy &operator=(NonTCopy const &) = default; |
| |
| int Val{0}; |
| }; |
| |
| #if defined(_MSC_VER) && _MSC_VER >= 1927 && !defined(__clang__) |
| // Currently only true on recent MSVC releases. |
| static_assert(std::is_trivially_copyable<NonTCopy>::value, |
| "Expect NonTCopy to be trivially copyable"); |
| |
| static_assert(!std::is_trivially_copy_constructible<NonTCopy>::value, |
| "Expect NonTCopy not to be trivially copy constructible."); |
| #endif // defined(_MSC_VER) && _MSC_VER >= 1927 |
| |
| TEST(OptionalTest, DeletedCopyConstructor) { |
| |
| // Expect compile to fail if 'trivial' version of |
| // optional_detail::OptionalStorage is chosen. |
| using NonTCopyOptT = Optional<NonTCopy>; |
| NonTCopyOptT NonTCopy1; |
| |
| // Check that the Optional can be copy constructed. |
| NonTCopyOptT NonTCopy2{NonTCopy1}; |
| |
| // Check that the Optional can be copy assigned. |
| NonTCopy1 = NonTCopy2; |
| } |
| |
| // Craft a class which is_trivially_copyable, but not |
| // is_trivially_copy_assignable. |
| class NonTAssign { |
| public: |
| NonTAssign() = default; |
| NonTAssign(NonTAssign const &) = default; |
| |
| // Delete the volatile copy assignment to engage the "rule of 3" and delete |
| // any unspecified copy assignment or constructor. |
| NonTAssign &operator=(volatile NonTAssign const &) = delete; |
| |
| // Leave the non-volatile default copy assignment unspecified (deleted by rule |
| // of 3). |
| |
| // This template can serve as the copy assignment, but isn't chosen |
| // by =default in a class with a 'NonTAssign' member. |
| template <typename Self = NonTAssign> |
| NonTAssign &operator=(Self const &Other) { |
| A = Other.A; |
| return *this; |
| } |
| |
| int A{0}; |
| }; |
| |
| #if defined(_MSC_VER) && _MSC_VER >= 1927 && !defined(__clang__) |
| // Currently only true on recent MSVC releases. |
| static_assert(std::is_trivially_copyable<NonTAssign>::value, |
| "Expect NonTAssign to be trivially copyable"); |
| |
| static_assert(!std::is_trivially_copy_assignable<NonTAssign>::value, |
| "Expect NonTAssign not to be trivially assignable."); |
| #endif // defined(_MSC_VER) && _MSC_VER >= 1927 |
| |
| TEST(OptionalTest, DeletedCopyAssignment) { |
| |
| // Expect compile to fail if 'trivial' version of |
| // optional_detail::OptionalStorage is chosen. |
| using NonTAssignOptT = Optional<NonTAssign>; |
| NonTAssignOptT NonTAssign1; |
| |
| // Check that the Optional can be copy constructed. |
| NonTAssignOptT NonTAssign2{NonTAssign1}; |
| |
| // Check that the Optional can be copy assigned. |
| NonTAssign1 = NonTAssign2; |
| } |
| |
| struct NoTMove { |
| NoTMove() = default; |
| NoTMove(NoTMove const &) = default; |
| NoTMove &operator=(NoTMove const &) = default; |
| |
| // Delete move constructor / assignment. Compiler should fall-back to the |
| // trivial copy constructor / assignment in the trivial OptionalStorage |
| // specialization. |
| NoTMove(NoTMove &&) = delete; |
| NoTMove &operator=(NoTMove &&) = delete; |
| |
| int Val{0}; |
| }; |
| |
| TEST(OptionalTest, DeletedMoveConstructor) { |
| using NoTMoveOptT = Optional<NoTMove>; |
| |
| NoTMoveOptT NonTMove1; |
| NoTMoveOptT NonTMove2{std::move(NonTMove1)}; |
| |
| NonTMove1 = std::move(NonTMove2); |
| |
| static_assert( |
| std::is_trivially_copyable<NoTMoveOptT>::value, |
| "Expect Optional<NoTMove> to still use the trivial specialization " |
| "of OptionalStorage despite the deleted move constructor / assignment."); |
| } |
| |
| class NoCopyStringMap { |
| public: |
| NoCopyStringMap() = default; |
| |
| private: |
| llvm::StringMap<std::unique_ptr<int>> Map; |
| }; |
| |
| TEST(OptionalTest, DeletedCopyStringMap) { |
| // Old versions of gcc (7.3 and prior) instantiate the copy constructor when |
| // std::is_trivially_copyable is instantiated. This test will fail |
| // compilation if std::is_trivially_copyable is used in the OptionalStorage |
| // specialization condition by gcc <= 7.3. |
| Optional<NoCopyStringMap> TestInstantiation; |
| } |
| |
| #if LLVM_HAS_RVALUE_REFERENCE_THIS |
| |
| TEST(OptionalTest, MoveGetValueOr) { |
| Optional<MoveOnly> A; |
| |
| MoveOnly::ResetCounts(); |
| EXPECT_EQ(42, std::move(A).getValueOr(MoveOnly(42)).val); |
| EXPECT_EQ(1u, MoveOnly::MoveConstructions); |
| EXPECT_EQ(0u, MoveOnly::MoveAssignments); |
| EXPECT_EQ(2u, MoveOnly::Destructions); |
| |
| A = MoveOnly(5); |
| MoveOnly::ResetCounts(); |
| EXPECT_EQ(5, std::move(A).getValueOr(MoveOnly(42)).val); |
| EXPECT_EQ(1u, MoveOnly::MoveConstructions); |
| EXPECT_EQ(0u, MoveOnly::MoveAssignments); |
| EXPECT_EQ(2u, MoveOnly::Destructions); |
| } |
| |
| #endif // LLVM_HAS_RVALUE_REFERENCE_THIS |
| |
| struct EqualTo { |
| template <typename T, typename U> static bool apply(const T &X, const U &Y) { |
| return X == Y; |
| } |
| }; |
| |
| struct NotEqualTo { |
| template <typename T, typename U> static bool apply(const T &X, const U &Y) { |
| return X != Y; |
| } |
| }; |
| |
| struct Less { |
| template <typename T, typename U> static bool apply(const T &X, const U &Y) { |
| return X < Y; |
| } |
| }; |
| |
| struct Greater { |
| template <typename T, typename U> static bool apply(const T &X, const U &Y) { |
| return X > Y; |
| } |
| }; |
| |
| struct LessEqual { |
| template <typename T, typename U> static bool apply(const T &X, const U &Y) { |
| return X <= Y; |
| } |
| }; |
| |
| struct GreaterEqual { |
| template <typename T, typename U> static bool apply(const T &X, const U &Y) { |
| return X >= Y; |
| } |
| }; |
| |
| template <typename OperatorT, typename T> |
| void CheckRelation(const Optional<T> &Lhs, const Optional<T> &Rhs, |
| bool Expected) { |
| EXPECT_EQ(Expected, OperatorT::apply(Lhs, Rhs)); |
| |
| if (Lhs) |
| EXPECT_EQ(Expected, OperatorT::apply(*Lhs, Rhs)); |
| else |
| EXPECT_EQ(Expected, OperatorT::apply(None, Rhs)); |
| |
| if (Rhs) |
| EXPECT_EQ(Expected, OperatorT::apply(Lhs, *Rhs)); |
| else |
| EXPECT_EQ(Expected, OperatorT::apply(Lhs, None)); |
| } |
| |
| struct EqualityMock {}; |
| const Optional<EqualityMock> NoneEq, EqualityLhs((EqualityMock())), |
| EqualityRhs((EqualityMock())); |
| bool IsEqual; |
| |
| bool operator==(const EqualityMock &Lhs, const EqualityMock &Rhs) { |
| EXPECT_EQ(&*EqualityLhs, &Lhs); |
| EXPECT_EQ(&*EqualityRhs, &Rhs); |
| return IsEqual; |
| } |
| |
| TEST(OptionalTest, OperatorEqual) { |
| CheckRelation<EqualTo>(NoneEq, NoneEq, true); |
| CheckRelation<EqualTo>(NoneEq, EqualityRhs, false); |
| CheckRelation<EqualTo>(EqualityLhs, NoneEq, false); |
| |
| IsEqual = false; |
| CheckRelation<EqualTo>(EqualityLhs, EqualityRhs, IsEqual); |
| IsEqual = true; |
| CheckRelation<EqualTo>(EqualityLhs, EqualityRhs, IsEqual); |
| } |
| |
| TEST(OptionalTest, OperatorNotEqual) { |
| CheckRelation<NotEqualTo>(NoneEq, NoneEq, false); |
| CheckRelation<NotEqualTo>(NoneEq, EqualityRhs, true); |
| CheckRelation<NotEqualTo>(EqualityLhs, NoneEq, true); |
| |
| IsEqual = false; |
| CheckRelation<NotEqualTo>(EqualityLhs, EqualityRhs, !IsEqual); |
| IsEqual = true; |
| CheckRelation<NotEqualTo>(EqualityLhs, EqualityRhs, !IsEqual); |
| } |
| |
| struct InequalityMock {}; |
| const Optional<InequalityMock> NoneIneq, InequalityLhs((InequalityMock())), |
| InequalityRhs((InequalityMock())); |
| bool IsLess; |
| |
| bool operator<(const InequalityMock &Lhs, const InequalityMock &Rhs) { |
| EXPECT_EQ(&*InequalityLhs, &Lhs); |
| EXPECT_EQ(&*InequalityRhs, &Rhs); |
| return IsLess; |
| } |
| |
| TEST(OptionalTest, OperatorLess) { |
| CheckRelation<Less>(NoneIneq, NoneIneq, false); |
| CheckRelation<Less>(NoneIneq, InequalityRhs, true); |
| CheckRelation<Less>(InequalityLhs, NoneIneq, false); |
| |
| IsLess = false; |
| CheckRelation<Less>(InequalityLhs, InequalityRhs, IsLess); |
| IsLess = true; |
| CheckRelation<Less>(InequalityLhs, InequalityRhs, IsLess); |
| } |
| |
| TEST(OptionalTest, OperatorGreater) { |
| CheckRelation<Greater>(NoneIneq, NoneIneq, false); |
| CheckRelation<Greater>(NoneIneq, InequalityRhs, false); |
| CheckRelation<Greater>(InequalityLhs, NoneIneq, true); |
| |
| IsLess = false; |
| CheckRelation<Greater>(InequalityRhs, InequalityLhs, IsLess); |
| IsLess = true; |
| CheckRelation<Greater>(InequalityRhs, InequalityLhs, IsLess); |
| } |
| |
| TEST(OptionalTest, OperatorLessEqual) { |
| CheckRelation<LessEqual>(NoneIneq, NoneIneq, true); |
| CheckRelation<LessEqual>(NoneIneq, InequalityRhs, true); |
| CheckRelation<LessEqual>(InequalityLhs, NoneIneq, false); |
| |
| IsLess = false; |
| CheckRelation<LessEqual>(InequalityRhs, InequalityLhs, !IsLess); |
| IsLess = true; |
| CheckRelation<LessEqual>(InequalityRhs, InequalityLhs, !IsLess); |
| } |
| |
| TEST(OptionalTest, OperatorGreaterEqual) { |
| CheckRelation<GreaterEqual>(NoneIneq, NoneIneq, true); |
| CheckRelation<GreaterEqual>(NoneIneq, InequalityRhs, false); |
| CheckRelation<GreaterEqual>(InequalityLhs, NoneIneq, true); |
| |
| IsLess = false; |
| CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess); |
| IsLess = true; |
| CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess); |
| } |
| |
| struct ComparableAndStreamable { |
| friend bool operator==(ComparableAndStreamable, |
| ComparableAndStreamable) LLVM_ATTRIBUTE_USED { |
| return true; |
| } |
| |
| friend raw_ostream &operator<<(raw_ostream &OS, ComparableAndStreamable) { |
| return OS << "ComparableAndStreamable"; |
| } |
| |
| static Optional<ComparableAndStreamable> get() { |
| return ComparableAndStreamable(); |
| } |
| }; |
| |
| TEST(OptionalTest, StreamOperator) { |
| auto to_string = [](Optional<ComparableAndStreamable> O) { |
| SmallString<16> S; |
| raw_svector_ostream OS(S); |
| OS << O; |
| return S; |
| }; |
| EXPECT_EQ("ComparableAndStreamable", |
| to_string(ComparableAndStreamable::get())); |
| EXPECT_EQ("None", to_string(None)); |
| } |
| |
| struct Comparable { |
| friend bool operator==(Comparable, Comparable) LLVM_ATTRIBUTE_USED { |
| return true; |
| } |
| static Optional<Comparable> get() { return Comparable(); } |
| }; |
| |
| TEST(OptionalTest, UseInUnitTests) { |
| // Test that we invoke the streaming operators when pretty-printing values in |
| // EXPECT macros. |
| EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, ComparableAndStreamable::get()), |
| "Expected: llvm::None\n" |
| " Which is: None\n" |
| "To be equal to: ComparableAndStreamable::get()\n" |
| " Which is: ComparableAndStreamable"); |
| |
| // Test that it is still possible to compare objects which do not have a |
| // custom streaming operator. |
| EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, Comparable::get()), "object"); |
| } |
| |
| TEST(OptionalTest, HashValue) { |
| // Check that None, false, and true all hash differently. |
| Optional<bool> B, B0 = false, B1 = true; |
| EXPECT_NE(hash_value(B0), hash_value(B)); |
| EXPECT_NE(hash_value(B1), hash_value(B)); |
| EXPECT_NE(hash_value(B1), hash_value(B0)); |
| |
| // Check that None, 0, and 1 all hash differently. |
| Optional<int> I, I0 = 0, I1 = 1; |
| EXPECT_NE(hash_value(I0), hash_value(I)); |
| EXPECT_NE(hash_value(I1), hash_value(I)); |
| EXPECT_NE(hash_value(I1), hash_value(I0)); |
| |
| // Check None hash the same way regardless of type. |
| EXPECT_EQ(hash_value(B), hash_value(I)); |
| } |
| |
| } // end anonymous namespace |