| // Copyright 2026 The Pigweed Authors |
| // |
| // 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 |
| // |
| // https://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 "pw_allocator/maybe_shared_ptr.h" |
| |
| // TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`. |
| #if PW_ALLOCATOR_HAS_ATOMICS |
| |
| #include <cstddef> |
| #include <utility> |
| |
| #include "pw_allocator/allocator.h" |
| #include "pw_allocator/internal/counter.h" |
| #include "pw_allocator/shared_ptr.h" |
| #include "pw_allocator/testing.h" |
| #include "pw_unit_test/framework.h" |
| |
| namespace { |
| |
| using pw::allocator::test::Counter; |
| |
| class MaybeSharedPtrTest : public pw::allocator::test::TestWithCounters { |
| protected: |
| pw::allocator::test::AllocatorForTest<256> allocator_; |
| }; |
| |
| struct Foo { |
| virtual ~Foo() = default; |
| int foo() const { return val + 1; } |
| int val = 0; |
| }; |
| |
| struct Bar : public Foo { |
| int bar() const { return val + 2; } |
| }; |
| |
| struct Baz : public Bar { |
| int baz() const { return val + 3; } |
| }; |
| |
| TEST_F(MaybeSharedPtrTest, DefaultInitializationIsNullptr) { |
| pw::MaybeSharedPtr<int> empty; |
| EXPECT_EQ(empty.get(), nullptr); |
| EXPECT_EQ(empty, nullptr); |
| EXPECT_EQ(empty.allocator(), nullptr); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConstructFromOwnedSharedPtr) { |
| pw::SharedPtr<Counter> shared = allocator_.MakeShared<Counter>(42u); |
| ASSERT_NE(shared, nullptr); |
| |
| { |
| pw::MaybeSharedPtr<Counter> maybe(shared); |
| EXPECT_NE(maybe, nullptr); |
| EXPECT_EQ(maybe.get(), shared.get()); |
| EXPECT_EQ(maybe->value(), 42u); |
| EXPECT_EQ((*maybe).value(), 42u); |
| EXPECT_EQ(maybe.allocator(), &allocator_); |
| } |
| |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, MoveConstructFromOwnedSharedPtr) { |
| pw::SharedPtr<Counter> shared = allocator_.MakeShared<Counter>(42u); |
| Counter* raw = shared.get(); |
| |
| pw::MaybeSharedPtr<Counter> maybe(std::move(shared)); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(shared, nullptr); |
| EXPECT_NE(maybe, nullptr); |
| EXPECT_EQ(maybe.get(), raw); |
| EXPECT_EQ(maybe->value(), 42u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConstructFromUnownedReference) { |
| Counter counter(99u); |
| { |
| pw::MaybeSharedPtr<Counter> maybe = |
| pw::MaybeSharedPtr<Counter>::Unowned(counter); |
| EXPECT_NE(maybe, nullptr); |
| EXPECT_EQ(maybe.get(), &counter); |
| EXPECT_EQ(maybe->value(), 99u); |
| EXPECT_EQ((*maybe).value(), 99u); |
| EXPECT_EQ(maybe.allocator(), nullptr); |
| } |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ResetUnownedPointerDoesNotDestroyObject) { |
| Counter counter(99u); |
| pw::MaybeSharedPtr<Counter> maybe = pw::Unowned(counter); |
| maybe.reset(); |
| EXPECT_EQ(maybe, nullptr); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConstructFromUnownedHelper) { |
| Counter counter(123u); |
| { |
| auto maybe = pw::Unowned(counter); |
| EXPECT_NE(maybe, nullptr); |
| EXPECT_EQ(maybe.get(), &counter); |
| EXPECT_EQ(maybe->value(), 123u); |
| } |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, OwnedDestructionFreesObject) { |
| { |
| pw::MaybeSharedPtr<Counter> maybe(allocator_.MakeShared<Counter>(1u)); |
| } |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CopyOwned) { |
| pw::MaybeSharedPtr<Counter> ptr1(allocator_.MakeShared<Counter>(10u)); |
| |
| pw::MaybeSharedPtr<Counter> ptr2 = ptr1; |
| EXPECT_EQ(ptr1.get(), ptr2.get()); |
| |
| ptr1.reset(); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| |
| ptr2.reset(); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, MoveOwned) { |
| pw::MaybeSharedPtr<Counter> ptr1(allocator_.MakeShared<Counter>(10u)); |
| Counter* raw = ptr1.get(); |
| |
| pw::MaybeSharedPtr<Counter> ptr2 = std::move(ptr1); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(ptr1, nullptr); |
| EXPECT_NE(ptr2, nullptr); |
| EXPECT_EQ(ptr2.get(), raw); |
| |
| ptr2.reset(); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CopyUnowned) { |
| Counter counter(55u); |
| { |
| pw::MaybeSharedPtr<Counter> ptr1 = pw::Unowned(counter); |
| pw::MaybeSharedPtr<Counter> ptr2 = ptr1; |
| EXPECT_EQ(ptr1.get(), &counter); |
| EXPECT_EQ(ptr2.get(), &counter); |
| } |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, MoveUnowned) { |
| Counter counter(55u); |
| { |
| pw::MaybeSharedPtr<Counter> ptr1 = pw::Unowned(counter); |
| pw::MaybeSharedPtr<Counter> ptr2 = std::move(ptr1); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(ptr1, nullptr); |
| EXPECT_EQ(ptr2.get(), &counter); |
| } |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConvertingConstructorsForOwned) { |
| pw::SharedPtr<Baz> baz = allocator_.MakeShared<Baz>(); |
| pw::MaybeSharedPtr<Bar> bar = baz; |
| pw::MaybeSharedPtr<Foo> foo = bar; |
| EXPECT_EQ(foo.get(), baz.get()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConvertingConstructorsForUnowned) { |
| Baz static_baz; |
| pw::MaybeSharedPtr<Baz> unowned_baz = pw::Unowned(static_baz); |
| pw::MaybeSharedPtr<Foo> unowned_foo = unowned_baz; |
| EXPECT_EQ(unowned_foo.get(), &static_baz); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, StaticPointerCast) { |
| pw::SharedPtr<Baz> baz = allocator_.MakeShared<Baz>(); |
| pw::MaybeSharedPtr<Foo> foo = baz; |
| |
| pw::MaybeSharedPtr<Baz> cast_baz = pw::static_pointer_cast<Baz>(foo); |
| EXPECT_EQ(cast_baz.get(), baz.get()); |
| |
| Baz static_baz; |
| pw::MaybeSharedPtr<Foo> unowned_foo = pw::Unowned<Foo>(static_baz); |
| pw::MaybeSharedPtr<Baz> unowned_baz = |
| pw::static_pointer_cast<Baz>(unowned_foo); |
| EXPECT_EQ(unowned_baz.get(), &static_baz); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConstPointerCast) { |
| pw::SharedPtr<Baz> baz = allocator_.MakeShared<Baz>(); |
| pw::MaybeSharedPtr<Baz> foo = baz; |
| |
| pw::MaybeSharedPtr<const Baz> const_baz = foo; |
| pw::MaybeSharedPtr<Baz> non_const_baz = |
| pw::const_pointer_cast<Baz>(const_baz); |
| EXPECT_EQ(non_const_baz.get(), foo.get()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CompareWithNullptr) { |
| pw::MaybeSharedPtr<Counter> empty; |
| EXPECT_EQ(empty, nullptr); |
| EXPECT_EQ(nullptr, empty); |
| |
| Counter counter(1u); |
| pw::MaybeSharedPtr<Counter> unowned = pw::Unowned(counter); |
| EXPECT_NE(unowned, nullptr); |
| EXPECT_NE(nullptr, unowned); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CompareMaybeSharedPtrInstances) { |
| Counter counter1(1u); |
| Counter counter2(2u); |
| |
| pw::MaybeSharedPtr<Counter> unowned1 = pw::Unowned(counter1); |
| pw::MaybeSharedPtr<Counter> unowned2 = pw::Unowned(counter2); |
| pw::MaybeSharedPtr<Counter> unowned1_copy = pw::Unowned(counter1); |
| |
| EXPECT_EQ(unowned1, unowned1_copy); |
| EXPECT_NE(unowned1, unowned2); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CompareOwnedMaybeSharedPtrWithSharedPtr) { |
| pw::SharedPtr<Counter> shared1 = allocator_.MakeShared<Counter>(10u); |
| pw::MaybeSharedPtr<Counter> maybe_shared = shared1; |
| EXPECT_EQ(maybe_shared, shared1); |
| EXPECT_EQ(shared1, maybe_shared); |
| |
| pw::SharedPtr<Counter> shared2 = allocator_.MakeShared<Counter>(20u); |
| EXPECT_NE(maybe_shared, shared2); |
| EXPECT_NE(shared2, maybe_shared); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CompareUnownedMaybeSharedPtrWithSharedPtr) { |
| pw::SharedPtr<Counter> shared = allocator_.MakeShared<Counter>(10u); |
| pw::MaybeSharedPtr<Counter> unowned_to_shared = pw::Unowned(*shared); |
| pw::MaybeSharedPtr<Counter> maybe_shared = shared; |
| |
| EXPECT_EQ(unowned_to_shared, shared); |
| EXPECT_EQ(shared, unowned_to_shared); |
| EXPECT_EQ(unowned_to_shared, maybe_shared); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, Swap) { |
| Counter counter(1u); |
| pw::MaybeSharedPtr<Counter> unowned = pw::Unowned(counter); |
| pw::MaybeSharedPtr<Counter> owned(allocator_.MakeShared<Counter>(2u)); |
| |
| Counter* unowned_ptr = unowned.get(); |
| Counter* owned_ptr = owned.get(); |
| |
| unowned.swap(owned); |
| EXPECT_EQ(unowned.get(), owned_ptr); |
| EXPECT_EQ(owned.get(), unowned_ptr); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, IsOwned) { |
| pw::MaybeSharedPtr<int> empty; |
| EXPECT_FALSE(empty.is_owned()); |
| |
| pw::MaybeSharedPtr<int> null_init(nullptr); |
| EXPECT_FALSE(null_init.is_owned()); |
| |
| int val = 10; |
| pw::MaybeSharedPtr<int> unowned = pw::Unowned(val); |
| EXPECT_FALSE(unowned.is_owned()); |
| |
| pw::SharedPtr<int> shared = allocator_.MakeShared<int>(20); |
| pw::MaybeSharedPtr<int> owned(shared); |
| EXPECT_TRUE(owned.is_owned()); |
| |
| pw::MaybeSharedPtr<int> moved = std::move(owned); |
| EXPECT_TRUE(moved.is_owned()); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_FALSE(owned.is_owned()); |
| |
| moved.reset(); |
| EXPECT_FALSE(moved.is_owned()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CopyAssignOwned) { |
| pw::MaybeSharedPtr<Counter> ptr1(allocator_.MakeShared<Counter>(10u)); |
| pw::MaybeSharedPtr<Counter> ptr2; |
| ptr2 = ptr1; |
| EXPECT_EQ(ptr1.get(), ptr2.get()); |
| EXPECT_TRUE(ptr2.is_owned()); |
| |
| ptr1.reset(); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| |
| ptr2.reset(); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CopyAssignDecreasesOldUseCount) { |
| pw::MaybeSharedPtr<Counter> ptr1(allocator_.MakeShared<Counter>(10u)); |
| pw::MaybeSharedPtr<Counter> ptr2(allocator_.MakeShared<Counter>(20u)); |
| EXPECT_EQ(Counter::TakeNumCtorCalls(), 2u); |
| |
| ptr2 = ptr1; |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| EXPECT_EQ(ptr1.get(), ptr2.get()); |
| |
| ptr1.reset(); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| |
| ptr2.reset(); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CopyAssignUnowned) { |
| Counter counter1(10u); |
| Counter counter2(20u); |
| pw::MaybeSharedPtr<Counter> ptr1 = pw::Unowned(counter1); |
| pw::MaybeSharedPtr<Counter> ptr2 = pw::Unowned(counter2); |
| |
| ptr2 = ptr1; |
| EXPECT_EQ(ptr2.get(), &counter1); |
| EXPECT_FALSE(ptr2.is_owned()); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CopyAssignUnownedToOwnedFreesOldObject) { |
| Counter counter(10u); |
| pw::MaybeSharedPtr<Counter> ptr1 = pw::Unowned(counter); |
| pw::MaybeSharedPtr<Counter> ptr2(allocator_.MakeShared<Counter>(20u)); |
| EXPECT_TRUE(ptr2.is_owned()); |
| |
| ptr2 = ptr1; |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| EXPECT_EQ(ptr2.get(), &counter); |
| EXPECT_FALSE(ptr2.is_owned()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CopyAssignOwnedToUnownedDoesNotFreeUnowned) { |
| Counter counter(10u); |
| pw::MaybeSharedPtr<Counter> ptr1(allocator_.MakeShared<Counter>(20u)); |
| pw::MaybeSharedPtr<Counter> ptr2 = pw::Unowned(counter); |
| EXPECT_FALSE(ptr2.is_owned()); |
| |
| ptr2 = ptr1; |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| EXPECT_EQ(ptr2.get(), ptr1.get()); |
| EXPECT_TRUE(ptr2.is_owned()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, SelfCopyAssignOwned) { |
| pw::MaybeSharedPtr<Counter> ptr(allocator_.MakeShared<Counter>(42u)); |
| ASSERT_NE(ptr, nullptr); |
| EXPECT_TRUE(ptr.is_owned()); |
| EXPECT_EQ(ptr->value(), 42u); |
| EXPECT_EQ(Counter::TakeNumCtorCalls(), 1u); |
| |
| ptr = *&ptr; |
| |
| EXPECT_NE(ptr, nullptr); |
| EXPECT_TRUE(ptr.is_owned()); |
| EXPECT_EQ(ptr->value(), 42u); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| |
| ptr.reset(); |
| EXPECT_EQ(ptr, nullptr); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, SelfCopyAssignUnowned) { |
| Counter counter(99u); |
| pw::MaybeSharedPtr<Counter> ptr = pw::Unowned(counter); |
| ASSERT_NE(ptr, nullptr); |
| EXPECT_FALSE(ptr.is_owned()); |
| EXPECT_EQ(ptr.get(), &counter); |
| EXPECT_EQ(ptr->value(), 99u); |
| |
| ptr = *&ptr; |
| |
| EXPECT_NE(ptr, nullptr); |
| EXPECT_FALSE(ptr.is_owned()); |
| EXPECT_EQ(ptr.get(), &counter); |
| EXPECT_EQ(ptr->value(), 99u); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConvertingSelfCopyAssignOwned) { |
| pw::MaybeSharedPtr<Counter> ptr(allocator_.MakeShared<Counter>(42u)); |
| ASSERT_NE(ptr, nullptr); |
| EXPECT_TRUE(ptr.is_owned()); |
| EXPECT_EQ(ptr->value(), 42u); |
| |
| ptr.operator= <Counter>(ptr); |
| |
| EXPECT_NE(ptr, nullptr); |
| EXPECT_TRUE(ptr.is_owned()); |
| EXPECT_EQ(ptr->value(), 42u); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConvertingSelfCopyAssignUnowned) { |
| Counter counter(99u); |
| pw::MaybeSharedPtr<Counter> ptr = pw::Unowned(counter); |
| ASSERT_NE(ptr, nullptr); |
| EXPECT_FALSE(ptr.is_owned()); |
| EXPECT_EQ(ptr.get(), &counter); |
| EXPECT_EQ(ptr->value(), 99u); |
| |
| ptr.operator= <Counter>(ptr); |
| |
| EXPECT_NE(ptr, nullptr); |
| EXPECT_FALSE(ptr.is_owned()); |
| EXPECT_EQ(ptr.get(), &counter); |
| EXPECT_EQ(ptr->value(), 99u); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, MoveAssignOwned) { |
| pw::MaybeSharedPtr<Counter> ptr1(allocator_.MakeShared<Counter>(10u)); |
| Counter* raw = ptr1.get(); |
| pw::MaybeSharedPtr<Counter> ptr2; |
| |
| ptr2 = std::move(ptr1); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(ptr1, nullptr); |
| EXPECT_EQ(ptr2.get(), raw); |
| EXPECT_TRUE(ptr2.is_owned()); |
| |
| ptr2.reset(); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, MoveAssignDecreasesOldUseCount) { |
| pw::MaybeSharedPtr<Counter> ptr1(allocator_.MakeShared<Counter>(10u)); |
| pw::MaybeSharedPtr<Counter> ptr2(allocator_.MakeShared<Counter>(20u)); |
| Counter* raw1 = ptr1.get(); |
| |
| ptr2 = std::move(ptr1); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(ptr1, nullptr); |
| EXPECT_EQ(ptr2.get(), raw1); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, MoveAssignUnowned) { |
| Counter counter(10u); |
| pw::MaybeSharedPtr<Counter> ptr1 = pw::Unowned(counter); |
| pw::MaybeSharedPtr<Counter> ptr2; |
| |
| ptr2 = std::move(ptr1); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(ptr1, nullptr); |
| EXPECT_EQ(ptr2.get(), &counter); |
| EXPECT_FALSE(ptr2.is_owned()); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CopyAssignSharedPtr) { |
| pw::SharedPtr<Counter> shared1 = allocator_.MakeShared<Counter>(42u); |
| pw::MaybeSharedPtr<Counter> maybe; |
| |
| maybe = shared1; |
| EXPECT_EQ(maybe.get(), shared1.get()); |
| EXPECT_TRUE(maybe.is_owned()); |
| |
| pw::SharedPtr<Counter> shared2 = allocator_.MakeShared<Counter>(99u); |
| maybe = shared2; |
| EXPECT_EQ(maybe.get(), shared2.get()); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| |
| shared1 = nullptr; |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, MoveAssignSharedPtr) { |
| pw::SharedPtr<Counter> shared = allocator_.MakeShared<Counter>(42u); |
| Counter* raw = shared.get(); |
| pw::MaybeSharedPtr<Counter> maybe; |
| |
| maybe = std::move(shared); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(shared, nullptr); |
| EXPECT_EQ(maybe.get(), raw); |
| EXPECT_TRUE(maybe.is_owned()); |
| |
| maybe.reset(); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConvertingMoveConstructorForOwned) { |
| pw::MaybeSharedPtr<Baz> baz(allocator_.MakeShared<Baz>()); |
| Baz* raw = baz.get(); |
| pw::MaybeSharedPtr<Foo> foo(std::move(baz)); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(baz, nullptr); |
| EXPECT_EQ(foo.get(), raw); |
| EXPECT_TRUE(foo.is_owned()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConvertingMoveConstructorForUnowned) { |
| Baz static_baz; |
| pw::MaybeSharedPtr<Baz> baz = pw::Unowned(static_baz); |
| pw::MaybeSharedPtr<Foo> foo(std::move(baz)); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(baz, nullptr); |
| EXPECT_EQ(foo.get(), &static_baz); |
| EXPECT_FALSE(foo.is_owned()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConvertingAssignments) { |
| pw::MaybeSharedPtr<Baz> baz(allocator_.MakeShared<Baz>()); |
| pw::MaybeSharedPtr<Foo> foo; |
| foo = baz; |
| EXPECT_EQ(foo.get(), baz.get()); |
| EXPECT_TRUE(foo.is_owned()); |
| |
| pw::MaybeSharedPtr<Baz> baz2(allocator_.MakeShared<Baz>()); |
| Baz* raw2 = baz2.get(); |
| foo = std::move(baz2); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(baz2, nullptr); |
| EXPECT_EQ(foo.get(), raw2); |
| |
| pw::SharedPtr<Baz> shared_baz = allocator_.MakeShared<Baz>(); |
| foo = shared_baz; |
| EXPECT_EQ(foo.get(), shared_baz.get()); |
| |
| pw::SharedPtr<Baz> shared_baz2 = allocator_.MakeShared<Baz>(); |
| Baz* raw_shared2 = shared_baz2.get(); |
| foo = std::move(shared_baz2); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(shared_baz2, nullptr); |
| EXPECT_EQ(foo.get(), raw_shared2); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, AssignNullptrToOwnedFreesObject) { |
| pw::MaybeSharedPtr<Counter> maybe(allocator_.MakeShared<Counter>(42u)); |
| EXPECT_TRUE(maybe.is_owned()); |
| |
| maybe = nullptr; |
| EXPECT_EQ(maybe, nullptr); |
| EXPECT_FALSE(maybe.is_owned()); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, AssignNullptrToUnownedDoesNotDestroyObject) { |
| Counter counter(42u); |
| pw::MaybeSharedPtr<Counter> maybe = pw::Unowned(counter); |
| EXPECT_FALSE(maybe.is_owned()); |
| |
| maybe = nullptr; |
| EXPECT_EQ(maybe, nullptr); |
| EXPECT_FALSE(maybe.is_owned()); |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CanExplicitlyDownCastOwned) { |
| pw::MaybeSharedPtr<Foo> foo(allocator_.MakeShared<Baz>()); |
| pw::MaybeSharedPtr<Baz> baz = static_cast<pw::MaybeSharedPtr<Baz>>(foo); |
| EXPECT_EQ(baz->foo(), 1); |
| EXPECT_EQ(baz->bar(), 2); |
| EXPECT_EQ(baz->baz(), 3); |
| EXPECT_TRUE(baz.is_owned()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CanExplicitlyDownCastUnowned) { |
| Baz static_baz; |
| pw::MaybeSharedPtr<Foo> foo = pw::Unowned<Foo>(static_baz); |
| pw::MaybeSharedPtr<Baz> baz = static_cast<pw::MaybeSharedPtr<Baz>>(foo); |
| EXPECT_EQ(baz->foo(), 1); |
| EXPECT_EQ(baz->bar(), 2); |
| EXPECT_EQ(baz->baz(), 3); |
| EXPECT_FALSE(baz.is_owned()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ConstPointerCastUnowned) { |
| Baz static_baz; |
| pw::MaybeSharedPtr<const Baz> const_baz = pw::Unowned(static_baz); |
| pw::MaybeSharedPtr<Baz> non_const_baz = |
| pw::const_pointer_cast<Baz>(const_baz); |
| EXPECT_EQ(non_const_baz.get(), &static_baz); |
| EXPECT_FALSE(non_const_baz.is_owned()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, PointerCastsOnNull) { |
| pw::MaybeSharedPtr<Foo> empty_foo; |
| pw::MaybeSharedPtr<Baz> cast_baz = pw::static_pointer_cast<Baz>(empty_foo); |
| EXPECT_EQ(cast_baz, nullptr); |
| EXPECT_FALSE(cast_baz.is_owned()); |
| |
| pw::MaybeSharedPtr<const Foo> empty_const_foo; |
| pw::MaybeSharedPtr<Foo> cast_non_const = |
| pw::const_pointer_cast<Foo>(empty_const_foo); |
| EXPECT_EQ(cast_non_const, nullptr); |
| EXPECT_FALSE(cast_non_const.is_owned()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, SwapWhenOneIsEmpty) { |
| Counter counter(111u); |
| pw::MaybeSharedPtr<Counter> ptr1 = pw::Unowned(counter); |
| pw::MaybeSharedPtr<Counter> ptr2; |
| |
| ptr1.swap(ptr2); |
| EXPECT_EQ(ptr1, nullptr); |
| EXPECT_EQ(ptr2.get(), &counter); |
| |
| ptr1.swap(ptr2); |
| EXPECT_EQ(ptr1.get(), &counter); |
| EXPECT_EQ(ptr2, nullptr); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, SwapWhenBothAreEmpty) { |
| pw::MaybeSharedPtr<Counter> ptr1; |
| pw::MaybeSharedPtr<Counter> ptr2; |
| ptr1.swap(ptr2); |
| EXPECT_EQ(ptr1, nullptr); |
| EXPECT_EQ(ptr2, nullptr); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, ResetEmptyPointerIsNoOp) { |
| pw::MaybeSharedPtr<Counter> empty; |
| empty.reset(); |
| EXPECT_EQ(empty, nullptr); |
| EXPECT_FALSE(empty.is_owned()); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CompareEmptyWithEmpty) { |
| pw::MaybeSharedPtr<Counter> empty1; |
| pw::MaybeSharedPtr<Counter> empty2; |
| EXPECT_EQ(empty1, empty2); |
| EXPECT_FALSE(empty1 != empty2); |
| |
| pw::SharedPtr<Counter> empty_shared; |
| EXPECT_EQ(empty1, empty_shared); |
| EXPECT_EQ(empty_shared, empty1); |
| EXPECT_FALSE(empty1 != empty_shared); |
| EXPECT_FALSE(empty_shared != empty1); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CompareOwnedWithNullptr) { |
| pw::MaybeSharedPtr<Counter> owned(allocator_.MakeShared<Counter>(42u)); |
| EXPECT_NE(owned, nullptr); |
| EXPECT_NE(nullptr, owned); |
| EXPECT_FALSE(owned == nullptr); |
| EXPECT_FALSE(nullptr == owned); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, CompareDifferentTypes) { |
| pw::SharedPtr<Baz> baz = allocator_.MakeShared<Baz>(); |
| pw::MaybeSharedPtr<Foo> foo = baz; |
| pw::MaybeSharedPtr<Bar> bar = baz; |
| EXPECT_EQ(foo, bar); |
| EXPECT_EQ(bar, foo); |
| EXPECT_EQ(foo, baz); |
| EXPECT_EQ(baz, foo); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, FreedExactlyOnceWithMultipleMaybeSharedPtr) { |
| auto shared = allocator_.MakeShared<Counter>(42u); |
| EXPECT_EQ(Counter::TakeNumCtorCalls(), 1u); |
| |
| pw::MaybeSharedPtr<Counter> maybe1 = shared; |
| pw::MaybeSharedPtr<Counter> maybe2 = maybe1; |
| |
| shared = nullptr; |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| |
| maybe1 = nullptr; |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 0u); |
| |
| maybe2 = nullptr; |
| EXPECT_EQ(Counter::TakeNumDtorCalls(), 1u); |
| } |
| |
| TEST_F(MaybeSharedPtrTest, AllocatorUpdatesOnResetAndMove) { |
| pw::MaybeSharedPtr<Counter> owned(allocator_.MakeShared<Counter>(10u)); |
| EXPECT_EQ(owned.allocator(), &allocator_); |
| |
| pw::MaybeSharedPtr<Counter> moved = std::move(owned); |
| // NOLINTNEXTLINE(bugprone-use-after-move) |
| EXPECT_EQ(owned.allocator(), nullptr); |
| EXPECT_EQ(moved.allocator(), &allocator_); |
| |
| moved.reset(); |
| EXPECT_EQ(moved.allocator(), nullptr); |
| } |
| |
| } // namespace |
| |
| #endif // PW_ALLOCATOR_HAS_ATOMICS |