1 // Copyright 2016 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #pragma once 6 7 #include <fbl/alloc_checker.h> 8 #include <fbl/atomic.h> 9 #include <fbl/ref_counted.h> 10 #include <fbl/ref_ptr.h> 11 #include <fbl/unique_ptr.h> 12 #include <unittest/unittest.h> 13 14 #include <memory> 15 #include <utility> 16 17 namespace fbl { 18 namespace tests { 19 namespace intrusive_containers { 20 21 // All test objects derive from a simple base class which keeps track of how 22 // many of the object are currently alive. 23 class TestObjBase { 24 public: TestObjBase(size_t)25 explicit TestObjBase(size_t) { ++live_obj_count_; } ~TestObjBase()26 ~TestObjBase() { --live_obj_count_; } 27 live_obj_count()28 static size_t live_obj_count() { return live_obj_count_; } ResetLiveObjCount()29 static void ResetLiveObjCount() { live_obj_count_ = 0; } 30 31 private: 32 static size_t live_obj_count_; 33 }; 34 35 // The base class for keyed test objects. Implements the storage for a key as 36 // well as the default traits accessor and a set method for use by tests. 37 template <typename KeyType> 38 class KeyedTestObjBase : public TestObjBase { 39 public: KeyedTestObjBase(size_t val)40 explicit KeyedTestObjBase(size_t val) 41 : TestObjBase(val), 42 key_(static_cast<KeyType>(val)) { } 43 GetKey()44 KeyType GetKey() const { return key_; } SetKey(KeyType key)45 void SetKey(KeyType key) { key_ = key; } 46 47 private: 48 KeyType key_; 49 }; 50 51 // The base class for hash-able test objects. Implements a default hash 52 // function accessor as well as inheriting from KeyedTestObjBase 53 template <typename KeyType, typename HashType, HashType kNumBuckets> 54 class HashedTestObjBase : public KeyedTestObjBase<KeyType> { 55 public: HashedTestObjBase(size_t val)56 explicit HashedTestObjBase(size_t val) : KeyedTestObjBase<KeyType>(val) { } 57 GetHash(const KeyType & key)58 static HashType GetHash(const KeyType& key) { 59 // Our simple hash function just multiplies by a big prime and mods by 60 // the number of buckets. 61 return (static_cast<HashType>(key) * 0xcf2fd713) % kNumBuckets; 62 } 63 }; 64 65 // A 'test' custom deleter for use when testing managed pointer type which have 66 // support for template define custom deleters. 67 template <typename T> 68 struct TestCustomDeleter { 69 public: 70 constexpr TestCustomDeleter() = default; operatorTestCustomDeleter71 void operator()(T* ptr) const { 72 delete_count_.fetch_add(1u); 73 delete ptr; 74 } 75 reset_delete_countTestCustomDeleter76 static void reset_delete_count() { delete_count_.store(0); } delete_countTestCustomDeleter77 static size_t delete_count() { return delete_count_.load(); } 78 79 private: 80 static fbl::atomic<size_t> delete_count_; 81 }; 82 83 // Container test objects are objects which... 84 // 85 // 1) Store a size_t value 86 // 2) Store a 'visited' flag for use when testing iterators 87 // 3) Derive from TestObjBase (so that live object counts are maintained) 88 // 4) Exercise the base class helper for the container which makes an object 89 // containable (SinglyLinkedListable for SinglyLinkedList, etc...) 90 // 5) Have storage of the appropriate type to exist in another version of the 91 // container being exercised. 92 template <typename _ContainerTraits> 93 class TestObj : public _ContainerTraits::TestObjBaseType, 94 public _ContainerTraits::ContainableBaseClass { 95 public: 96 using ContainerTraits = _ContainerTraits; 97 using ContainerStateType = typename ContainerTraits::ContainerStateType; 98 using PtrTraits = typename ContainerStateType::PtrTraits; 99 TestObj(size_t val)100 explicit TestObj(size_t val) 101 : _ContainerTraits::TestObjBaseType(val), 102 val_(val) { } 103 value()104 size_t value() const { return val_; } raw_ptr()105 const void* raw_ptr() const { return this; } 106 107 // Note: the visit method needs to be const (and the visited_count_ member mutable) so we can 108 // test const_iterators. Visit()109 void Visit() const { ++visited_count_; } ResetVisitedCount()110 void ResetVisitedCount() { visited_count_ = 0; } visited_count()111 size_t visited_count() const { return visited_count_; } 112 113 bool operator==(const TestObj<ContainerTraits>& other) const { return this == &other; } 114 bool operator!=(const TestObj<ContainerTraits>& other) const { return this != &other; } 115 116 private: 117 friend typename ContainerTraits::OtherContainerTraits; 118 119 size_t val_; 120 mutable size_t visited_count_ = 0; 121 typename ContainerTraits::OtherContainerStateType other_container_state_; 122 }; 123 124 // RefedTestObj is a ref-counted version of TestObj for use with RefPtr<> tests. 125 template <typename ContainerTraits> 126 class RefedTestObj : public TestObj<ContainerTraits>, 127 public RefCounted<RefedTestObj<ContainerTraits>> { 128 public: RefedTestObj(size_t val)129 explicit RefedTestObj(size_t val) : TestObj<ContainerTraits>(val) { } 130 }; 131 132 // Basic pointer type definitions for the 5 types of currently support pointers 133 // 134 // Used by the macros which generate the various test environmements 135 // 136 // 1) Foo* (unmanaged/raw) 137 // 2) fbl::unique_ptr<Foo> 138 // 3) std::unique_ptr<Foo> 139 // 4) std::unique_ptr<Foo, CustomDeleter> 140 // 5) fbl::RefPtr<Foo> 141 // 142 namespace ptr_type { 143 144 template <typename _ObjType> 145 struct Unmanaged { using type = _ObjType*; }; 146 147 template <typename _ObjType> 148 struct UniquePtr { using type = ::fbl::unique_ptr<_ObjType>; }; 149 150 template <typename _ObjType> 151 struct StdUniquePtrDefaultDeleter { using type = ::std::unique_ptr<_ObjType>; }; 152 153 template <typename _ObjType> 154 struct StdUniquePtrCustomDeleter { 155 using type = ::std::unique_ptr<_ObjType, TestCustomDeleter<_ObjType>>; 156 }; 157 158 template <typename _ObjType> 159 struct RefPtr { using type = ::fbl::RefPtr<_ObjType>; }; 160 161 } // namespace ptr_type 162 163 // Test trait structures contain utilities which define test behavior for the 164 // five types of pointers which are managed by intrusive containers (see above). 165 // 166 // Defined behaviors include... 167 // 168 // 1) Allocating a valid version of a pointer to a TestObj of the proper type. 169 // 2) "Transferring" a pointer (eg. copying if the pointer type supports copying, 170 // otherwise moving). 171 // 3) Testing to see if a pointer to an object was properly transferred into a 172 // container. 173 // 4) Testing to see if a pointer to an object was properly moved into a 174 // container. 175 // 5) Checking to see if the number of times an associated custom deleter was 176 // invoked. 177 // 5) Resetting any assoicated custom deleter state. 178 template <typename _ObjType> 179 struct UnmanagedTestTraits { 180 using ObjType = _ObjType; 181 using PtrType = typename ptr_type::Unmanaged<ObjType>::type; 182 using ConstPtrType = const ObjType*; 183 using ContainerType = typename ObjType::ContainerTraits::ContainerType; 184 CreateObjectUnmanagedTestTraits185 static PtrType CreateObject(size_t value) { 186 AllocChecker ac; 187 auto r = new (&ac) ObjType(value); 188 return ac.check() ? r : nullptr; 189 } 190 ReleaseObjectUnmanagedTestTraits191 static void ReleaseObject(PtrType& ptr) { 192 delete ptr; 193 ptr = nullptr; 194 } 195 CheckCustomDeleteInvocationsUnmanagedTestTraits196 static bool CheckCustomDeleteInvocations(size_t expected) { return true; } ResetCustomDeleterUnmanagedTestTraits197 static void ResetCustomDeleter() { } 198 199 // Unmanaged pointers never get cleared when being moved or transferred. TransferUnmanagedTestTraits200 static inline PtrType& Transfer(PtrType& ptr) { return ptr; } WasTransferredUnmanagedTestTraits201 static bool WasTransferred(const ConstPtrType& ptr) { return ptr != nullptr; } WasMovedUnmanagedTestTraits202 static bool WasMoved (const ConstPtrType& ptr) { return ptr != nullptr; } 203 }; 204 205 template <typename _ObjType> 206 struct UniquePtrTestTraits { 207 using ObjType = _ObjType; 208 using PtrType = typename ptr_type::UniquePtr<ObjType>::type; 209 using ConstPtrType = const PtrType; 210 using ContainerType = typename ObjType::ContainerTraits::ContainerType; 211 CreateObjectUniquePtrTestTraits212 static PtrType CreateObject(size_t value) { 213 AllocChecker ac; 214 auto r = new (&ac) ObjType(value); 215 return PtrType(ac.check() ? r : nullptr); 216 } 217 ReleaseObjectUniquePtrTestTraits218 static void ReleaseObject(PtrType& ptr) { 219 ptr = nullptr; 220 } 221 CheckCustomDeleteInvocationsUniquePtrTestTraits222 static bool CheckCustomDeleteInvocations(size_t expected) { return true; } ResetCustomDeleterUniquePtrTestTraits223 static void ResetCustomDeleter() { } 224 225 // Unique pointers always get cleared when being moved or transferred. TransferUniquePtrTestTraits226 static inline PtrType&& Transfer(PtrType& ptr) { return std::move(ptr); } WasTransferredUniquePtrTestTraits227 static bool WasTransferred(const ConstPtrType& ptr) { return ptr == nullptr; } WasMovedUniquePtrTestTraits228 static bool WasMoved (const ConstPtrType& ptr) { return ptr == nullptr; } 229 }; 230 231 template <typename _ObjType> 232 struct StdUniquePtrDefaultDeleterTestTraits { 233 using ObjType = _ObjType; 234 using PtrType = typename ptr_type::StdUniquePtrDefaultDeleter<ObjType>::type; 235 using ConstPtrType = const PtrType; 236 using ContainerType = typename ObjType::ContainerTraits::ContainerType; 237 CreateObjectStdUniquePtrDefaultDeleterTestTraits238 static PtrType CreateObject(size_t value) { 239 AllocChecker ac; 240 auto r = new (&ac) ObjType(value); 241 return PtrType(ac.check() ? r : nullptr); 242 } 243 ReleaseObjectStdUniquePtrDefaultDeleterTestTraits244 static void ReleaseObject(PtrType& ptr) { 245 ptr = nullptr; 246 } 247 CheckCustomDeleteInvocationsStdUniquePtrDefaultDeleterTestTraits248 static bool CheckCustomDeleteInvocations(size_t expected) { return true; } ResetCustomDeleterStdUniquePtrDefaultDeleterTestTraits249 static void ResetCustomDeleter() { } 250 251 // Unique pointers always get cleared when being moved or transferred. TransferStdUniquePtrDefaultDeleterTestTraits252 static inline PtrType&& Transfer(PtrType& ptr) { return std::move(ptr); } WasTransferredStdUniquePtrDefaultDeleterTestTraits253 static bool WasTransferred(const ConstPtrType& ptr) { return ptr == nullptr; } WasMovedStdUniquePtrDefaultDeleterTestTraits254 static bool WasMoved (const ConstPtrType& ptr) { return ptr == nullptr; } 255 }; 256 257 template <typename _ObjType> 258 struct StdUniquePtrCustomDeleterTestTraits { 259 using ObjType = _ObjType; 260 using PtrType = typename ptr_type::StdUniquePtrCustomDeleter<ObjType>::type; 261 using ConstPtrType = const PtrType; 262 using ContainerType = typename ObjType::ContainerTraits::ContainerType; 263 CreateObjectStdUniquePtrCustomDeleterTestTraits264 static PtrType CreateObject(size_t value) { 265 AllocChecker ac; 266 auto r = new (&ac) ObjType(value); 267 return PtrType(ac.check() ? r : nullptr); 268 } 269 ReleaseObjectStdUniquePtrCustomDeleterTestTraits270 static void ReleaseObject(PtrType& ptr) { 271 ptr = nullptr; 272 } 273 CheckCustomDeleteInvocationsStdUniquePtrCustomDeleterTestTraits274 static bool CheckCustomDeleteInvocations(size_t expected) { 275 BEGIN_HELPER; 276 EXPECT_EQ(expected, TestCustomDeleter<_ObjType>::delete_count()); 277 END_HELPER; 278 } 279 ResetCustomDeleterStdUniquePtrCustomDeleterTestTraits280 static void ResetCustomDeleter() { 281 TestCustomDeleter<_ObjType>::reset_delete_count(); 282 } 283 284 // Unique pointers always get cleared when being moved or transferred. TransferStdUniquePtrCustomDeleterTestTraits285 static inline PtrType&& Transfer(PtrType& ptr) { return std::move(ptr); } WasTransferredStdUniquePtrCustomDeleterTestTraits286 static bool WasTransferred(const ConstPtrType& ptr) { return ptr == nullptr; } WasMovedStdUniquePtrCustomDeleterTestTraits287 static bool WasMoved (const ConstPtrType& ptr) { return ptr == nullptr; } 288 }; 289 290 template <typename _ObjType> 291 struct RefPtrTestTraits { 292 using ObjType = _ObjType; 293 using PtrType = typename ptr_type::RefPtr<ObjType>::type; 294 using ConstPtrType = const PtrType; 295 using ContainerType = typename ObjType::ContainerTraits::ContainerType; 296 CreateObjectRefPtrTestTraits297 static PtrType CreateObject(size_t value) { 298 AllocChecker ac; 299 auto r = new (&ac) ObjType(value); 300 return AdoptRef(ac.check() ? r : nullptr); 301 } 302 ReleaseObjectRefPtrTestTraits303 static void ReleaseObject(PtrType& ptr) { 304 ptr = nullptr; 305 } 306 CheckCustomDeleteInvocationsRefPtrTestTraits307 static bool CheckCustomDeleteInvocations(size_t expected) { return true; } ResetCustomDeleterRefPtrTestTraits308 static void ResetCustomDeleter() { } 309 310 // RefCounted pointers do not get cleared when being transferred, but do get 311 // cleared when being moved. TransferRefPtrTestTraits312 static inline PtrType& Transfer(PtrType& ptr) { return ptr; } WasTransferredRefPtrTestTraits313 static bool WasTransferred(const ConstPtrType& ptr) { return ptr != nullptr; } WasMovedRefPtrTestTraits314 static bool WasMoved (const ConstPtrType& ptr) { return ptr == nullptr; } 315 }; 316 317 } // namespace intrusive_containers 318 } // namespace tests 319 } // namespace fbl 320