#include "base/memory/weak_ptr.h"
#include <memory>
#include <string>
#include "base/debug/leak_annotations.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/gtest_util.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
WeakPtr<int> PassThru(WeakPtr<int> ptr) {
return ptr;
}
template <class T>
class OffThreadObjectCreator {
public:
static T* NewObject() {
T* result;
{
Thread creator_thread("creator_thread");
creator_thread.Start();
creator_thread.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(OffThreadObjectCreator::CreateObject, &result));
}
DCHECK(result);
return result;
}
private:
static void CreateObject(T** result) {
*result = new T;
}
};
struct Base {
std::string member;
};
struct Derived : public Base {};
struct TargetBase {};
struct Target : public TargetBase, public SupportsWeakPtr<Target> {
virtual ~Target() = default;
};
struct DerivedTarget : public Target {};
struct DerivedTargetWithNestedBase : public Target {
using Base = void;
};
struct VirtualDestructor {
virtual ~VirtualDestructor() = default;
};
struct DerivedTargetMultipleInheritance : public VirtualDestructor,
public Target {};
struct Arrow {
WeakPtr<Target> target;
};
struct TargetWithFactory : public Target {
TargetWithFactory() {}
WeakPtrFactory<Target> factory{this};
};
class BackgroundThread : public Thread {
public:
BackgroundThread() : Thread("owner_thread") {}
~BackgroundThread() override { Stop(); }
void CreateArrowFromTarget(Arrow** arrow, Target* target) {
WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
task_runner()->PostTask(
FROM_HERE, base::BindOnce(&BackgroundThread::DoCreateArrowFromTarget,
arrow, target, &completion));
completion.Wait();
}
void CreateArrowFromArrow(Arrow** arrow, const Arrow* other) {
WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
task_runner()->PostTask(
FROM_HERE, base::BindOnce(&BackgroundThread::DoCreateArrowFromArrow,
arrow, other, &completion));
completion.Wait();
}
void DeleteTarget(Target* object) {
WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&BackgroundThread::DoDeleteTarget, object, &completion));
completion.Wait();
}
void CopyAndAssignArrow(Arrow* object) {
WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
task_runner()->PostTask(
FROM_HERE, base::BindOnce(&BackgroundThread::DoCopyAndAssignArrow,
object, &completion));
completion.Wait();
}
void CopyAndAssignArrowBase(Arrow* object) {
WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
task_runner()->PostTask(
FROM_HERE, base::BindOnce(&BackgroundThread::DoCopyAndAssignArrowBase,
object, &completion));
completion.Wait();
}
void DeleteArrow(Arrow* object) {
WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&BackgroundThread::DoDeleteArrow, object, &completion));
completion.Wait();
}
Target* DeRef(const Arrow* arrow) {
WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
Target* result = nullptr;
task_runner()->PostTask(
FROM_HERE, base::BindOnce(&BackgroundThread::DoDeRef, arrow, &result,
&completion));
completion.Wait();
return result;
}
protected:
static void DoCreateArrowFromArrow(Arrow** arrow,
const Arrow* other,
WaitableEvent* completion) {
*arrow = new Arrow;
**arrow = *other;
completion->Signal();
}
static void DoCreateArrowFromTarget(Arrow** arrow,
Target* target,
WaitableEvent* completion) {
*arrow = new Arrow;
(*arrow)->target = target->AsWeakPtr();
completion->Signal();
}
static void DoDeRef(const Arrow* arrow,
Target** result,
WaitableEvent* completion) {
*result = arrow->target.get();
completion->Signal();
}
static void DoDeleteTarget(Target* object, WaitableEvent* completion) {
delete object;
completion->Signal();
}
static void DoCopyAndAssignArrow(Arrow* object, WaitableEvent* completion) {
Arrow a = *object;
*object = a;
completion->Signal();
}
static void DoCopyAndAssignArrowBase(
Arrow* object,
WaitableEvent* completion) {
WeakPtr<TargetBase> b = object->target;
WeakPtr<TargetBase> c;
c = object->target;
completion->Signal();
}
static void DoDeleteArrow(Arrow* object, WaitableEvent* completion) {
delete object;
completion->Signal();
}
};
}
TEST(WeakPtrFactoryTest, Basic) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
EXPECT_EQ(&data, ptr.get());
}
TEST(WeakPtrFactoryTest, Comparison) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
WeakPtr<int> ptr2 = ptr;
EXPECT_EQ(ptr.get(), ptr2.get());
}
TEST(WeakPtrFactoryTest, Move) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
WeakPtr<int> ptr2 = factory.GetWeakPtr();
WeakPtr<int> ptr3 = std::move(ptr2);
EXPECT_NE(ptr.get(), ptr2.get());
EXPECT_EQ(ptr.get(), ptr3.get());
}
TEST(WeakPtrFactoryTest, OutOfScope) {
WeakPtr<int> ptr;
EXPECT_EQ(nullptr, ptr.get());
{
int data;
WeakPtrFactory<int> factory(&data);
ptr = factory.GetWeakPtr();
}
EXPECT_EQ(nullptr, ptr.get());
}
TEST(WeakPtrFactoryTest, Multiple) {
WeakPtr<int> a, b;
{
int data;
WeakPtrFactory<int> factory(&data);
a = factory.GetWeakPtr();
b = factory.GetWeakPtr();
EXPECT_EQ(&data, a.get());
EXPECT_EQ(&data, b.get());
}
EXPECT_EQ(nullptr, a.get());
EXPECT_EQ(nullptr, b.get());
}
TEST(WeakPtrFactoryTest, MultipleStaged) {
WeakPtr<int> a;
{
int data;
WeakPtrFactory<int> factory(&data);
a = factory.GetWeakPtr();
{
WeakPtr<int> b = factory.GetWeakPtr();
}
EXPECT_NE(nullptr, a.get());
}
EXPECT_EQ(nullptr, a.get());
}
TEST(WeakPtrFactoryTest, Dereference) {
Base data;
data.member = "123456";
WeakPtrFactory<Base> factory(&data);
WeakPtr<Base> ptr = factory.GetWeakPtr();
EXPECT_EQ(&data, ptr.get());
EXPECT_EQ(data.member, (*ptr).member);
EXPECT_EQ(data.member, ptr->member);
}
TEST(WeakPtrFactoryTest, UpCast) {
Derived data;
WeakPtrFactory<Derived> factory(&data);
WeakPtr<Base> ptr = factory.GetWeakPtr();
ptr = factory.GetWeakPtr();
EXPECT_EQ(ptr.get(), &data);
}
TEST(WeakPtrTest, ConstructFromNullptr) {
WeakPtr<int> ptr = PassThru(nullptr);
EXPECT_EQ(nullptr, ptr.get());
}
TEST(WeakPtrTest, SupportsWeakPtr) {
Target target;
WeakPtr<Target> ptr = target.AsWeakPtr();
EXPECT_EQ(&target, ptr.get());
}
TEST(WeakPtrTest, DerivedTarget) {
DerivedTarget target;
WeakPtr<DerivedTarget> ptr = AsWeakPtr(&target);
EXPECT_EQ(&target, ptr.get());
}
TEST(WeakPtrTest, DerivedTargetWithNestedBase) {
DerivedTargetWithNestedBase target;
WeakPtr<DerivedTargetWithNestedBase> ptr = AsWeakPtr(&target);
EXPECT_EQ(&target, ptr.get());
}
TEST(WeakPtrTest, DerivedTargetMultipleInheritance) {
DerivedTargetMultipleInheritance derived_target;
Target& target = derived_target;
EXPECT_NE(static_cast<void*>(&derived_target), static_cast<void*>(&target));
WeakPtr<Target> target_weak_ptr = AsWeakPtr(&target);
EXPECT_EQ(target_weak_ptr.get(), &target);
WeakPtr<DerivedTargetMultipleInheritance> derived_target_weak_ptr =
AsWeakPtr(&derived_target);
EXPECT_EQ(derived_target_weak_ptr.get(), &derived_target);
target_weak_ptr = derived_target_weak_ptr;
EXPECT_EQ(target_weak_ptr.get(), &target);
target_weak_ptr = std::move(derived_target_weak_ptr);
EXPECT_EQ(target_weak_ptr.get(), &target);
}
TEST(WeakPtrFactoryTest, BooleanTesting) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr_to_an_instance = factory.GetWeakPtr();
EXPECT_TRUE(ptr_to_an_instance);
EXPECT_FALSE(!ptr_to_an_instance);
if (ptr_to_an_instance) {
} else {
ADD_FAILURE() << "Pointer to an instance should result in true.";
}
if (!ptr_to_an_instance) {
ADD_FAILURE() << "Pointer to an instance should result in !x being false.";
}
WeakPtr<int> null_ptr;
EXPECT_FALSE(null_ptr);
EXPECT_TRUE(!null_ptr);
if (null_ptr) {
ADD_FAILURE() << "Null pointer should result in false.";
}
if (!null_ptr) {
} else {
ADD_FAILURE() << "Null pointer should result in !x being true.";
}
}
TEST(WeakPtrFactoryTest, ComparisonToNull) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr_to_an_instance = factory.GetWeakPtr();
EXPECT_NE(nullptr, ptr_to_an_instance);
EXPECT_NE(ptr_to_an_instance, nullptr);
WeakPtr<int> null_ptr;
EXPECT_EQ(null_ptr, nullptr);
EXPECT_EQ(nullptr, null_ptr);
}
struct ReallyBaseClass {};
struct BaseClass : ReallyBaseClass {
virtual ~BaseClass() = default;
void VirtualMethod() {}
};
struct OtherBaseClass {
virtual ~OtherBaseClass() = default;
virtual void VirtualMethod() {}
};
struct WithWeak final : BaseClass, OtherBaseClass {
WeakPtrFactory<WithWeak> factory{this};
};
TEST(WeakPtrTest, ConversionOffsetsPointer) {
WithWeak with;
WeakPtr<WithWeak> ptr(with.factory.GetWeakPtr());
{
WeakPtr<OtherBaseClass> base_ptr(ptr);
EXPECT_EQ(static_cast<WithWeak*>(&*base_ptr), &with);
}
{
WeakPtr<OtherBaseClass> base_ptr(std::move(ptr));
EXPECT_EQ(static_cast<WithWeak*>(&*base_ptr), &with);
}
}
TEST(WeakPtrTest, InvalidateWeakPtrs) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
EXPECT_EQ(&data, ptr.get());
EXPECT_TRUE(factory.HasWeakPtrs());
factory.InvalidateWeakPtrs();
EXPECT_EQ(nullptr, ptr.get());
EXPECT_FALSE(factory.HasWeakPtrs());
WeakPtr<int> ptr2 = factory.GetWeakPtr();
EXPECT_EQ(&data, ptr2.get());
EXPECT_TRUE(factory.HasWeakPtrs());
factory.InvalidateWeakPtrs();
EXPECT_EQ(nullptr, ptr2.get());
EXPECT_FALSE(factory.HasWeakPtrs());
}
TEST(WeakPtrTest, WasInvalidatedByFactoryDestruction) {
WeakPtr<int> ptr;
EXPECT_FALSE(ptr.WasInvalidated());
{
int data = 0;
WeakPtrFactory<int> factory(&data);
ptr = factory.GetWeakPtr();
EXPECT_FALSE(ptr.WasInvalidated());
}
EXPECT_TRUE(ptr.WasInvalidated());
ptr = nullptr;
EXPECT_FALSE(ptr.WasInvalidated());
}
TEST(WeakPtrTest, WasInvalidatedByInvalidateWeakPtrs) {
int data = 0;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
EXPECT_FALSE(ptr.WasInvalidated());
factory.InvalidateWeakPtrs();
EXPECT_TRUE(ptr.WasInvalidated());
ptr = nullptr;
EXPECT_FALSE(ptr.WasInvalidated());
}
TEST(WeakPtrTest, WasInvalidatedWhilstNull) {
int data = 0;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
EXPECT_FALSE(ptr.WasInvalidated());
ptr = nullptr;
EXPECT_FALSE(ptr.WasInvalidated());
factory.InvalidateWeakPtrs();
EXPECT_FALSE(ptr.WasInvalidated());
}
TEST(WeakPtrTest, MaybeValidOnSameSequence) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
EXPECT_TRUE(ptr.MaybeValid());
factory.InvalidateWeakPtrs();
EXPECT_FALSE(ptr.MaybeValid());
}
TEST(WeakPtrTest, MaybeValidOnOtherSequence) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
EXPECT_TRUE(ptr.MaybeValid());
base::Thread other_thread("other_thread");
other_thread.StartAndWaitForTesting();
other_thread.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
[](WeakPtr<int> ptr) {
const TimeDelta timeout = TestTimeouts::tiny_timeout();
const TimeTicks begin = TimeTicks::Now();
while (ptr.MaybeValid() && (TimeTicks::Now() - begin) < timeout)
PlatformThread::YieldCurrentThread();
EXPECT_FALSE(ptr.MaybeValid());
},
ptr));
factory.InvalidateWeakPtrs();
}
TEST(WeakPtrTest, HasWeakPtrs) {
int data;
WeakPtrFactory<int> factory(&data);
{
WeakPtr<int> ptr = factory.GetWeakPtr();
EXPECT_TRUE(factory.HasWeakPtrs());
}
EXPECT_FALSE(factory.HasWeakPtrs());
}
TEST(WeakPtrTest, ObjectAndWeakPtrOnDifferentThreads) {
std::unique_ptr<Target> target(OffThreadObjectCreator<Target>::NewObject());
WeakPtr<Target> weak_ptr = target->AsWeakPtr();
EXPECT_EQ(target.get(), weak_ptr.get());
}
TEST(WeakPtrTest, WeakPtrInitiateAndUseOnDifferentThreads) {
std::unique_ptr<Arrow> arrow(OffThreadObjectCreator<Arrow>::NewObject());
Target target;
arrow->target = target.AsWeakPtr();
EXPECT_EQ(&target, arrow->target.get());
}
TEST(WeakPtrTest, MoveOwnershipImplicitly) {
BackgroundThread background;
background.Start();
Target* target = new Target();
{
WeakPtr<Target> weak_ptr = target->AsWeakPtr();
}
Arrow* arrow;
background.CreateArrowFromTarget(&arrow, target);
EXPECT_EQ(background.DeRef(arrow), target);
{
Arrow scoped_arrow;
scoped_arrow.target = target->AsWeakPtr();
EXPECT_EQ(target, background.DeRef(&scoped_arrow));
}
background.DeleteTarget(target);
background.DeleteArrow(arrow);
}
TEST(WeakPtrTest, MoveOwnershipOfUnreferencedObject) {
BackgroundThread background;
background.Start();
Arrow* arrow;
{
Target target;
background.CreateArrowFromTarget(&arrow, &target);
EXPECT_EQ(&target, background.DeRef(arrow));
arrow->target.reset();
arrow->target = target.AsWeakPtr();
EXPECT_EQ(&target, arrow->target.get());
}
delete arrow;
}
TEST(WeakPtrTest, MoveOwnershipAfterInvalidate) {
BackgroundThread background;
background.Start();
Arrow arrow;
std::unique_ptr<TargetWithFactory> target(new TargetWithFactory);
arrow.target = target->factory.GetWeakPtr();
EXPECT_EQ(target.get(), arrow.target.get());
target->factory.InvalidateWeakPtrs();
EXPECT_EQ(nullptr, arrow.target.get());
arrow.target = target->factory.GetWeakPtr();
EXPECT_EQ(target.get(), background.DeRef(&arrow));
background.DeleteTarget(target.release());
}
TEST(WeakPtrTest, MainThreadRefOutlivesBackgroundThreadRef) {
BackgroundThread background;
background.Start();
Target target;
Arrow arrow;
arrow.target = target.AsWeakPtr();
Arrow* arrow_copy;
background.CreateArrowFromArrow(&arrow_copy, &arrow);
EXPECT_EQ(arrow_copy->target.get(), &target);
background.DeleteArrow(arrow_copy);
}
TEST(WeakPtrTest, BackgroundThreadRefOutlivesMainThreadRef) {
BackgroundThread background;
background.Start();
Target target;
Arrow* arrow_copy;
{
Arrow arrow;
arrow.target = target.AsWeakPtr();
background.CreateArrowFromArrow(&arrow_copy, &arrow);
}
EXPECT_EQ(arrow_copy->target.get(), &target);
background.DeleteArrow(arrow_copy);
}
TEST(WeakPtrTest, OwnerThreadDeletesObject) {
BackgroundThread background;
background.Start();
Arrow* arrow_copy;
{
Target target;
Arrow arrow;
arrow.target = target.AsWeakPtr();
background.CreateArrowFromArrow(&arrow_copy, &arrow);
}
EXPECT_EQ(nullptr, arrow_copy->target.get());
background.DeleteArrow(arrow_copy);
}
TEST(WeakPtrTest, NonOwnerThreadCanCopyAndAssignWeakPtr) {
Target target;
Arrow *arrow = new Arrow();
arrow->target = target.AsWeakPtr();
BackgroundThread background;
background.Start();
background.CopyAndAssignArrow(arrow);
background.DeleteArrow(arrow);
}
TEST(WeakPtrTest, NonOwnerThreadCanCopyAndAssignWeakPtrBase) {
Target target;
Arrow *arrow = new Arrow();
arrow->target = target.AsWeakPtr();
BackgroundThread background;
background.Start();
background.CopyAndAssignArrowBase(arrow);
background.DeleteArrow(arrow);
}
TEST(WeakPtrTest, NonOwnerThreadCanDeleteWeakPtr) {
Target target;
Arrow* arrow = new Arrow();
arrow->target = target.AsWeakPtr();
BackgroundThread background;
background.Start();
background.DeleteArrow(arrow);
}
TEST(WeakPtrTest, ConstUpCast) {
Target target;
WeakPtr<const Target> const_weak_ptr = target.AsWeakPtr();
static_assert(
!std::is_constructible_v<WeakPtr<Target>, WeakPtr<const Target>>);
}
TEST(WeakPtrTest, ConstGetWeakPtr) {
struct TestTarget {
const char* Method() const { return "const method"; }
const char* Method() { return "non-const method"; }
WeakPtrFactory<TestTarget> weak_ptr_factory{this};
} non_const_test_target;
const TestTarget& const_test_target = non_const_test_target;
EXPECT_EQ(const_test_target.weak_ptr_factory.GetWeakPtr()->Method(),
"const method");
EXPECT_EQ(non_const_test_target.weak_ptr_factory.GetWeakPtr()->Method(),
"non-const method");
EXPECT_EQ(const_test_target.weak_ptr_factory.GetMutableWeakPtr()->Method(),
"non-const method");
}
TEST(WeakPtrTest, GetMutableWeakPtr) {
struct TestStruct {
int member = 0;
WeakPtrFactory<TestStruct> weak_ptr_factory{this};
};
TestStruct test_struct;
EXPECT_EQ(test_struct.member, 0);
const TestStruct& const_test_struct = test_struct;
WeakPtr<TestStruct> weak_ptr =
const_test_struct.weak_ptr_factory.GetMutableWeakPtr();
weak_ptr->member = 1;
EXPECT_EQ(test_struct.member, 1);
}
TEST(WeakPtrDeathTest, WeakPtrCopyDoesNotChangeThreadBinding) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
BackgroundThread background;
background.Start();
Target target;
Arrow arrow;
arrow.target = target.AsWeakPtr();
Arrow* arrow_copy;
background.CreateArrowFromArrow(&arrow_copy, &arrow);
EXPECT_EQ(arrow.target.get(), arrow_copy->target.get());
ASSERT_DCHECK_DEATH(background.DeRef(arrow_copy));
background.DeleteArrow(arrow_copy);
}
TEST(WeakPtrDeathTest, NonOwnerThreadDereferencesWeakPtrAfterReference) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
Target target;
Arrow arrow;
arrow.target = target.AsWeakPtr();
arrow.target.get();
BackgroundThread background;
background.Start();
ASSERT_DCHECK_DEATH(background.DeRef(&arrow));
}
TEST(WeakPtrDeathTest, NonOwnerThreadDeletesWeakPtrAfterReference) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
std::unique_ptr<Target> target(new Target());
Arrow arrow;
arrow.target = target->AsWeakPtr();
BackgroundThread background;
background.Start();
background.DeRef(&arrow);
ASSERT_DCHECK_DEATH(target.reset());
background.DeleteTarget(target.release());
}
TEST(WeakPtrDeathTest, NonOwnerThreadDeletesObjectAfterReference) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
std::unique_ptr<Target> target(new Target());
Arrow arrow;
arrow.target = target->AsWeakPtr();
arrow.target.get();
BackgroundThread background;
background.Start();
ASSERT_DCHECK_DEATH(background.DeleteTarget(target.release()));
}
TEST(WeakPtrDeathTest, NonOwnerThreadReferencesObjectAfterDeletion) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
std::unique_ptr<Target> target(new Target());
Arrow arrow;
arrow.target = target->AsWeakPtr();
BackgroundThread background;
background.Start();
background.DeleteTarget(target.release());
ASSERT_DCHECK_DEATH(arrow.target.get());
}
TEST(WeakPtrDeathTest, ArrowOperatorChecksOnBadDereference) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
auto target = std::make_unique<Target>();
WeakPtr<Target> weak = target->AsWeakPtr();
target.reset();
EXPECT_CHECK_DEATH(weak->AsWeakPtr());
}
TEST(WeakPtrDeathTest, StarOperatorChecksOnBadDereference) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
auto target = std::make_unique<Target>();
WeakPtr<Target> weak = target->AsWeakPtr();
target.reset();
EXPECT_CHECK_DEATH((*weak).AsWeakPtr());
}
}