/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * 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 #include #include #include #include using namespace apache::thrift::detail; struct TestStruct { using __fbthrift_cpp2_type = TestStruct; static constexpr bool __fbthrift_cpp2_is_union = false; TestStruct() : a(0) {} explicit TestStruct(int val) : a(val) {} void set_a(int b) { a = b; } int a; }; constexpr bool operator==(const TestStruct& lhs, const TestStruct& rhs) { return lhs.a == rhs.a; } constexpr bool operator==(const TestStruct& lhs, int rhs) { return lhs.a == rhs; } constexpr bool operator==(int lhs, const TestStruct& rhs) { return lhs == rhs.a; } constexpr bool operator<(const TestStruct& lhs, const TestStruct& rhs) { return lhs.a < rhs.a; } constexpr bool operator<(const TestStruct& lhs, int rhs) { return lhs.a < rhs; } constexpr bool operator<(int lhs, const TestStruct& rhs) { return lhs < rhs.a; } TEST(BoxedValuePtrTest, DefaultConstructor) { boxed_value_ptr a; EXPECT_FALSE(static_cast(a)); } TEST(BoxedValuePtrTest, Constructor) { boxed_value_ptr a(5); EXPECT_EQ(*a, 5); } TEST(BoxedValuePtrTest, CopyConstructor) { boxed_value_ptr a(5); boxed_value_ptr b(a); EXPECT_EQ(*b, 5); EXPECT_EQ(*a, 5); } TEST(BoxedValuePtrTest, CopyAssignment) { boxed_value_ptr a(5); boxed_value_ptr b; b = a; EXPECT_EQ(*b, 5); EXPECT_EQ(*a, 5); } TEST(BoxedValuePtrTest, MoveConstructor) { boxed_value_ptr a(5); boxed_value_ptr b(std::move(a)); EXPECT_EQ(*b, 5); } TEST(BoxedValuePtrTest, MoveAssignment) { boxed_value_ptr a(5); boxed_value_ptr b; b = std::move(a); EXPECT_EQ(*b, 5); } TEST(BoxedValuePtrTest, EmptyAssignment) { boxed_value_ptr a; boxed_value_ptr b(5); EXPECT_EQ(*b, 5); b = a; EXPECT_FALSE(static_cast(b)); } TEST(BoxedValuePtrTest, Emplace) { boxed_value_ptr a; a.emplace(5); EXPECT_EQ(*a, 5); a.emplace(7); EXPECT_EQ(*a, 7); } TEST(BoxedValuePtrTest, Reset) { boxed_value_ptr a(6); a.reset(); EXPECT_FALSE(static_cast(a)); } TEST(BoxedValuePtrTest, Assignment) { boxed_value_ptr a; a = 6; EXPECT_EQ(*a, 6); } TEST(BoxedValuePtrTest, MoveOnlyType) { boxed_value_ptr> a; a = std::make_unique(5); EXPECT_EQ(**a, 5); boxed_value_ptr> b(std::move(a)); EXPECT_EQ(**b, 5); } TEST(BoxedValuePtrTest, Swap) { boxed_value_ptr a(5); boxed_value_ptr b(7); std::swap(a, b); EXPECT_EQ(*a, 7); EXPECT_EQ(*b, 5); } TEST(BoxedValuePtrTest, Equal) { boxed_value_ptr a; boxed_value_ptr b; EXPECT_TRUE(a == b); a = 5; EXPECT_FALSE(a == b); b = 5; EXPECT_FALSE(a == b); b = 7; EXPECT_FALSE(a == b); } TEST(BoxedPtrTest, DefaultConstructor) { boxed_ptr a; EXPECT_FALSE(static_cast(a)); } TEST(BoxedPtrTest, FromStaticConstant) { const TestStruct t{5}; boxed_ptr a = boxed_ptr::fromStaticConstant(&t); EXPECT_EQ(*a, 5); } TEST(BoxedPtrTest, UniquePtrConstructor) { boxed_ptr a{std::make_unique(5)}; EXPECT_EQ(*a, 5); } TEST(BoxedPtrTest, CopyConstructorAndAssignment) { static_assert(!std::is_copy_constructible>::value); static_assert(!std::is_copy_assignable>::value); } TEST(BoxedPtrTest, MoveConstructor) { const TestStruct t{5}; boxed_ptr a = boxed_ptr::fromStaticConstant(&t); boxed_ptr b(std::move(a)); EXPECT_EQ(*b, 5); } TEST(BoxedPtrTest, MoveAssignment) { const TestStruct t{5}; { boxed_ptr unowned = boxed_ptr::fromStaticConstant(&t); boxed_ptr b; b = std::move(unowned); EXPECT_EQ(*b, 5); } { boxed_ptr owned{std::make_unique(6)}; boxed_ptr b; b = std::move(owned); EXPECT_EQ(*b, 6); } { boxed_ptr unowned = boxed_ptr::fromStaticConstant(&t); boxed_ptr b{std::make_unique(7)}; b = std::move(unowned); EXPECT_EQ(*b, 5); } { boxed_ptr owned{std::make_unique(6)}; boxed_ptr b{std::make_unique(7)}; b = std::move(owned); EXPECT_EQ(*b, 6); } } TEST(BoxedPtrTest, Reset) { const TestStruct t{5}; boxed_ptr a = boxed_ptr::fromStaticConstant(&t); a.reset(); EXPECT_FALSE(static_cast(a)); a.reset(std::make_unique(6)); EXPECT_TRUE(static_cast(a)); EXPECT_EQ(*a, 6); } TEST(BoxedPtrTest, Swap) { const TestStruct t1{5}; const TestStruct t2{7}; boxed_ptr a = boxed_ptr::fromStaticConstant(&t1); boxed_ptr b = boxed_ptr::fromStaticConstant(&t2); std::swap(a, b); EXPECT_EQ(*a, 7); EXPECT_EQ(*b, 5); } TEST(BoxedPtrTest, Equal) { const TestStruct t1{5}; const TestStruct t2{7}; boxed_ptr a; boxed_ptr b; EXPECT_TRUE(a == b); a = boxed_ptr::fromStaticConstant(&t1); EXPECT_FALSE(a == b); b = boxed_ptr::fromStaticConstant(&t1); EXPECT_TRUE(a == b); b = boxed_ptr::fromStaticConstant(&t2); EXPECT_FALSE(a == b); } TEST(BoxedPtrTest, Mut) { const TestStruct t1{5}; boxed_ptr a = boxed_ptr::fromStaticConstant(&t1); boxed_ptr b = boxed_ptr::fromStaticConstant(&t1); EXPECT_TRUE(a == b); (void)a.mut(); EXPECT_FALSE(a == b); } TEST(BoxedPtrTest, MutFromDefaultConstructed) { boxed_ptr a; EXPECT_FALSE(static_cast(a)); if (folly::kIsDebug) { EXPECT_DEATH(a.mut(), ""); } } TEST(BoxedPtrTest, Copy) { const TestStruct t1{5}; boxed_ptr a = boxed_ptr::fromStaticConstant(&t1); // Shallow copy when 'boxed_ptr' does not own the value. boxed_ptr b = a.copy(); EXPECT_TRUE(a == b); (void)a.mut(); // Deep copy when 'boxed_ptr' owns the value. boxed_ptr c = a.copy(); EXPECT_FALSE(a == c); } TEST(BoxedValueTest, DefaultConstructor) { boxed_value a; EXPECT_FALSE(a.has_value()); } TEST(BoxedValueTest, FromStaticConstant) { const TestStruct t{5}; boxed_value a = boxed_value::fromStaticConstant(&t); EXPECT_EQ(a.value(), 5); } TEST(BoxedValueTest, UniquePtrConstructor) { boxed_value a{std::make_unique(5)}; EXPECT_EQ(a.value(), 5); } TEST(BoxedValueTest, CopyConstructor) { const TestStruct t{5}; boxed_value a = boxed_value::fromStaticConstant(&t); boxed_value b{a}; EXPECT_EQ(a.value(), 5); // shallow copy. EXPECT_EQ(b.get(), a.get()); a.mut(); boxed_value c{a}; // deep copy. EXPECT_NE(c.get(), a.get()); } TEST(BoxedValueTest, CopyAssignment) { const TestStruct t{5}; boxed_value a = boxed_value::fromStaticConstant(&t); boxed_value b = a; EXPECT_EQ(a.value(), 5); // shallow copy. EXPECT_EQ(b.get(), a.get()); a.mut(); boxed_value c = a; // deep copy. EXPECT_NE(c.get(), a.get()); } TEST(BoxedValueTest, MoveConstructor) { const TestStruct t{5}; boxed_value a = boxed_value::fromStaticConstant(&t); boxed_value b(std::move(a)); EXPECT_EQ(b.value(), 5); } TEST(BoxedValueTest, MoveAssignment) { const TestStruct t{5}; boxed_value a = boxed_value::fromStaticConstant(&t); boxed_value b; b = std::move(a); EXPECT_EQ(b.value(), 5); } TEST(BoxedValueTest, Reset) { const TestStruct t{5}; boxed_value a = boxed_value::fromStaticConstant(&t); a.reset(); EXPECT_FALSE(a.has_value()); a.reset(std::make_unique(6)); EXPECT_TRUE(a.has_value()); EXPECT_EQ(*a, 6); } TEST(BoxedValueTest, Swap) { const TestStruct t1{5}; const TestStruct t2{7}; boxed_value a = boxed_value::fromStaticConstant(&t1); boxed_value b = boxed_value::fromStaticConstant(&t2); std::swap(a, b); EXPECT_EQ(*a, 7); EXPECT_EQ(*b, 5); } TEST(BoxedValueTest, Mut) { const TestStruct t1{5}; boxed_value a = boxed_value::fromStaticConstant(&t1); boxed_value b = boxed_value::fromStaticConstant(&t1); a.mut().set_a(1); std::move(b).mut().set_a(2); EXPECT_EQ(a.value(), 1); EXPECT_EQ(b.value(), 2); } TEST(BoxedValueTest, MutFromDefaultConstructed) { boxed_value a; EXPECT_THROW(a.mut(), std::logic_error); EXPECT_THROW(std::move(a).mut(), std::logic_error); } TEST(BoxedValueTest, Comparison) { const TestStruct t1{1}; const TestStruct t2{2}; boxed_value a = boxed_value::fromStaticConstant(&t1); boxed_value b = boxed_value::fromStaticConstant(&t2); EXPECT_FALSE(a == b); EXPECT_TRUE(a != b); EXPECT_TRUE(a < b); EXPECT_TRUE(a <= b); EXPECT_FALSE(a > b); EXPECT_FALSE(a >= b); EXPECT_FALSE(a == t2); EXPECT_TRUE(a != t2); EXPECT_TRUE(a < t2); EXPECT_TRUE(a <= t2); EXPECT_FALSE(a > t2); EXPECT_FALSE(a >= t2); EXPECT_FALSE(t1 == b); EXPECT_TRUE(t1 != b); EXPECT_TRUE(t1 < b); EXPECT_TRUE(t1 <= b); EXPECT_FALSE(t1 > b); EXPECT_FALSE(t1 >= b); EXPECT_TRUE(t2 == b); EXPECT_FALSE(t2 != b); EXPECT_FALSE(t2 < b); EXPECT_TRUE(t2 <= b); EXPECT_FALSE(t2 > b); EXPECT_TRUE(t2 >= b); } TEST(BoxedValueTest, ComparisonWithNoValue) { const TestStruct t1{1}; boxed_value a = boxed_value::fromStaticConstant(&t1); boxed_value b; boxed_value c; EXPECT_FALSE(a == b); EXPECT_TRUE(a != b); EXPECT_FALSE(a < b); EXPECT_FALSE(a <= b); EXPECT_TRUE(a > b); EXPECT_TRUE(a >= b); EXPECT_FALSE(b == t1); EXPECT_TRUE(b != t1); EXPECT_TRUE(b < t1); EXPECT_TRUE(b <= t1); EXPECT_FALSE(b > t1); EXPECT_FALSE(b >= t1); EXPECT_FALSE(t1 == b); EXPECT_TRUE(t1 != b); EXPECT_FALSE(t1 < b); EXPECT_FALSE(t1 <= b); EXPECT_TRUE(t1 > b); EXPECT_TRUE(t1 >= b); } TEST(BoxedValueTest, ValueComparison) { const TestStruct t{1}; boxed_value a = boxed_value::fromStaticConstant(&t); boxed_value b = boxed_value::fromStaticConstant(&t); b.mut(); // 'b' points to shared value; meanwhile, 'c' owns the same value. EXPECT_NE(a.get(), b.get()); EXPECT_EQ(a, b); } TEST(BoxedValueTest, Ensure) { const TestStruct t{1}; boxed_value a = boxed_value::fromStaticConstant(&t); boxed_value b = boxed_value::fromStaticConstant(&t); boxed_value c; EXPECT_EQ(a.get(), b.get()); a.ensure(); EXPECT_EQ(a.get(), b.get()); c.ensure(); c.mut().set_a(1); EXPECT_EQ(a, c); } class LifecycleTester { public: using __fbthrift_cpp2_type = LifecycleTester; LifecycleTester() : isACopy_(false), hasBeenCopied_(false) {} explicit LifecycleTester(int) : isACopy_(false), hasBeenCopied_(false) {} // NOLINTNEXTLINE(facebook-hte-NonConstCopyContructor) LifecycleTester(LifecycleTester& other) : isACopy_(true), hasBeenCopied_(false) { other.hasBeenCopied_ = true; } LifecycleTester& operator=(LifecycleTester& other) { LifecycleTester tmp{other}; std::swap(isACopy_, tmp.isACopy_); std::swap(hasBeenCopied_, tmp.hasBeenCopied_); other.hasBeenCopied_ = true; return *this; } bool isACopy() const { return isACopy_; } bool hasBeenCopied() const { return hasBeenCopied_; } private: bool isACopy_; bool hasBeenCopied_; }; TEST(LifecycleTest, ConstUnownedCopy) { const LifecycleTester lct; boxed_value boxed1 = boxed_value::fromStaticConstant(&lct); EXPECT_FALSE(boxed1->isACopy()); EXPECT_FALSE(boxed1->hasBeenCopied()); boxed_value boxed2 = boxed1; EXPECT_FALSE(boxed1->hasBeenCopied()); EXPECT_FALSE(boxed2->isACopy()); } TEST(LifecycleTest, MutOwnedRefCopy) { const LifecycleTester lct; boxed_value boxed = boxed_value::fromStaticConstant(&lct); EXPECT_FALSE(boxed->isACopy()); EXPECT_TRUE(boxed.mut().isACopy()); // This mutable ref creates a copy. } TEST(LifecycleTest, copyOfMutableRefCreatesCopy) { // Creating the object this way starts with a MutOwned state. boxed_value boxed1; boxed1.ensure(); EXPECT_FALSE(boxed1->hasBeenCopied()); auto boxed2 = boxed1; EXPECT_TRUE(boxed1->hasBeenCopied()); }