284 lines
9.1 KiB
C++
284 lines
9.1 KiB
C++
|
|
// 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 <utility>
|
||
|
|
|
||
|
|
#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"
|
||
|
|
#include "third_party/abseil-cpp/absl/types/optional.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<WithWeak> self{this};
|
||
|
|
base::WeakPtrFactory<WithWeak> factory{this};
|
||
|
|
};
|
||
|
|
|
||
|
|
TEST(SafeRefTest, FromWeakPtrFactory) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<WithWeak> safe(with.factory.GetSafeRef());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefTest, Operators) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<WithWeak> 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, CanCopyAndMove) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<WithWeak> safe(with.factory.GetSafeRef());
|
||
|
|
EXPECT_EQ(safe->self->i, 1); // Will crash if not live.
|
||
|
|
SafeRef<WithWeak> 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<WithWeak> 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<WithWeak> safe(with.factory.GetSafeRef());
|
||
|
|
SafeRef<WithWeak> safe2(with2.factory.GetSafeRef());
|
||
|
|
EXPECT_NE(safe->self, &with2);
|
||
|
|
safe = safe2;
|
||
|
|
EXPECT_EQ(safe->self, &with2);
|
||
|
|
|
||
|
|
SafeRef<WithWeak> safe3(with3.factory.GetSafeRef());
|
||
|
|
EXPECT_NE(safe->self, &with3);
|
||
|
|
safe = std::move(safe3);
|
||
|
|
EXPECT_EQ(safe->self, &with3);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefDeathTest, ArrowOperatorCrashIfBadPointer) {
|
||
|
|
absl::optional<WithWeak> with(absl::in_place);
|
||
|
|
SafeRef<WithWeak> safe(with->factory.GetSafeRef());
|
||
|
|
with.reset();
|
||
|
|
EXPECT_CHECK_DEATH(safe.operator->()); // Will crash since not live.
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefDeathTest, StarOperatorCrashIfBadPointer) {
|
||
|
|
absl::optional<WithWeak> with(absl::in_place);
|
||
|
|
SafeRef<WithWeak> safe(with->factory.GetSafeRef());
|
||
|
|
with.reset();
|
||
|
|
EXPECT_CHECK_DEATH(safe.operator*()); // Will crash since not live.
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefTest, ConversionToBaseClassFromCopyConstruct) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<WithWeak> safe(with.factory.GetSafeRef());
|
||
|
|
SafeRef<OtherBaseClass> base_safe = safe;
|
||
|
|
EXPECT_EQ(static_cast<WithWeak*>(&*base_safe), &with);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefTest, ConversionToBaseClassFromMoveConstruct) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<WithWeak> safe(with.factory.GetSafeRef());
|
||
|
|
SafeRef<OtherBaseClass> base_safe = std::move(safe);
|
||
|
|
EXPECT_EQ(static_cast<WithWeak*>(&*base_safe), &with);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefTest, ConversionToBaseClassFromCopyAssign) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<WithWeak> safe(with.factory.GetSafeRef());
|
||
|
|
SafeRef<OtherBaseClass> base_safe(with.factory.GetSafeRef());
|
||
|
|
base_safe = safe;
|
||
|
|
EXPECT_EQ(static_cast<WithWeak*>(&*base_safe), &with);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefTest, ConversionToBaseClassFromMoveAssign) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<WithWeak> safe(with.factory.GetSafeRef());
|
||
|
|
SafeRef<OtherBaseClass> base_safe(with.factory.GetSafeRef());
|
||
|
|
base_safe = std::move(safe);
|
||
|
|
EXPECT_EQ(static_cast<WithWeak*>(&*base_safe), &with);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefTest, CanDerefConst) {
|
||
|
|
WithWeak with;
|
||
|
|
const SafeRef<WithWeak> safe(with.factory.GetSafeRef());
|
||
|
|
EXPECT_EQ(safe->self->i, 1);
|
||
|
|
EXPECT_EQ((*safe).self->i, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefTest, InvalidAfterMoveConstruction) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<WithWeak> safe(with.factory.GetSafeRef());
|
||
|
|
SafeRef<WithWeak> 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<WithWeak> safe3(safe)); }
|
||
|
|
{
|
||
|
|
SafeRef<WithWeak> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = safe);
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<WithWeak> safe3(std::move(safe))); }
|
||
|
|
{
|
||
|
|
SafeRef<WithWeak> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = std::move(safe));
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(safe)); }
|
||
|
|
{
|
||
|
|
SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = safe);
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(std::move(safe))); }
|
||
|
|
{
|
||
|
|
SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = std::move(safe));
|
||
|
|
}
|
||
|
|
EXPECT_CHECK_DEATH((void)safe->self->i);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefTest, InvalidAfterMoveAssignment) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<WithWeak> safe(with.factory.GetSafeRef());
|
||
|
|
SafeRef<WithWeak> 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<WithWeak> safe3(safe)); }
|
||
|
|
{
|
||
|
|
SafeRef<WithWeak> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = safe);
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<WithWeak> safe3(std::move(safe))); }
|
||
|
|
{
|
||
|
|
SafeRef<WithWeak> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = std::move(safe));
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(safe)); }
|
||
|
|
{
|
||
|
|
SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = safe);
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(std::move(safe))); }
|
||
|
|
{
|
||
|
|
SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = std::move(safe));
|
||
|
|
}
|
||
|
|
EXPECT_CHECK_DEATH((void)safe->self->i);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefTest, InvalidAfterMoveConversionConstruction) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<BaseClass> safe(with.factory.GetSafeRef());
|
||
|
|
SafeRef<BaseClass> safe2 = std::move(safe);
|
||
|
|
// Will crash if not live.
|
||
|
|
EXPECT_EQ(static_cast<WithWeak*>(&*safe2)->self->i, 1);
|
||
|
|
// `safe` was previously moved-from, so using it in any way should crash now.
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(safe)); }
|
||
|
|
{
|
||
|
|
SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = safe);
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(std::move(safe))); }
|
||
|
|
{
|
||
|
|
SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = std::move(safe));
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<ReallyBaseClass> safe3(safe)); }
|
||
|
|
{
|
||
|
|
SafeRef<ReallyBaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = safe);
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<ReallyBaseClass> safe3(std::move(safe))); }
|
||
|
|
{
|
||
|
|
SafeRef<ReallyBaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = std::move(safe));
|
||
|
|
}
|
||
|
|
EXPECT_CHECK_DEATH((void)static_cast<WithWeak*>(&*safe)->self->i);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(SafeRefTest, InvalidAfterMoveConversionAssignment) {
|
||
|
|
WithWeak with;
|
||
|
|
SafeRef<BaseClass> safe(with.factory.GetSafeRef());
|
||
|
|
SafeRef<BaseClass> safe2(with.factory.GetSafeRef());
|
||
|
|
safe2 = std::move(safe);
|
||
|
|
// // Will crash if not live.
|
||
|
|
EXPECT_EQ(static_cast<WithWeak*>(&*safe2)->self->i, 1);
|
||
|
|
// `safe` was previously moved-from, so using it in any way should crash now.
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(safe)); }
|
||
|
|
{
|
||
|
|
SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = safe);
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(std::move(safe))); }
|
||
|
|
{
|
||
|
|
SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = std::move(safe));
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<ReallyBaseClass> safe3(safe)); }
|
||
|
|
{
|
||
|
|
SafeRef<ReallyBaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = safe);
|
||
|
|
}
|
||
|
|
{ EXPECT_CHECK_DEATH(SafeRef<ReallyBaseClass> safe3(std::move(safe))); }
|
||
|
|
{
|
||
|
|
SafeRef<ReallyBaseClass> safe3(with.factory.GetSafeRef());
|
||
|
|
EXPECT_CHECK_DEATH(safe3 = std::move(safe));
|
||
|
|
}
|
||
|
|
EXPECT_CHECK_DEATH((void)static_cast<WithWeak*>(&*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<WithWeak>();
|
||
|
|
SafeRef<WithWeak> 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);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
} // namespace base
|