|  | // Copyright 2020 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <lib/fidl/llcpp/aligned.h> | 
|  | #include <lib/fidl/llcpp/tracking_ptr.h> | 
|  |  | 
|  | #include <set> | 
|  | #include <unordered_set> | 
|  |  | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | struct DestructionState { | 
|  | bool destructor_called = false; | 
|  | }; | 
|  | struct DestructableObject { | 
|  | DestructableObject() : ds(nullptr) {} | 
|  | DestructableObject(DestructionState* ds) : ds(ds) {} | 
|  |  | 
|  | ~DestructableObject() { ds->destructor_called = true; } | 
|  |  | 
|  | DestructionState* ds; | 
|  | }; | 
|  |  | 
|  | TEST(TrackingPtr, DefaultConstructor) { | 
|  | fidl::tracking_ptr<int32_t> ptr; | 
|  | EXPECT_EQ(ptr, nullptr); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, SetGet) { | 
|  | int32_t x; | 
|  | fidl::unowned_ptr<int32_t> uptr(&x); | 
|  | fidl::tracking_ptr<int32_t> ptr(uptr); | 
|  | EXPECT_EQ(ptr.get(), &x); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, UnownedSingleValueLifecycle) { | 
|  | DestructionState ds1, ds2; | 
|  | DestructableObject obj1(&ds1), obj2(&ds2); | 
|  | { | 
|  | fidl::tracking_ptr<DestructableObject> ptr1 = fidl::unowned_ptr<DestructableObject>(&obj1); | 
|  | fidl::tracking_ptr<DestructableObject> ptr2 = fidl::unowned_ptr<DestructableObject>(&obj2); | 
|  | ptr2 = std::move(ptr1); | 
|  | } | 
|  | EXPECT_FALSE(ds1.destructor_called); | 
|  | EXPECT_FALSE(ds2.destructor_called); | 
|  | } | 
|  |  | 
|  | #if TRACKING_PTR_ENABLE_UNIQUE_PTR_CONSTRUCTOR | 
|  |  | 
|  | TEST(TrackingPtr, OwnedSingleValueLifecycle) { | 
|  | DestructionState ds1, ds2; | 
|  | { | 
|  | fidl::tracking_ptr<DestructableObject> ptr1 = std::make_unique<DestructableObject>(&ds1); | 
|  | fidl::tracking_ptr<DestructableObject> ptr2 = std::make_unique<DestructableObject>(&ds2); | 
|  | EXPECT_FALSE(ds1.destructor_called); | 
|  | EXPECT_FALSE(ds2.destructor_called); | 
|  | ptr2 = std::move(ptr1); | 
|  | EXPECT_FALSE(ds1.destructor_called); | 
|  | EXPECT_TRUE(ds2.destructor_called); | 
|  | } | 
|  | EXPECT_TRUE(ds1.destructor_called); | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | TEST(TrackingPtr, UnownedArrayLifecycle) { | 
|  | DestructionState ds1[2] = {}; | 
|  | DestructionState ds2[2] = {}; | 
|  | DestructableObject arr1[] = {&ds1[0], &ds1[1]}; | 
|  | DestructableObject arr2[] = {&ds2[0], &ds2[1]}; | 
|  | { | 
|  | fidl::tracking_ptr<DestructableObject[]> ptr1 = fidl::unowned_ptr<DestructableObject>(arr1); | 
|  | fidl::tracking_ptr<DestructableObject[]> ptr2 = fidl::unowned_ptr<DestructableObject>(arr2); | 
|  | ptr2 = std::move(ptr1); | 
|  | } | 
|  | EXPECT_FALSE(ds1[0].destructor_called); | 
|  | EXPECT_FALSE(ds1[1].destructor_called); | 
|  | EXPECT_FALSE(ds2[0].destructor_called); | 
|  | EXPECT_FALSE(ds2[1].destructor_called); | 
|  | } | 
|  |  | 
|  | #if TRACKING_PTR_ENABLE_UNIQUE_PTR_CONSTRUCTOR | 
|  |  | 
|  | TEST(TrackingPtr, OwnedArrayLifecycle) { | 
|  | DestructionState ds1[2] = {}; | 
|  | DestructionState ds2[2] = {}; | 
|  |  | 
|  | { | 
|  | auto arr1 = std::make_unique<DestructableObject[]>(2); | 
|  | arr1[0].ds = &ds1[0]; | 
|  | arr1[1].ds = &ds1[1]; | 
|  | fidl::tracking_ptr<DestructableObject[]> ptr1(std::move(arr1)); | 
|  | auto arr2 = std::make_unique<DestructableObject[]>(2); | 
|  | arr2[0].ds = &ds2[0]; | 
|  | arr2[1].ds = &ds2[1]; | 
|  | fidl::tracking_ptr<DestructableObject[]> ptr2(std::move(arr2)); | 
|  | EXPECT_FALSE(ds1[0].destructor_called); | 
|  | EXPECT_FALSE(ds1[1].destructor_called); | 
|  | EXPECT_FALSE(ds2[0].destructor_called); | 
|  | EXPECT_FALSE(ds2[1].destructor_called); | 
|  | ptr2 = std::move(ptr1); | 
|  | EXPECT_FALSE(ds1[0].destructor_called); | 
|  | EXPECT_FALSE(ds1[1].destructor_called); | 
|  | EXPECT_TRUE(ds2[0].destructor_called); | 
|  | EXPECT_TRUE(ds2[1].destructor_called); | 
|  | } | 
|  | EXPECT_TRUE(ds1[0].destructor_called); | 
|  | EXPECT_TRUE(ds1[1].destructor_called); | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | TEST(TrackingPtr, SingleValueOperatorBool) { | 
|  | fidl::tracking_ptr<int32_t> default_ptr; | 
|  | EXPECT_FALSE(default_ptr); | 
|  | int32_t val = 1; | 
|  | fidl::tracking_ptr<int32_t> ptr = fidl::unowned_ptr<int32_t>(&val); | 
|  | EXPECT_TRUE(ptr); | 
|  | ptr = nullptr; | 
|  | EXPECT_FALSE(ptr); | 
|  | ptr = 0; | 
|  | EXPECT_FALSE(ptr); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, ArrayOperatorBool) { | 
|  | int32_t arr[3] = {}; | 
|  | fidl::unowned_ptr<int32_t> uptr(arr); | 
|  | fidl::tracking_ptr<int32_t[]> ptr(uptr); | 
|  | EXPECT_TRUE(ptr); | 
|  | ptr = nullptr; | 
|  | EXPECT_FALSE(ptr); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, VoidOperatorBool) { | 
|  | int32_t val = 1; | 
|  | fidl::tracking_ptr<int32_t> int_ptr = fidl::unowned_ptr<int32_t>(&val); | 
|  | fidl::tracking_ptr<void> nonnull_ptr(std::move(int_ptr)); | 
|  | EXPECT_TRUE(nonnull_ptr); | 
|  |  | 
|  | fidl::tracking_ptr<void> null_ptr((fidl::tracking_ptr<int32_t>(nullptr))); | 
|  | EXPECT_FALSE(null_ptr); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, SingleValueDereference) { | 
|  | struct TestStruct { | 
|  | int a; | 
|  | }; | 
|  | TestStruct example{.a = 1}; | 
|  | fidl::tracking_ptr<TestStruct> example_ptr = fidl::unowned_ptr<TestStruct>(&example); | 
|  | EXPECT_EQ((*example_ptr).a, 1); | 
|  | EXPECT_EQ(example_ptr->a, 1); | 
|  | *example_ptr = TestStruct{.a = 2}; | 
|  | EXPECT_EQ(example_ptr->a, 2); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, ArrayIndexing) { | 
|  | int32_t arr[3] = {1, 2, 3}; | 
|  | fidl::tracking_ptr<int32_t[]> ptr = fidl::unowned_ptr<int32_t>(arr); | 
|  | EXPECT_EQ(ptr[1], 2); | 
|  | ptr[0] = 4; | 
|  | EXPECT_EQ(ptr[0], 4); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, Swap) { | 
|  | int32_t x, y; | 
|  | fidl::tracking_ptr<int32_t> x_ptr = fidl::unowned_ptr<int32_t>(&x); | 
|  | fidl::tracking_ptr<int32_t> y_ptr = fidl::unowned_ptr<int32_t>(&y); | 
|  | std::swap(x_ptr, y_ptr); | 
|  | EXPECT_EQ(x_ptr.get(), &y); | 
|  | EXPECT_EQ(y_ptr.get(), &x); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, SingleValueHashing) { | 
|  | int32_t val; | 
|  | EXPECT_EQ(std::hash<fidl::tracking_ptr<int32_t>>{}(fidl::unowned_ptr<int32_t>(&val)), | 
|  | std::hash<int32_t*>{}(&val)); | 
|  |  | 
|  | // Ensure that hashing is correctly implemented so unordered_set can be used. | 
|  | std::unordered_set<fidl::tracking_ptr<int32_t>> set; | 
|  | set.insert(fidl::unowned_ptr<int32_t>(&val)); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, ArrayHashing) { | 
|  | int32_t arr[3]{}; | 
|  | EXPECT_EQ(std::hash<fidl::tracking_ptr<int32_t[]>>{}(fidl::unowned_ptr<int32_t>(arr)), | 
|  | std::hash<int32_t*>{}(arr)); | 
|  |  | 
|  | // Ensure that hashing is correctly implemented so unordered_set can be used. | 
|  | std::unordered_set<fidl::tracking_ptr<int32_t[]>> set; | 
|  | set.insert(fidl::unowned_ptr<int32_t>(arr)); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, Comparison) { | 
|  | int32_t* lower_ptr = reinterpret_cast<int32_t*>(0x10); | 
|  | int32_t* upper_ptr = reinterpret_cast<int32_t*>(0x20); | 
|  | fidl::tracking_ptr<int32_t> lower = fidl::unowned_ptr<int32_t>(lower_ptr); | 
|  | fidl::tracking_ptr<int32_t> lower2 = fidl::unowned_ptr<int32_t>(lower_ptr); | 
|  | fidl::tracking_ptr<int32_t> upper = fidl::unowned_ptr<int32_t>(upper_ptr); | 
|  |  | 
|  | EXPECT_TRUE(lower == lower2); | 
|  | EXPECT_FALSE(lower == upper); | 
|  | EXPECT_TRUE(lower != upper); | 
|  | EXPECT_FALSE(lower != lower2); | 
|  | EXPECT_TRUE(lower < upper); | 
|  | EXPECT_FALSE(lower < lower); | 
|  | EXPECT_FALSE(upper < lower); | 
|  | EXPECT_TRUE(lower <= upper); | 
|  | EXPECT_TRUE(lower <= lower); | 
|  | EXPECT_FALSE(upper <= lower); | 
|  | EXPECT_TRUE(upper > lower); | 
|  | EXPECT_FALSE(upper > upper); | 
|  | EXPECT_FALSE(lower > upper); | 
|  | EXPECT_TRUE(upper >= lower); | 
|  | EXPECT_TRUE(upper >= upper); | 
|  | EXPECT_FALSE(lower >= upper); | 
|  |  | 
|  | EXPECT_FALSE(lower == nullptr); | 
|  | EXPECT_FALSE(nullptr == lower); | 
|  | EXPECT_TRUE(lower != nullptr); | 
|  | EXPECT_TRUE(nullptr != lower); | 
|  |  | 
|  | // Ensure that comparison is correctly implemented so set can be used. | 
|  | std::set<fidl::tracking_ptr<int32_t>> set; | 
|  | set.insert(fidl::unowned_ptr<int32_t>(lower_ptr)); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, Casting) { | 
|  | struct Base { | 
|  | uint64_t v; | 
|  | }; | 
|  | struct Derived : public Base {}; | 
|  | Derived d; | 
|  | fidl::tracking_ptr<Derived> d_ptr = fidl::unowned_ptr<Derived>(&d); | 
|  | EXPECT_EQ(static_cast<fidl::tracking_ptr<Base>>(std::move(d_ptr)).get(), &d); | 
|  |  | 
|  | fidl::tracking_ptr<Derived> d_ptr2 = fidl::unowned_ptr<Derived>(&d); | 
|  | auto vptr = static_cast<fidl::tracking_ptr<void>>(std::move(d_ptr2)); | 
|  | EXPECT_EQ(vptr.get(), &d); | 
|  |  | 
|  | auto d_arr_ptr = static_cast<fidl::tracking_ptr<Derived[]>>(std::move(vptr)); | 
|  | EXPECT_EQ(d_arr_ptr.get(), &d); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, FidlAligned) { | 
|  | fidl::aligned<uint8_t> byte = 1; | 
|  | fidl::tracking_ptr<uint8_t> ptr = fidl::unowned_ptr<fidl::aligned<uint8_t>>(&byte); | 
|  | EXPECT_EQ(ptr.get(), &byte.value); | 
|  | fidl::tracking_ptr<uint8_t[]> arr_ptr = fidl::unowned_ptr<fidl::aligned<uint8_t>>(&byte); | 
|  | EXPECT_EQ(ptr.get(), &byte.value); | 
|  | } | 
|  |  | 
|  | TEST(TrackingPtr, FidlAlignedArray) { | 
|  | fidl::aligned<uint8_t[8]> byteArray; | 
|  | byteArray.value[0] = 0; | 
|  | byteArray.value[1] = 1; | 
|  | fidl::tracking_ptr<uint8_t[]> ptr = fidl::unowned(&byteArray); | 
|  | EXPECT_EQ(ptr[0], 0); | 
|  | EXPECT_EQ(ptr[1], 1); | 
|  |  | 
|  | fidl::aligned<fidl::Array<uint8_t, 8>> fidlArray; | 
|  | fidlArray.value[0] = 1; | 
|  | fidlArray.value[1] = 2; | 
|  | fidl::tracking_ptr<uint8_t[]> fidlArrayPtr = fidl::unowned(&fidlArray); | 
|  | EXPECT_EQ(fidlArrayPtr[0], 1); | 
|  | EXPECT_EQ(fidlArrayPtr[1], 2); | 
|  |  | 
|  | // Each fidl_aligned<uint8_t> is 8 bytes while fidl::aligned<uint8_t[8]> contains 1 byte values. | 
|  | fidl::aligned<uint8_t> alignedByteArray[8] = {2, 3}; | 
|  | fidl::tracking_ptr<fidl::aligned<uint8_t>[]> alignedArrayPtr = fidl::unowned(alignedByteArray); | 
|  | EXPECT_EQ(alignedArrayPtr[0], 2); | 
|  | EXPECT_EQ(alignedArrayPtr[1], 3); | 
|  | } |