| //===- llvm/unittest/ADT/OptionalTest.cpp - Optional unit tests -----------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/Optional.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| |
| 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; |
| |
| // Test fixture |
| class OptionalTest : public testing::Test { |
| }; |
| |
| TEST_F(OptionalTest, NonDefaultConstructibleTest) { |
| Optional<NonDefaultConstructible> O; |
| EXPECT_FALSE(O); |
| } |
| |
| TEST_F(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_F(OptionalTest, InitializationLeakTest) { |
| NonDefaultConstructible::ResetCounts(); |
| Optional<NonDefaultConstructible>(NonDefaultConstructible(3)); |
| EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); |
| EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); |
| EXPECT_EQ(2u, NonDefaultConstructible::Destructions); |
| } |
| |
| TEST_F(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_F(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_F(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_F(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_F(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_F(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; |
| |
| TEST_F(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; |
| |
| TEST_F(OptionalTest, MoveOnlyNull) { |
| MoveOnly::ResetCounts(); |
| Optional<MoveOnly> O; |
| EXPECT_EQ(0u, MoveOnly::MoveConstructions); |
| EXPECT_EQ(0u, MoveOnly::MoveAssignments); |
| EXPECT_EQ(0u, MoveOnly::Destructions); |
| } |
| |
| TEST_F(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_F(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_F(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_F(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_F(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_F(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; |
| |
| TEST_F(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); |
| } |
| |
| #if LLVM_HAS_RVALUE_REFERENCE_THIS |
| |
| TEST_F(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_F(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_F(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_F(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_F(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_F(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_F(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); |
| } |
| |
| #if __has_feature(is_trivially_copyable) && defined(_LIBCPP_VERSION) |
| static_assert(std::is_trivially_copyable<Optional<int>>::value, |
| "Should be trivially copyable"); |
| static_assert( |
| !std::is_trivially_copyable<Optional<NonDefaultConstructible>>::value, |
| "Shouldn't be trivially copyable"); |
| #endif |
| |
| } // end anonymous namespace |
| |