// Copyright 2016 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include static int destroy_count = 0; struct DeleteCounter { DeleteCounter() = default; DeleteCounter(int value) : value(value) {} static void operator delete(void* ptr) { destroy_count++; ::operator delete(ptr); } static void operator delete[](void* ptr) { destroy_count++; ::operator delete[](ptr); } int value = 0; }; using CountingPtr = fbl::unique_ptr; using CountingArrPtr = fbl::unique_ptr; static_assert(fbl::is_standard_layout::value, "fbl::unique_ptr's should have a standard layout"); static_assert(fbl::is_standard_layout::value, "fbl::unique_ptr's should have a standard layout"); static_assert(fbl::is_standard_layout::value, "fbl::unique_ptr's should have a standard layout"); static_assert(fbl::is_standard_layout::value, "fbl::unique_ptr's should have a standard layout"); static bool uptr_test_scoped_destruction() { BEGIN_TEST; destroy_count = 0; fbl::AllocChecker ac; // Construct and let a unique_ptr fall out of scope. { CountingPtr ptr(new (&ac) DeleteCounter); EXPECT_TRUE(ac.check()); } EXPECT_EQ(1, destroy_count); END_TEST; } static bool uptr_test_move() { BEGIN_TEST; destroy_count = 0; fbl::AllocChecker ac; // Construct and move into another unique_ptr. { CountingPtr ptr(new (&ac) DeleteCounter); EXPECT_TRUE(ac.check()); CountingPtr ptr2 = std::move(ptr); EXPECT_NULL(ptr, "expected ptr to be null"); } EXPECT_EQ(1, destroy_count); END_TEST; } static bool uptr_test_null_scoped_destruction() { BEGIN_TEST; destroy_count = 0; // Construct a null unique_ptr and let it fall out of scope - should not call // deleter. { CountingPtr ptr(nullptr); } EXPECT_EQ(0, destroy_count); END_TEST; } static bool uptr_test_diff_scope_swap() { BEGIN_TEST; destroy_count = 0; // Construct a pair of unique_ptrs in different scopes, swap them, and verify // that the values change places and that the values are destroyed at the // correct times. fbl::AllocChecker ac; { CountingPtr ptr1(new (&ac) DeleteCounter(4)); EXPECT_TRUE(ac.check()); { CountingPtr ptr2(new (&ac) DeleteCounter(7)); EXPECT_TRUE(ac.check()); ptr1.swap(ptr2); EXPECT_EQ(7, ptr1->value); EXPECT_EQ(4, ptr2->value); } EXPECT_EQ(1, destroy_count); } EXPECT_EQ(2, destroy_count); END_TEST; } static bool uptr_test_bool_op() { BEGIN_TEST; destroy_count = 0; fbl::AllocChecker ac; CountingPtr foo(new (&ac) DeleteCounter); EXPECT_TRUE(ac.check()); EXPECT_TRUE(static_cast(foo)); foo.reset(); EXPECT_EQ(1, destroy_count); EXPECT_FALSE(static_cast(foo)); END_TEST; } static bool uptr_test_comparison() { BEGIN_TEST; fbl::AllocChecker ac; // Test comparison operators. fbl::unique_ptr null_unique; fbl::unique_ptr lesser_unique(new (&ac) DeleteCounter(1)); EXPECT_TRUE(ac.check()); fbl::unique_ptr greater_unique(new (&ac) DeleteCounter(2)); EXPECT_TRUE(ac.check()); EXPECT_NE(lesser_unique.get(), greater_unique.get()); if (lesser_unique.get() > greater_unique.get()) lesser_unique.swap(greater_unique); // Comparison against nullptr EXPECT_TRUE( null_unique == nullptr); EXPECT_TRUE( lesser_unique != nullptr); EXPECT_TRUE(greater_unique != nullptr); EXPECT_TRUE(nullptr == null_unique); EXPECT_TRUE(nullptr != lesser_unique); EXPECT_TRUE(nullptr != greater_unique); // Comparison against other unique_ptr<>s EXPECT_TRUE( lesser_unique == lesser_unique); EXPECT_FALSE( lesser_unique == greater_unique); EXPECT_FALSE(greater_unique == lesser_unique); EXPECT_TRUE(greater_unique == greater_unique); EXPECT_FALSE( lesser_unique != lesser_unique); EXPECT_TRUE ( lesser_unique != greater_unique, ""); EXPECT_TRUE (greater_unique != lesser_unique, ""); EXPECT_FALSE(greater_unique != greater_unique); EXPECT_FALSE( lesser_unique < lesser_unique); EXPECT_TRUE ( lesser_unique < greater_unique, ""); EXPECT_FALSE(greater_unique < lesser_unique); EXPECT_FALSE(greater_unique < greater_unique); EXPECT_FALSE( lesser_unique > lesser_unique); EXPECT_FALSE( lesser_unique > greater_unique); EXPECT_TRUE (greater_unique > lesser_unique, ""); EXPECT_FALSE(greater_unique > greater_unique); EXPECT_TRUE ( lesser_unique <= lesser_unique, ""); EXPECT_TRUE ( lesser_unique <= greater_unique, ""); EXPECT_FALSE(greater_unique <= lesser_unique); EXPECT_TRUE (greater_unique <= greater_unique, ""); EXPECT_TRUE ( lesser_unique >= lesser_unique, ""); EXPECT_FALSE( lesser_unique >= greater_unique); EXPECT_TRUE (greater_unique >= lesser_unique, ""); EXPECT_TRUE (greater_unique >= greater_unique, ""); END_TEST; } static bool uptr_test_array_scoped_destruction() { BEGIN_TEST; destroy_count = 0; fbl::AllocChecker ac; // Construct and let a unique_ptr fall out of scope. { CountingArrPtr ptr(new (&ac) DeleteCounter[1]); EXPECT_TRUE(ac.check()); } EXPECT_EQ(1, destroy_count); END_TEST; } static bool uptr_test_array_move() { BEGIN_TEST; destroy_count = 0; fbl::AllocChecker ac; // Construct and move into another unique_ptr. { CountingArrPtr ptr(new (&ac) DeleteCounter[1]); EXPECT_TRUE(ac.check()); CountingArrPtr ptr2 = std::move(ptr); EXPECT_NULL(ptr, "expected ptr to be null"); } EXPECT_EQ(1, destroy_count); END_TEST; } static bool uptr_test_array_null_scoped_destruction() { BEGIN_TEST; destroy_count = 0; // Construct a null unique_ptr and let it fall out of scope - should not call // deleter. { CountingArrPtr ptr(nullptr); } EXPECT_EQ(0, destroy_count); END_TEST; } static bool uptr_test_array_diff_scope_swap() { BEGIN_TEST; destroy_count = 0; // Construct a pair of unique_ptrs in different scopes, swap them, and verify // that the values change places and that the values are destroyed at the // correct times. fbl::AllocChecker ac; { CountingArrPtr ptr1(new (&ac) DeleteCounter[1]); EXPECT_TRUE(ac.check()); ptr1[0] = 4; { CountingArrPtr ptr2(new (&ac) DeleteCounter[1]); EXPECT_TRUE(ac.check()); ptr2[0] = 7; ptr1.swap(ptr2); EXPECT_EQ(7, ptr1[0].value); EXPECT_EQ(4, ptr2[0].value); } EXPECT_EQ(1, destroy_count); } EXPECT_EQ(2, destroy_count); END_TEST; } static bool uptr_test_array_bool_op() { BEGIN_TEST; destroy_count = 0; fbl::AllocChecker ac; CountingArrPtr foo(new (&ac) DeleteCounter[1]); EXPECT_TRUE(ac.check()); EXPECT_TRUE(static_cast(foo)); foo.reset(); EXPECT_EQ(1, destroy_count); EXPECT_FALSE(static_cast(foo)); END_TEST; } static bool uptr_test_array_comparison() { BEGIN_TEST; fbl::AllocChecker ac; fbl::unique_ptr null_unique; fbl::unique_ptr lesser_unique(new (&ac) DeleteCounter[1]); EXPECT_TRUE(ac.check()); fbl::unique_ptr greater_unique(new (&ac) DeleteCounter[2]); EXPECT_TRUE(ac.check()); EXPECT_NE(lesser_unique.get(), greater_unique.get()); if (lesser_unique.get() > greater_unique.get()) lesser_unique.swap(greater_unique); // Comparison against nullptr EXPECT_TRUE( null_unique == nullptr); EXPECT_TRUE( lesser_unique != nullptr); EXPECT_TRUE(greater_unique != nullptr); EXPECT_TRUE(nullptr == null_unique); EXPECT_TRUE(nullptr != lesser_unique); EXPECT_TRUE(nullptr != greater_unique); // Comparison against other unique_ptr<>s EXPECT_TRUE( lesser_unique == lesser_unique); EXPECT_FALSE( lesser_unique == greater_unique); EXPECT_FALSE(greater_unique == lesser_unique); EXPECT_TRUE(greater_unique == greater_unique); EXPECT_FALSE( lesser_unique != lesser_unique); EXPECT_TRUE ( lesser_unique != greater_unique, ""); EXPECT_TRUE (greater_unique != lesser_unique, ""); EXPECT_FALSE(greater_unique != greater_unique); EXPECT_FALSE( lesser_unique < lesser_unique); EXPECT_TRUE ( lesser_unique < greater_unique, ""); EXPECT_FALSE(greater_unique < lesser_unique); EXPECT_FALSE(greater_unique < greater_unique); EXPECT_FALSE( lesser_unique > lesser_unique); EXPECT_FALSE( lesser_unique > greater_unique); EXPECT_TRUE (greater_unique > lesser_unique, ""); EXPECT_FALSE(greater_unique > greater_unique); EXPECT_TRUE ( lesser_unique <= lesser_unique, ""); EXPECT_TRUE ( lesser_unique <= greater_unique, ""); EXPECT_FALSE(greater_unique <= lesser_unique); EXPECT_TRUE (greater_unique <= greater_unique, ""); EXPECT_TRUE ( lesser_unique >= lesser_unique, ""); EXPECT_FALSE( lesser_unique >= greater_unique); EXPECT_TRUE (greater_unique >= lesser_unique, ""); EXPECT_TRUE (greater_unique >= greater_unique, ""); END_TEST; } namespace upcasting { class A { public: virtual ~A() { stuff_ = 0; } private: volatile uint32_t stuff_; }; class B { public: ~B() { stuff_ = 1; } private: volatile uint32_t stuff_; }; class C : public A, public B { public: ~C() { stuff_ = 2; } private: volatile uint32_t stuff_; }; class D { public: virtual ~D() { stuff_ = 3; } private: volatile uint32_t stuff_; }; template static bool handoff_fn(UptrType&& ptr) { BEGIN_TEST; EXPECT_NONNULL(ptr); END_TEST; } class OverloadTestHelper { public: enum class Result { None, ClassA, ClassB, ClassD, }; void PassByMove(fbl::unique_ptr&&) { result_ = Result::ClassA; } void PassByMove(fbl::unique_ptr&&) { result_ = Result::ClassD; } #if TEST_WILL_NOT_COMPILE || 0 // Enabling this overload should cause the overload test to fail to compile // due to ambiguity (it does not know whether to cast fbl::unique_ptr to // fbl::unique_ptr or fbl::unique_ptr) void PassByMove(fbl::unique_ptr&&) { result_ = Result::ClassB; } #endif Result result() const { return result_; } private: Result result_ = Result::None; }; template static bool test_upcast() { BEGIN_TEST; fbl::AllocChecker ac; fbl::unique_ptr derived_ptr; // Construct unique_ptr with a move and implicit cast derived_ptr.reset(new (&ac) Derived()); ASSERT_TRUE(ac.check()); { EXPECT_NONNULL(derived_ptr); fbl::unique_ptr base_ptr(std::move(derived_ptr)); EXPECT_NULL(derived_ptr); EXPECT_NONNULL(base_ptr); } // Assign unique_ptr at declaration time with a std::move derived_ptr.reset(new (&ac) Derived()); ASSERT_TRUE(ac.check()); { EXPECT_NONNULL(derived_ptr); fbl::unique_ptr base_ptr = std::move(derived_ptr); EXPECT_NULL(derived_ptr); EXPECT_NONNULL(base_ptr); } // Assign unique_ptr after declaration with a std::move derived_ptr.reset(new (&ac) Derived()); ASSERT_TRUE(ac.check()); { fbl::unique_ptr base_ptr; base_ptr = std::move(derived_ptr); } // Pass the pointer to a function with a move and an implicit cast derived_ptr.reset(new (&ac) Derived()); ASSERT_TRUE(ac.check()); { EXPECT_NONNULL(derived_ptr); bool test_res = handoff_fn>(std::move(derived_ptr)); EXPECT_NULL(derived_ptr); EXPECT_TRUE(test_res); } #if TEST_WILL_NOT_COMPILE || 0 // Construct unique_ptr without a move. derived_ptr.reset(new (&ac) Derived()); ASSERT_TRUE(ac.check()); { fbl::unique_ptr base_ptr(derived_ptr); } #endif #if TEST_WILL_NOT_COMPILE || 0 // Assign unique_ptr at declaration time without a std::move. derived_ptr.reset(new (&ac) Derived()); ASSERT_TRUE(ac.check()); { fbl::unique_ptr base_ptr = derived_ptr; } #endif #if TEST_WILL_NOT_COMPILE || 0 // Assign unique_ptr after declaration without a std::move. derived_ptr.reset(new (&ac) Derived()); ASSERT_TRUE(ac.check()); { fbl::unique_ptr base_ptr; base_ptr = derived_ptr; } #endif #if TEST_WILL_NOT_COMPILE || 0 // Pass the pointer to a function with an implicit cast but without a move. derived_ptr.reset(new (&ac) Derived()); ASSERT_TRUE(ac.check()); { bool test_res = handoff_fn>(derived_ptr); EXPECT_FALSE(test_res); } #endif END_TEST; } static bool uptr_upcasting() { BEGIN_TEST; bool test_res; // This should work. C derives from A, A has a virtual destructor, and // everything is using the default deleter. test_res = test_upcast(); EXPECT_TRUE(test_res); #if TEST_WILL_NOT_COMPILE || 0 // This should not work. C derives from B, but B has no virtual destructor. test_res = test_upcast(); EXPECT_FALSE(test_res); #endif // This should work even though B has no virtual destructor. { fbl::AllocChecker ac; fbl::unique_ptr ptr(new (&ac) B()); ASSERT_TRUE(ac.check()); fbl::unique_ptr const_ptr; const_ptr = std::move(ptr); EXPECT_NULL(ptr); EXPECT_NONNULL(const_ptr); } #if TEST_WILL_NOT_COMPILE || 0 // This should not work. D has a virtual destructor, but it is not a base // class of C. test_res = test_upcast(); EXPECT_FALSE(test_res); #endif // Test overload resolution. Make a C and the try to pass it to // OverloadTestHelper's various overloaded methods. The compiler should // know which version to pick, and it should pick the unique_ptr version, // not the unique_ptr version. If the TEST_WILL_NOT_COMPILE check is // enabled in OverloadTestHelper, a unique_ptr version will be enabled as // well. This should cause the build to break because of ambiguity. fbl::AllocChecker ac; fbl::unique_ptr ptr(new (&ac) C()); ASSERT_TRUE(ac.check()); { // Now test pass by move. OverloadTestHelper helper; helper.PassByMove(std::move(ptr)); EXPECT_NULL(ptr); EXPECT_EQ(OverloadTestHelper::Result::ClassA, helper.result()); } END_TEST; } } // namespace upcasting static bool uptr_test_make_unique() { BEGIN_TEST; // no alloc checker destroy_count = 0; { CountingPtr ptr = fbl::make_unique(42); EXPECT_EQ(42, ptr->value, "value"); } EXPECT_EQ(1, destroy_count); // with alloc checker destroy_count = 0; { fbl::AllocChecker ac; CountingPtr ptr = fbl::make_unique_checked(&ac, 4242); EXPECT_TRUE(ac.check()); EXPECT_EQ(4242, ptr->value, "value"); } EXPECT_EQ(1, destroy_count); END_TEST; } static bool uptr_test_make_unique_array() { BEGIN_TEST; constexpr size_t array_size = 4; // no alloc checker destroy_count = 0; { CountingArrPtr ptr = fbl::make_unique(array_size); EXPECT_NONNULL(ptr); for (size_t i = 0; i < array_size; ++i) { EXPECT_EQ(0, ptr[i].value); } } EXPECT_EQ(1, destroy_count); END_TEST; } BEGIN_TEST_CASE(unique_ptr) RUN_NAMED_TEST("Scoped Destruction", uptr_test_scoped_destruction) RUN_NAMED_TEST("Move", uptr_test_move) RUN_NAMED_TEST("nullptr Scoped Destruction", uptr_test_null_scoped_destruction) RUN_NAMED_TEST("Different Scope Swapping", uptr_test_diff_scope_swap) RUN_NAMED_TEST("operator bool", uptr_test_bool_op) RUN_NAMED_TEST("comparison operators", uptr_test_comparison) RUN_NAMED_TEST("Array Scoped Destruction", uptr_test_array_scoped_destruction) RUN_NAMED_TEST("Array Move", uptr_test_array_move) RUN_NAMED_TEST("Array nullptr Scoped Destruction", uptr_test_array_null_scoped_destruction) RUN_NAMED_TEST("Array Different Scope Swapping", uptr_test_array_diff_scope_swap) RUN_NAMED_TEST("Array operator bool", uptr_test_array_bool_op) RUN_NAMED_TEST("Array comparison operators", uptr_test_array_comparison) RUN_NAMED_TEST("Upcast tests", upcasting::uptr_upcasting) RUN_NAMED_TEST("Make unique", uptr_test_make_unique) RUN_NAMED_TEST("Make unique array", uptr_test_make_unique_array) END_TEST_CASE(unique_ptr);