// Copyright 2021 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/memory/safe_ref.h" #include #include #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/memory/raw_ptr_exclusion.h" #include "base/memory/weak_ptr.h" #include "base/test/gtest_util.h" #include "base/test/memory/dangling_ptr_instrumentation.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { namespace { struct ReallyBaseClass {}; struct BaseClass : ReallyBaseClass { virtual ~BaseClass() = default; void VirtualMethod() {} }; struct OtherBaseClass { virtual ~OtherBaseClass() = default; virtual void VirtualMethod() {} }; struct WithWeak final : BaseClass, OtherBaseClass { ~WithWeak() final { self = nullptr; } void Method() {} int i = 1; raw_ptr self{this}; base::WeakPtrFactory factory{this}; }; TEST(SafeRefTest, FromWeakPtrFactory) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); } TEST(SafeRefTest, Operators) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); // operator->. EXPECT_EQ(safe->self->i, 1); // Will crash if not live. // operator*. EXPECT_EQ((*safe).self->i, 1); // Will crash if not live. } TEST(SafeRefTest, ThreeWayComparison) { WithWeak with1; SafeRef safe1(with1.factory.GetSafeRef()); WithWeak with2; SafeRef safe2(with2.factory.GetSafeRef()); EXPECT_EQ(&with1 <=> &with2, safe1 <=> safe2); } TEST(SafeRefTest, CanCopyAndMove) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); EXPECT_EQ(safe->self->i, 1); // Will crash if not live. SafeRef safe2 = safe; // Copy. EXPECT_EQ(safe2->self->i, 1); // Will crash if not live. EXPECT_EQ(safe->self->i, 1); // Will crash if not live. SafeRef safe3 = std::move(safe); // Move. EXPECT_EQ(safe3->self->i, 1); // Will crash if not live. } TEST(SafeRefTest, AssignCopyAndMove) { WithWeak with; WithWeak with2; WithWeak with3; // Ensure `with`s outlive `safe`s SafeRef safe(with.factory.GetSafeRef()); SafeRef safe2(with2.factory.GetSafeRef()); EXPECT_NE(safe->self, &with2); safe = safe2; EXPECT_EQ(safe->self, &with2); SafeRef safe3(with3.factory.GetSafeRef()); EXPECT_NE(safe->self, &with3); safe = std::move(safe3); EXPECT_EQ(safe->self, &with3); } TEST(SafeRefDeathTest, ArrowOperatorCrashIfBadPointer) { std::optional with(std::in_place); SafeRef safe(with->factory.GetSafeRef()); with.reset(); EXPECT_CHECK_DEATH(safe.operator->()); // Will crash since not live. } TEST(SafeRefDeathTest, StarOperatorCrashIfBadPointer) { std::optional with(std::in_place); SafeRef safe(with->factory.GetSafeRef()); with.reset(); EXPECT_CHECK_DEATH(safe.operator*()); // Will crash since not live. } TEST(SafeRefTest, ConversionToBaseClassFromCopyConstruct) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); SafeRef base_safe = safe; EXPECT_EQ(static_cast(&*base_safe), &with); } TEST(SafeRefTest, ConversionToBaseClassFromMoveConstruct) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); SafeRef base_safe = std::move(safe); EXPECT_EQ(static_cast(&*base_safe), &with); } TEST(SafeRefTest, ConversionToBaseClassFromCopyAssign) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); SafeRef base_safe(with.factory.GetSafeRef()); base_safe = safe; EXPECT_EQ(static_cast(&*base_safe), &with); } TEST(SafeRefTest, ConversionToBaseClassFromMoveAssign) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); SafeRef base_safe(with.factory.GetSafeRef()); base_safe = std::move(safe); EXPECT_EQ(static_cast(&*base_safe), &with); } TEST(SafeRefTest, CanDerefConst) { WithWeak with; const SafeRef safe(with.factory.GetSafeRef()); EXPECT_EQ(safe->self->i, 1); EXPECT_EQ((*safe).self->i, 1); } TEST(SafeRefTest, InvalidAfterMoveConstruction) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); SafeRef safe2 = std::move(safe); // Will crash if not live. EXPECT_EQ(safe2->self->i, 1); // `safe` was previously moved-from, so using it in any way should crash now. { EXPECT_CHECK_DEATH(SafeRef safe3(safe)); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = safe); } { EXPECT_CHECK_DEATH(SafeRef safe3(std::move(safe))); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = std::move(safe)); } { EXPECT_CHECK_DEATH(SafeRef safe3(safe)); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = safe); } { EXPECT_CHECK_DEATH(SafeRef safe3(std::move(safe))); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = std::move(safe)); } EXPECT_CHECK_DEATH((void)safe->self->i); } TEST(SafeRefTest, InvalidAfterMoveAssignment) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); SafeRef safe2(with.factory.GetSafeRef()); safe2 = std::move(safe); // Will crash if not live. EXPECT_EQ(safe2->self->i, 1); // `safe` was previously moved-from, so using it in any way should crash now. { EXPECT_CHECK_DEATH(SafeRef safe3(safe)); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = safe); } { EXPECT_CHECK_DEATH(SafeRef safe3(std::move(safe))); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = std::move(safe)); } { EXPECT_CHECK_DEATH(SafeRef safe3(safe)); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = safe); } { EXPECT_CHECK_DEATH(SafeRef safe3(std::move(safe))); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = std::move(safe)); } EXPECT_CHECK_DEATH((void)safe->self->i); } TEST(SafeRefTest, InvalidAfterMoveConversionConstruction) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); SafeRef safe2 = std::move(safe); // Will crash if not live. EXPECT_EQ(static_cast(&*safe2)->self->i, 1); // `safe` was previously moved-from, so using it in any way should crash now. { EXPECT_CHECK_DEATH(SafeRef safe3(safe)); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = safe); } { EXPECT_CHECK_DEATH(SafeRef safe3(std::move(safe))); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = std::move(safe)); } { EXPECT_CHECK_DEATH(SafeRef safe3(safe)); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = safe); } { EXPECT_CHECK_DEATH(SafeRef safe3(std::move(safe))); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = std::move(safe)); } EXPECT_CHECK_DEATH((void)static_cast(&*safe)->self->i); } TEST(SafeRefTest, InvalidAfterMoveConversionAssignment) { WithWeak with; SafeRef safe(with.factory.GetSafeRef()); SafeRef safe2(with.factory.GetSafeRef()); safe2 = std::move(safe); // // Will crash if not live. EXPECT_EQ(static_cast(&*safe2)->self->i, 1); // `safe` was previously moved-from, so using it in any way should crash now. { EXPECT_CHECK_DEATH(SafeRef safe3(safe)); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = safe); } { EXPECT_CHECK_DEATH(SafeRef safe3(std::move(safe))); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = std::move(safe)); } { EXPECT_CHECK_DEATH(SafeRef safe3(safe)); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = safe); } { EXPECT_CHECK_DEATH(SafeRef safe3(std::move(safe))); } { SafeRef safe3(with.factory.GetSafeRef()); EXPECT_CHECK_DEATH(safe3 = std::move(safe)); } EXPECT_CHECK_DEATH((void)static_cast(&*safe)->self->i); } TEST(SafeRefTest, Bind) { WithWeak with; BindOnce(&WithWeak::Method, with.factory.GetSafeRef()).Run(); } TEST(SafeRefTest, DanglingPointerDetector) { auto instrumentation = test::DanglingPtrInstrumentation::Create(); if (!instrumentation.has_value()) { GTEST_SKIP() << instrumentation.error(); } { auto with = std::make_unique(); SafeRef safe(with->factory.GetSafeRef()); EXPECT_EQ(instrumentation->dangling_ptr_detected(), 0u); EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); with.reset(); EXPECT_EQ(instrumentation->dangling_ptr_detected(), 1u); EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); } EXPECT_EQ(instrumentation->dangling_ptr_detected(), 1u); EXPECT_EQ(instrumentation->dangling_ptr_released(), 1u); } TEST(SafeRefTest, DanglingUntriaged) { auto instrumentation = test::DanglingPtrInstrumentation::Create(); if (!instrumentation.has_value()) { GTEST_SKIP() << instrumentation.error(); } { auto with = std::make_unique(); SafeRef safe( with->factory.GetSafeRef()); EXPECT_EQ(instrumentation->dangling_ptr_detected(), 0u); EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); with.reset(); EXPECT_EQ(instrumentation->dangling_ptr_detected(), 0u); EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); } EXPECT_EQ(instrumentation->dangling_ptr_detected(), 0u); EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); } } // namespace } // namespace base