chromium/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_unittest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "partition_alloc/pointers/raw_ptr.h"

#include <climits>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <thread>
#include <type_traits>
#include <utility>
#include <variant>

#include "base/allocator/partition_alloc_features.h"
#include "base/allocator/partition_alloc_support.h"
#include "base/cpu.h"
#include "base/metrics/histogram_base.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/memory/dangling_ptr_instrumentation.h"
#include "base/test/scoped_feature_list.h"
#include "base/types/to_address.h"
#include "partition_alloc/build_config.h"
#include "partition_alloc/buildflags.h"
#include "partition_alloc/dangling_raw_ptr_checks.h"
#include "partition_alloc/partition_alloc-inl.h"
#include "partition_alloc/partition_alloc.h"
#include "partition_alloc/partition_alloc_base/cpu.h"
#include "partition_alloc/partition_alloc_base/logging.h"
#include "partition_alloc/partition_alloc_base/numerics/checked_math.h"
#include "partition_alloc/partition_alloc_config.h"
#include "partition_alloc/partition_alloc_constants.h"
#include "partition_alloc/partition_alloc_hooks.h"
#include "partition_alloc/partition_root.h"
#include "partition_alloc/pointers/instance_tracer.h"
#include "partition_alloc/pointers/raw_ptr_counting_impl_for_test.h"
#include "partition_alloc/pointers/raw_ptr_test_support.h"
#include "partition_alloc/pointers/raw_ref.h"
#include "partition_alloc/tagging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

#if PA_BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
#include <sanitizer/asan_interface.h>
#include "base/debug/asan_service.h"
#endif

AllOf;
Eq;
HasSubstr;
IsEmpty;
Ne;
SizeIs;
Test;

// The instance tracer has unavoidable per-instance overhead, but when disabled,
// there should be no size difference between raw_ptr<T> and T*.
#if !PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER)
static_assert;
static_assert;
static_assert;
#endif

#if !PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) &&   \
    !PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) && \
    !PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) &&     \
    !PA_BUILDFLAG(RAW_PTR_ZERO_ON_MOVE) &&          \
    !PA_BUILDFLAG(RAW_PTR_ZERO_ON_DESTRUCT)
// |is_trivially_copyable| assertion means that arrays/vectors of raw_ptr can
// be copied by memcpy.
static_assert(std::is_trivially_copyable_v<raw_ptr<void>>,
              "raw_ptr should be trivially copyable");
static_assert(std::is_trivially_copyable_v<raw_ptr<int>>,
              "raw_ptr should be trivially copyable");
static_assert(std::is_trivially_copyable_v<raw_ptr<std::string>>,
              "raw_ptr should be trivially copyable");
#endif  // !PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) &&
        // !PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) &&
        // !PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) &&
        // !PA_BUILDFLAG(RAW_PTR_ZERO_ON_MOVE) &&
        // !PA_BUILDFLAG(RAW_PTR_ZERO_ON_DESTRUCT)

#if !PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) &&   \
    !PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) && \
    !PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) &&     \
    !PA_BUILDFLAG(RAW_PTR_ZERO_ON_CONSTRUCT) &&     \
    !PA_BUILDFLAG(RAW_PTR_ZERO_ON_DESTRUCT)
// |is_trivially_default_constructible| assertion helps retain implicit default
// constructors when raw_ptr is used as a union field.  Example of an error
// if this assertion didn't hold:
//
//     ../../base/trace_event/trace_arguments.h:249:16: error: call to
//     implicitly-deleted default constructor of 'base::trace_event::TraceValue'
//         TraceValue ret;
//                    ^
//     ../../base/trace_event/trace_arguments.h:211:26: note: default
//     constructor of 'TraceValue' is implicitly deleted because variant field
//     'as_pointer' has a non-trivial default constructor
//       raw_ptr<const void> as_pointer;
static_assert(std::is_trivially_default_constructible_v<raw_ptr<void>>,
              "raw_ptr should be trivially default constructible");
static_assert(std::is_trivially_default_constructible_v<raw_ptr<int>>,
              "raw_ptr should be trivially default constructible");
static_assert(std::is_trivially_default_constructible_v<raw_ptr<std::string>>,
              "raw_ptr should be trivially default constructible");
#endif  // !PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) &&
        // !PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) &&
        // !PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) &&
        // !PA_BUILDFLAG(RAW_PTR_ZERO_ON_CONSTRUCT) &&
        // !PA_BUILDFLAG(RAW_PTR_ZERO_ON_DESTRUCT)

// Verify that raw_ptr is a literal type, and its entire interface is constexpr.
//
// Constexpr destructors were introduced in C++20. PartitionAlloc's minimum
// supported C++ version is C++17, so raw_ptr is not a literal type in C++17.
// Thus we only test for constexpr in C++20.
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L
static_assert;
#endif

struct StructWithoutTypeBasedTraits {};
struct BaseWithTypeBasedTraits {};
struct DerivedWithTypeBasedTraits : BaseWithTypeBasedTraits {};

namespace base::raw_ptr_traits {
// `BaseWithTypeBasedTraits` and any derived classes have
// `RawPtrTraits::kDummyForTest`.
kTypeTraits;
}  // namespace base::raw_ptr_traits

// `raw_ptr<T>` should have traits based on specialization of `kTypeTraits<T>`.
static_assert;
static_assert;
static_assert;

// Don't use base::internal for testing raw_ptr API, to test if code outside
// this namespace calls the correct functions from this namespace.
namespace {

// Shorter name for expected test impl.
RawPtrCountingImpl;

CountingRawPtr;

// Ensure that the `kUseCountingImplForTest` flag selects the test impl.
static_assert;

CountingRawPtrMayDangle;

// Ensure that the `kUseCountingImplForTest` flag selects the test impl.
static_assert;

CountingRawPtrUninitialized;

// Ensure that the `kUseCountingImplForTest` flag selects the test impl.
static_assert;

struct MyStruct {};

struct Base1 {};

struct Base2 {};

struct Derived : Base1, Base2 {};

class RawPtrTest : public Test {};

// Use this instead of std::ignore, to prevent the instruction from getting
// optimized out by the compiler.
volatile int g_volatile_int_to_ignore;

TEST_F(RawPtrTest, NullStarDereference) {}

TEST_F(RawPtrTest, NullArrowDereference) {}

TEST_F(RawPtrTest, NullExtractNoDereference) {}

TEST_F(RawPtrTest, InvalidExtractNoDereference) {}

TEST_F(RawPtrTest, NullCmpExplicit) {}

TEST_F(RawPtrTest, NullCmpBool) {}

void FuncThatAcceptsBool(bool b) {}

bool IsValidNoCast(CountingRawPtr<int> ptr) {}
bool IsValidNoCast2(CountingRawPtr<int> ptr) {}

TEST_F(RawPtrTest, BoolOpNotCast) {}

bool IsValidWithCast(CountingRawPtr<int> ptr) {}

// This test is mostly for documentation purposes. It demonstrates cases where
// |operator T*| is called first and then the pointer is converted to bool,
// as opposed to calling |operator bool| directly. The former may be more
// costly, so the caller has to be careful not to trigger this path.
TEST_F(RawPtrTest, CastNotBoolOp) {}

TEST_F(RawPtrTest, StarDereference) {}

TEST_F(RawPtrTest, ArrowDereference) {}

TEST_F(RawPtrTest, Delete) {}

TEST_F(RawPtrTest, ClearAndDelete) {}

TEST_F(RawPtrTest, ClearAndDeleteArray) {}

TEST_F(RawPtrTest, ExtractAsDangling) {}

TEST_F(RawPtrTest, ExtractAsDanglingFromDangling) {}

TEST_F(RawPtrTest, ConstVolatileVoidPtr) {}

TEST_F(RawPtrTest, VoidPtr) {}

TEST_F(RawPtrTest, OperatorEQ) {}

TEST_F(RawPtrTest, OperatorNE) {}

TEST_F(RawPtrTest, OperatorEQCast) {}

TEST_F(RawPtrTest, OperatorEQCastHierarchy) {}

TEST_F(RawPtrTest, OperatorNECast) {}

TEST_F(RawPtrTest, OperatorNECastHierarchy) {}

TEST_F(RawPtrTest, Cast) {}

TEST_F(RawPtrTest, UpcastConvertible) {}

TEST_F(RawPtrTest, UpcastNotConvertible) {}

TEST_F(RawPtrTest, UpcastPerformance) {}

TEST_F(RawPtrTest, CustomSwap) {}

TEST_F(RawPtrTest, StdSwap) {}

TEST_F(RawPtrTest, PostIncrementOperator) {}

TEST_F(RawPtrTest, PostDecrementOperator) {}

TEST_F(RawPtrTest, PreIncrementOperator) {}

TEST_F(RawPtrTest, PreDecrementOperator) {}

TEST_F(RawPtrTest, PlusEqualOperator) {}

TEST_F(RawPtrTest, PlusEqualOperatorTypes) {}

TEST_F(RawPtrTest, MinusEqualOperator) {}

TEST_F(RawPtrTest, MinusEqualOperatorTypes) {}

TEST_F(RawPtrTest, PlusOperator) {}

TEST_F(RawPtrTest, MinusOperator) {}

TEST_F(RawPtrTest, MinusDeltaOperator) {}

TEST_F(RawPtrTest, AdvanceString) {}

TEST_F(RawPtrTest, AssignmentFromNullptr) {}

void FunctionWithRawPtrParameter(raw_ptr<int> actual_ptr, int* expected_ptr) {}

// This test checks that raw_ptr<T> can be passed by value into function
// parameters.  This is mostly a smoke test for TRIVIAL_ABI attribute.
TEST_F(RawPtrTest, FunctionParameters_ImplicitlyMovedTemporary) {}

// This test checks that raw_ptr<T> can be passed by value into function
// parameters.  This is mostly a smoke test for TRIVIAL_ABI attribute.
TEST_F(RawPtrTest, FunctionParameters_ExplicitlyMovedLValue) {}

// This test checks that raw_ptr<T> can be passed by value into function
// parameters.  This is mostly a smoke test for TRIVIAL_ABI attribute.
TEST_F(RawPtrTest, FunctionParameters_Copy) {}

TEST_F(RawPtrTest, SetLookupUsesGetForComparison) {}

TEST_F(RawPtrTest, ComparisonOperatorUsesGetForComparison) {}

// Two `raw_ptr`s with different Traits should still hit `GetForComparison()`
// (as opposed to `GetForExtraction()`) in their comparison operators. We use
// `CountingRawPtr` and `CountingRawPtrMayDangle` to contrast two different
// Traits.
TEST_F(RawPtrTest, OperatorsUseGetForComparison) {}

// This test checks how the std library handles collections like
// std::vector<raw_ptr<T>>.
TEST_F(RawPtrTest, TrivialRelocability) {}

struct BaseStruct {};

struct DerivedType1 : public BaseStruct {};

struct DerivedType2 : public BaseStruct {};

TEST_F(RawPtrTest, DerivedStructsComparison) {}

class PmfTestBase {};

class PmfTestDerived : public PmfTestBase {};

TEST_F(RawPtrTest, PointerToMemberFunction) {}

TEST_F(RawPtrTest, WorksWithOptional) {}

TEST_F(RawPtrTest, WorksWithVariant) {}

TEST_F(RawPtrTest, CrossKindConversion) {}

TEST_F(RawPtrTest, CrossKindAssignment) {}

// Without the explicitly customized `raw_ptr::to_address()`,
// `std::to_address()` will use the dereference operator. This is not
// what we want; this test enforces extraction semantics for
// `to_address()`.
TEST_F(RawPtrTest, ToAddressDoesNotDereference) {}

TEST_F(RawPtrTest, ToAddressGivesBackRawAddress) {}

void InOutParamFuncWithPointer(int* in, int** out) {}

TEST_F(RawPtrTest, EphemeralRawAddrPointerPointer) {}

void InOutParamFuncWithReference(int* in, int*& out) {}

TEST_F(RawPtrTest, EphemeralRawAddrPointerReference) {}

// InstanceTracer has additional fields, so just skip this test when instance
// tracing is enabled.
#if !PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER)
#if PA_BUILDFLAG(PA_COMPILER_GCC) && !defined(__clang__)
// In GCC this test will optimize the return value of the constructor, so
// assert fails. Disable optimizations to verify uninitialized attribute works
// as expected.
#pragma GCC push_options
#pragma GCC optimize("O0")
#endif
TEST_F(RawPtrTest, AllowUninitialized) {}
#if PA_BUILDFLAG(PA_COMPILER_GCC) && !defined(__clang__)
#pragma GCC pop_options
#endif
#endif  // !PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER)

}  // namespace

namespace base::internal {

#if PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) && \
    !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)

void HandleOOM(size_t unused_size) {}

class BackupRefPtrTest : public testing::Test {};

TEST_F(BackupRefPtrTest, Basic) {}

TEST_F(BackupRefPtrTest, ZeroSized) {}

TEST_F(BackupRefPtrTest, EndPointer) {}

TEST_F(BackupRefPtrTest, QuarantinedBytes) {}

TEST_F(BackupRefPtrTest, SameSlotAssignmentWhenDangling) {}

void RunBackupRefPtrImplAdvanceTest(
    partition_alloc::PartitionAllocator& allocator,
    size_t requested_size) {}

TEST_F(BackupRefPtrTest, Advance) {}

TEST_F(BackupRefPtrTest, AdvanceAcrossPools) {}

TEST_F(BackupRefPtrTest, GetDeltaElems) {}

volatile char g_volatile_char_to_ignore;

TEST_F(BackupRefPtrTest, IndexOperator) {}

bool IsQuarantineEmpty(partition_alloc::PartitionAllocator& allocator) {}

struct BoundRawPtrTestHelper {};

// Check that bound callback arguments remain protected by BRP for the
// entire duration of a callback invocation.
TEST_F(BackupRefPtrTest, Bind) {}

#if PA_CONFIG(IN_SLOT_METADATA_CHECK_COOKIE)
TEST_F(BackupRefPtrTest, ReinterpretCast) {
  void* ptr = allocator_.root()->Alloc(16);
  allocator_.root()->Free(ptr);

  raw_ptr<void>* wrapped_ptr = reinterpret_cast<raw_ptr<void>*>(&ptr);
  // The reference count cookie check should detect that the allocation has
  // been already freed.
  BASE_EXPECT_DEATH(*wrapped_ptr = nullptr, "");
}
#endif  // PA_CONFIG(IN_SLOT_METADATA_CHECK_COOKIE)

// Tests that ref-count management is correct, despite `std::optional` may be
// using `union` underneath.
TEST_F(BackupRefPtrTest, WorksWithOptional) {}

// Tests that ref-count management is correct, despite `std::variant` may be
// using `union` underneath.
TEST_F(BackupRefPtrTest, WorksWithVariant) {}

namespace {

// Install dangling raw_ptr handlers and restore them when going out of scope.
class ScopedInstallDanglingRawPtrChecks {};

}  // namespace

TEST_F(BackupRefPtrTest, RawPtrMayDangle) {}

TEST_F(BackupRefPtrTest, RawPtrNotDangling) {}

// Check the comparator operators work, even across raw_ptr with different
// dangling policies.
TEST_F(BackupRefPtrTest, DanglingPtrComparison) {}

// Check the assignment operator works, even across raw_ptr with different
// dangling policies (only `not dangling` -> `dangling` direction is supported).
TEST_F(BackupRefPtrTest, DanglingPtrAssignment) {}

// Check the copy constructor works, even across raw_ptr with different dangling
// policies (only `not dangling` -> `dangling` direction is supported).
TEST_F(BackupRefPtrTest, DanglingPtrCopyContructor) {}

TEST_F(BackupRefPtrTest, RawPtrExtractAsDangling) {}

TEST_F(BackupRefPtrTest, RawPtrDeleteWithoutExtractAsDangling) {}

TEST_F(BackupRefPtrTest, SpatialAlgoCompat) {}

#if PA_BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR)
TEST_F(BackupRefPtrTest, Duplicate) {
  size_t requested_size = allocator_.root()->AdjustSizeForExtrasSubtract(512);
  char* ptr = static_cast<char*>(allocator_.root()->Alloc(requested_size));
  raw_ptr<char, AllowPtrArithmetic> protected_ptr1 = ptr;
  protected_ptr1 += requested_size;  // Pointer should now be poisoned.

  // Duplicating a poisoned pointer should be allowed.
  raw_ptr<char, AllowPtrArithmetic> protected_ptr2 = protected_ptr1;

  // The poison bit should be propagated to the duplicate such that the OOB
  // access is disallowed:
  EXPECT_DEATH_IF_SUPPORTED(*protected_ptr2 = ' ', "");

  // Assignment from a poisoned pointer should be allowed.
  raw_ptr<char, AllowPtrArithmetic> protected_ptr3;
  protected_ptr3 = protected_ptr1;

  // The poison bit should be propagated via the assignment such that the OOB
  // access is disallowed:
  EXPECT_DEATH_IF_SUPPORTED(*protected_ptr3 = ' ', "");

  protected_ptr1 = nullptr;
  protected_ptr2 = nullptr;
  protected_ptr3 = nullptr;
  allocator_.root()->Free(ptr);
}
#endif  // PA_BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR)

#if PA_BUILDFLAG(EXPENSIVE_DCHECKS_ARE_ON)
TEST_F(BackupRefPtrTest, WriteAfterFree) {}
#endif  // PA_BUILDFLAG(EXPENSIVE_DCHECKS_ARE_ON)

namespace {
constexpr uint8_t kCustomQuarantineByte =;
static_assert;

void CustomQuarantineHook(void* address, size_t size) {}
}  // namespace

TEST_F(BackupRefPtrTest, QuarantineHook) {}

TEST_F(BackupRefPtrTest, RawPtrTraits_DisableBRP) {}

#endif  // PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) &&
        // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)

#if PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL)

namespace {
#define FOR_EACH_RAW_PTR_OPERATION

// Can't use gMock to count the number of invocations because
// gMock itself triggers raw_ptr<T> operations.
struct CountingHooks {
  void ResetCounts() {
#define F
    FOR_EACH_RAW_PTR_OPERATION(F)
#undef F
  }

  static CountingHooks* Get() {
    static thread_local CountingHooks instance;
    return &instance;
  }

// The adapter method is templated to accept any number of arguments.
#define F
  FOR_EACH_RAW_PTR_OPERATION(F)
#undef F
};

constexpr RawPtrHooks raw_ptr_hooks{
#define F
    FOR_EACH_RAW_PTR_OPERATION(F)
#undef F
};
}  // namespace

class HookableRawPtrImplTest : public testing::Test {
 protected:
  void SetUp() override { InstallRawPtrHooks(&raw_ptr_hooks); }
  void TearDown() override { ResetRawPtrHooks(); }
};

TEST_F(HookableRawPtrImplTest, WrapPtr) {
  // Can't call `ResetCounts` in `SetUp` because gTest triggers
  // raw_ptr<T> operations between `SetUp` and the test body.
  CountingHooks::Get()->ResetCounts();
  {
    int* ptr = new int;
    [[maybe_unused]] raw_ptr<int> interesting_ptr = ptr;
    delete ptr;
  }
  EXPECT_EQ(CountingHooks::Get()->wrap_ptr_count, 1u);
}

TEST_F(HookableRawPtrImplTest, ReleaseWrappedPtr) {
  CountingHooks::Get()->ResetCounts();
  {
    int* ptr = new int;
    [[maybe_unused]] raw_ptr<int> interesting_ptr = ptr;
    delete ptr;
  }
  EXPECT_EQ(CountingHooks::Get()->release_wrapped_ptr_count, 1u);
}

TEST_F(HookableRawPtrImplTest, SafelyUnwrapForDereference) {
  CountingHooks::Get()->ResetCounts();
  {
    int* ptr = new int;
    raw_ptr<int> interesting_ptr = ptr;
    *interesting_ptr = 1;
    delete ptr;
  }
  EXPECT_EQ(CountingHooks::Get()->safely_unwrap_for_dereference_count, 1u);
}

TEST_F(HookableRawPtrImplTest, SafelyUnwrapForExtraction) {
  CountingHooks::Get()->ResetCounts();
  {
    int* ptr = new int;
    raw_ptr<int> interesting_ptr = ptr;
    ptr = interesting_ptr;
    delete ptr;
  }
  EXPECT_EQ(CountingHooks::Get()->safely_unwrap_for_extraction_count, 1u);
}

TEST_F(HookableRawPtrImplTest, UnsafelyUnwrapForComparison) {
  CountingHooks::Get()->ResetCounts();
  {
    int* ptr = new int;
    raw_ptr<int> interesting_ptr = ptr;
    EXPECT_EQ(interesting_ptr, ptr);
    delete ptr;
  }
  EXPECT_EQ(CountingHooks::Get()->unsafely_unwrap_for_comparison_count, 1u);
}

TEST_F(HookableRawPtrImplTest, Advance) {
  CountingHooks::Get()->ResetCounts();
  {
    int* ptr = new int[10];
    raw_ptr<int, AllowPtrArithmetic> interesting_ptr = ptr;
    interesting_ptr += 1;
    delete[] ptr;
  }
  EXPECT_EQ(CountingHooks::Get()->advance_count, 1u);
}

TEST_F(HookableRawPtrImplTest, Duplicate) {
  CountingHooks::Get()->ResetCounts();
  {
    int* ptr = new int;
    raw_ptr<int> interesting_ptr = ptr;
    raw_ptr<int> interesting_ptr2 = interesting_ptr;
    delete ptr;
  }
  EXPECT_EQ(CountingHooks::Get()->duplicate_count, 1u);
}

TEST_F(HookableRawPtrImplTest, CrossKindCopyConstruction) {
  CountingHooks::Get()->ResetCounts();
  {
    int* ptr = new int;
    raw_ptr<int> non_dangling_ptr = ptr;
    raw_ptr<int, RawPtrTraits::kMayDangle> dangling_ptr(non_dangling_ptr);
    delete ptr;
  }
  EXPECT_EQ(CountingHooks::Get()->duplicate_count, 0u);
  EXPECT_EQ(CountingHooks::Get()->wrap_ptr_for_duplication_count, 1u);
  EXPECT_EQ(CountingHooks::Get()->unsafely_unwrap_for_duplication_count, 1u);
}

#endif  // PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL)

TEST(DanglingPtrTest, DetectAndReset) {}

TEST(DanglingPtrTest, DetectAndDestructor) {}

TEST(DanglingPtrTest, DetectResetAndDestructor) {}

#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER) && \
    PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL)
TEST(RawPtrInstanceTracerTest, CreateAndDestroy) {
  auto owned = std::make_unique<int>(8);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());

  {
    raw_ptr<int> ptr1 = owned.get();
    const auto stacks =
        InstanceTracer::GetStackTracesForAddressForTest(owned.get());
    EXPECT_THAT(stacks, SizeIs(1));
    {
      // A second raw_ptr to the same object should result in an additional
      // stack trace.
      raw_ptr<int> ptr2 = owned.get();
      EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                  SizeIs(2));
    }
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                Eq(stacks));
  }
  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());
}

TEST(RawPtrInstanceTracerTest, CopyConstruction) {
  auto owned = std::make_unique<int>(8);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());
  {
    raw_ptr<int> ptr1 = owned.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                SizeIs(1));
    {
      // Copying `ptr1` to `ptr2` should result in an additional stack trace.
      raw_ptr<int> ptr2 = ptr1;
      EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                  SizeIs(2));
    }
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                SizeIs(1));
  }

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());
}

TEST(RawPtrInstanceTracerTest, CopyAssignment) {
  auto owned1 = std::make_unique<int>(8);
  auto owned2 = std::make_unique<int>(9);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
              IsEmpty());
  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
              IsEmpty());

  {
    raw_ptr<int> ptr1 = owned1.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(1));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                IsEmpty());

    raw_ptr<int> ptr2 = owned2.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(1));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                SizeIs(1));

    ptr2 = ptr1;
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(2));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                IsEmpty());
  }

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
              IsEmpty());
  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
              IsEmpty());
}

TEST(RawPtrInstanceTracerTest, MoveConstruction) {
  auto owned = std::make_unique<int>(8);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());
  {
    raw_ptr<int> ptr1 = owned.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                SizeIs(1));
    {
      // Moving `ptr1` to `ptr2` should not result in an additional stack trace.
      raw_ptr<int> ptr2 = std::move(ptr1);
      EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                  SizeIs(1));
    }
    // Once `ptr2` goes out of scope, there should be no more traces.
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                IsEmpty());
  }
}

TEST(RawPtrInstanceTracerTest, MoveAssignment) {
  auto owned1 = std::make_unique<int>(8);
  auto owned2 = std::make_unique<int>(9);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
              IsEmpty());
  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
              IsEmpty());

  {
    raw_ptr<int> ptr1 = owned1.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(1));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                IsEmpty());

    raw_ptr<int> ptr2 = owned2.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(1));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                SizeIs(1));

    ptr2 = std::move(ptr1);
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(1));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                IsEmpty());
  }

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
              IsEmpty());
  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
              IsEmpty());
}

TEST(RawPtrInstanceTracerTest, SelfCopy) {
  auto owned = std::make_unique<int>(8);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());

  {
    raw_ptr<int> ptr = owned.get();
    auto& ptr2 = ptr;  // To get around compiler self-assignment warning :)
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                SizeIs(1));

    ptr2 = ptr;
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                SizeIs(1));
  }

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());
}

TEST(RawPtrInstanceTracerTest, SelfMove) {
  auto owned = std::make_unique<int>(8);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());

  {
    raw_ptr<int> ptr = owned.get();
    auto& ptr2 = ptr;  // To get around compiler self-assignment warning :)
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                SizeIs(1));

    ptr2 = std::move(ptr);
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                SizeIs(1));
  }

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());
}

TEST(RawPtrInstanceTracerTest, ConversionCreateAndDestroy) {
  auto owned = std::make_unique<Derived>(1, 2, 3);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());

  {
    raw_ptr<Base1> ptr1 = owned.get();
    const auto stacks =
        InstanceTracer::GetStackTracesForAddressForTest(owned.get());
    EXPECT_THAT(stacks, SizeIs(1));
    {
      // A second raw_ptr to the same object should result in an additional
      // stack trace.
      raw_ptr<Base2> ptr2 = owned.get();
      EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                  SizeIs(2));
    }
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                Eq(stacks));
  }
  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());
}

TEST(RawPtrInstanceTracerTest, CopyConversionConstruction) {
  auto owned = std::make_unique<Derived>(1, 2, 3);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());
  {
    raw_ptr<Derived> ptr1 = owned.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                SizeIs(1));
    {
      // Copying `ptr1` to `ptr2` should result in an additional stack trace.
      raw_ptr<Base1> ptr2 = ptr1;
      EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                  SizeIs(2));
    }
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                SizeIs(1));
  }

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());
}

TEST(RawPtrInstanceTracerTest, CopyConversionAssignment) {
  auto owned1 = std::make_unique<Derived>(1, 2, 3);
  auto owned2 = std::make_unique<Derived>(4, 5, 6);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
              IsEmpty());
  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
              IsEmpty());

  {
    raw_ptr<Derived> ptr1 = owned1.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(1));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                IsEmpty());

    raw_ptr<Base1> ptr2 = owned2.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(1));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                SizeIs(1));

    ptr2 = ptr1;
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(2));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                IsEmpty());
  }

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
              IsEmpty());
  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
              IsEmpty());
}

TEST(RawPtrInstanceTracerTest, MoveConversionConstruction) {
  auto owned = std::make_unique<Derived>(1, 2, 3);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
              IsEmpty());
  {
    raw_ptr<Derived> ptr1 = owned.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                SizeIs(1));
    {
      // Moving `ptr1` to `ptr2` should not result in an additional stack trace.
      raw_ptr<Base1> ptr2 = std::move(ptr1);
      EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                  SizeIs(1));
    }
    // Once `ptr2` goes out of scope, there should be no more traces.
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned.get()),
                IsEmpty());
  }
}

TEST(RawPtrInstanceTracerTest, MoveConversionAssignment) {
  auto owned1 = std::make_unique<Derived>(1, 2, 3);
  auto owned2 = std::make_unique<Derived>(4, 5, 6);

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
              IsEmpty());
  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
              IsEmpty());

  {
    raw_ptr<Derived> ptr1 = owned1.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(1));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                IsEmpty());

    raw_ptr<Base1> ptr2 = owned2.get();
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(1));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                SizeIs(1));

    ptr2 = std::move(ptr1);
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
                SizeIs(1));
    EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
                IsEmpty());
  }

  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned1.get()),
              IsEmpty());
  EXPECT_THAT(InstanceTracer::GetStackTracesForAddressForTest(owned2.get()),
              IsEmpty());
}
#endif  // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER) &&
        // PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL)

}  // namespace base::internal