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