blob: e1e6f1a5edbdad6a0e8d73ed46c86466412f9be6 [file] [edit]
// 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