xref: /aosp_15_r20/external/pigweed/pw_intrusive_ptr/intrusive_ptr_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2022 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_intrusive_ptr/intrusive_ptr.h"
16 
17 #include <stdint.h>
18 
19 #include <utility>
20 
21 #include "pw_unit_test/framework.h"
22 
23 namespace pw {
24 namespace {
25 
26 class TestItem : public RefCounted<TestItem> {
27  public:
TestItem()28   TestItem() { ++instance_counter; }
29 
TestItem(int32_t f)30   explicit TestItem(int32_t f) : TestItem() { first = f; }
31 
TestItem(int64_t s)32   explicit TestItem(int64_t s) : TestItem() { second = s; }
33 
TestItem(int32_t f,int64_t s)34   TestItem(int32_t f, int64_t s) : TestItem() {
35     first = f;
36     second = s;
37   }
38 
TestItem(const TestItem &)39   TestItem(const TestItem&) : TestItem() {}
TestItem(TestItem &&)40   TestItem(TestItem&&) noexcept : TestItem() {}
41 
operator =(const TestItem & other)42   TestItem& operator=(const TestItem& other) {
43     if (&other != this) {
44       ++instance_counter;
45     }
46     return *this;
47   }
48 
operator =(TestItem && other)49   TestItem& operator=(TestItem&& other) noexcept {
50     if (&other != this) {
51       ++instance_counter;
52     }
53     return *this;
54   }
55 
~TestItem()56   virtual ~TestItem() { --instance_counter; }
57 
58   inline static int32_t instance_counter = 0;
59 
60   int32_t first = 0;
61   int64_t second = 1;
62 };
63 
64 class TestItemDerived : public TestItem {
65  public:
TestItemDerived()66   TestItemDerived() { ++derived_instance_counter; }
TestItemDerived(const TestItemDerived &)67   TestItemDerived(const TestItemDerived&) : TestItemDerived() {}
TestItemDerived(TestItemDerived &&)68   TestItemDerived(TestItemDerived&&) noexcept : TestItemDerived() {}
69 
operator =(const TestItemDerived & other)70   TestItemDerived& operator=(const TestItemDerived& other) {
71     if (&other != this) {
72       ++derived_instance_counter;
73     }
74     return *this;
75   }
76 
operator =(TestItemDerived && other)77   TestItemDerived& operator=(TestItemDerived&& other) noexcept {
78     if (&other != this) {
79       ++derived_instance_counter;
80     }
81     return *this;
82   }
83 
~TestItemDerived()84   ~TestItemDerived() override { --derived_instance_counter; }
85 
86   inline static int32_t derived_instance_counter = 0;
87 };
88 
89 struct FreeTestItem {
AddRefpw::__anon5d32cbbf0111::FreeTestItem90   void AddRef() const { ++instance_counter; }
91 
ReleaseRefpw::__anon5d32cbbf0111::FreeTestItem92   bool ReleaseRef() const { return --instance_counter < 1; }
93 
94   mutable int32_t instance_counter = 0;
95 };
96 
97 class IntrusivePtrTest : public ::testing::Test {
98  protected:
SetUp()99   void SetUp() override {
100     TestItem::instance_counter = 0;
101     TestItemDerived::derived_instance_counter = 0;
102   }
103 };
104 
TEST_F(IntrusivePtrTest,DeletingLastPtrDeletesTheObject)105 TEST_F(IntrusivePtrTest, DeletingLastPtrDeletesTheObject) {
106   {
107     IntrusivePtr<TestItem> ptr(new TestItem());
108     EXPECT_EQ(TestItem::instance_counter, 1);
109   }
110   EXPECT_EQ(TestItem::instance_counter, 0);
111 }
112 
TEST_F(IntrusivePtrTest,AssigningToNullptrDeletesTheObject)113 TEST_F(IntrusivePtrTest, AssigningToNullptrDeletesTheObject) {
114   IntrusivePtr<TestItem> ptr(new TestItem());
115   EXPECT_EQ(TestItem::instance_counter, 1);
116   ptr = nullptr;
117   EXPECT_EQ(TestItem::instance_counter, 0);
118 }
119 
TEST_F(IntrusivePtrTest,AssigningToEmptyPtrDeletesTheObject)120 TEST_F(IntrusivePtrTest, AssigningToEmptyPtrDeletesTheObject) {
121   IntrusivePtr<TestItem> ptr(new TestItem());
122   IntrusivePtr<TestItem> empty;
123   EXPECT_EQ(TestItem::instance_counter, 1);
124   ptr = empty;
125   EXPECT_EQ(TestItem::instance_counter, 0);
126 }
127 
TEST_F(IntrusivePtrTest,SwapWithNullptrKeepsTheObject)128 TEST_F(IntrusivePtrTest, SwapWithNullptrKeepsTheObject) {
129   IntrusivePtr<TestItem> ptr(new TestItem());
130   IntrusivePtr<TestItem> empty;
131   EXPECT_EQ(TestItem::instance_counter, 1);
132 
133   ptr.swap(empty);
134   EXPECT_EQ(TestItem::instance_counter, 1);
135   EXPECT_EQ(ptr, nullptr);
136 
137   empty = nullptr;
138   EXPECT_EQ(TestItem::instance_counter, 0);
139 }
140 
TEST_F(IntrusivePtrTest,CopyingPtrDoesntCreateNewObjects)141 TEST_F(IntrusivePtrTest, CopyingPtrDoesntCreateNewObjects) {
142   {
143     IntrusivePtr<TestItem> ptr(new TestItem());
144     EXPECT_EQ(TestItem::instance_counter, 1);
145 
146     {
147       IntrusivePtr<TestItem> ptr_2(ptr);
148       EXPECT_EQ(TestItem::instance_counter, 1);
149     }
150 
151     // We still have a ptr here.
152     EXPECT_EQ(TestItem::instance_counter, 1);
153   }
154   EXPECT_EQ(TestItem::instance_counter, 0);
155 }
156 
TEST_F(IntrusivePtrTest,MovingPtrDoesntCreateNewObjects)157 TEST_F(IntrusivePtrTest, MovingPtrDoesntCreateNewObjects) {
158   {
159     IntrusivePtr<TestItem> ptr(new TestItem());
160     EXPECT_EQ(TestItem::instance_counter, 1);
161 
162     {
163       IntrusivePtr<TestItem> ptr_2(std::move(ptr));
164       EXPECT_EQ(TestItem::instance_counter, 1);
165     }
166 
167     // ptr was moved away, object should be deleted.
168     EXPECT_EQ(TestItem::instance_counter, 0);
169   }
170   EXPECT_EQ(TestItem::instance_counter, 0);
171 }
172 
TEST_F(IntrusivePtrTest,CopyAssigningPtrDoesntCreateNewObjects)173 TEST_F(IntrusivePtrTest, CopyAssigningPtrDoesntCreateNewObjects) {
174   {
175     IntrusivePtr<TestItem> ptr(new TestItem());
176     EXPECT_EQ(TestItem::instance_counter, 1);
177 
178     {
179       auto ptr_2 = ptr;
180       EXPECT_EQ(TestItem::instance_counter, 1);
181     }
182 
183     // We still have a ptr here.
184     EXPECT_EQ(TestItem::instance_counter, 1);
185   }
186   EXPECT_EQ(TestItem::instance_counter, 0);
187 }
188 
TEST_F(IntrusivePtrTest,MoveAssigningPtrDoesntCreateNewObjects)189 TEST_F(IntrusivePtrTest, MoveAssigningPtrDoesntCreateNewObjects) {
190   {
191     IntrusivePtr<TestItem> ptr(new TestItem());
192     EXPECT_EQ(TestItem::instance_counter, 1);
193 
194     {
195       auto ptr_2 = std::move(ptr);
196       EXPECT_EQ(TestItem::instance_counter, 1);
197     }
198 
199     // ptr was moved away, object should be deleted.
200     EXPECT_EQ(TestItem::instance_counter, 0);
201   }
202   EXPECT_EQ(TestItem::instance_counter, 0);
203 }
204 
TEST_F(IntrusivePtrTest,CopyingPtrToBaseClassPtrDoesntCreateNewObjects)205 TEST_F(IntrusivePtrTest, CopyingPtrToBaseClassPtrDoesntCreateNewObjects) {
206   {
207     IntrusivePtr<TestItemDerived> ptr(new TestItemDerived());
208     EXPECT_EQ(TestItem::instance_counter, 1);
209     EXPECT_EQ(TestItemDerived::derived_instance_counter, 1);
210 
211     {
212       IntrusivePtr<TestItem> ptr_2(ptr);
213       EXPECT_EQ(TestItem::instance_counter, 1);
214       EXPECT_EQ(TestItemDerived::derived_instance_counter, 1);
215     }
216 
217     // We still have a ptr here.
218     EXPECT_EQ(TestItem::instance_counter, 1);
219     EXPECT_EQ(TestItemDerived::derived_instance_counter, 1);
220   }
221   EXPECT_EQ(TestItem::instance_counter, 0);
222   EXPECT_EQ(TestItemDerived::derived_instance_counter, 0);
223 }
224 
TEST_F(IntrusivePtrTest,MovingPtrToBaseClassPtrDoesntCreateNewObjects)225 TEST_F(IntrusivePtrTest, MovingPtrToBaseClassPtrDoesntCreateNewObjects) {
226   {
227     IntrusivePtr<TestItemDerived> ptr(new TestItemDerived());
228     EXPECT_EQ(TestItem::instance_counter, 1);
229     EXPECT_EQ(TestItemDerived::derived_instance_counter, 1);
230 
231     {
232       IntrusivePtr<TestItem> ptr_2(std::move(ptr));
233       EXPECT_EQ(TestItem::instance_counter, 1);
234       EXPECT_EQ(TestItemDerived::derived_instance_counter, 1);
235     }
236 
237     // ptr was moved away, object should be deleted.
238     EXPECT_EQ(TestItem::instance_counter, 0);
239     EXPECT_EQ(TestItemDerived::derived_instance_counter, 0);
240   }
241   EXPECT_EQ(TestItem::instance_counter, 0);
242   EXPECT_EQ(TestItemDerived::derived_instance_counter, 0);
243 }
244 
TEST_F(IntrusivePtrTest,CopyAssigningPtrToBaseClassPtrDoesntCreateNewObjects)245 TEST_F(IntrusivePtrTest, CopyAssigningPtrToBaseClassPtrDoesntCreateNewObjects) {
246   {
247     IntrusivePtr<TestItemDerived> ptr(new TestItemDerived());
248     EXPECT_EQ(TestItem::instance_counter, 1);
249     EXPECT_EQ(TestItemDerived::derived_instance_counter, 1);
250 
251     {
252       IntrusivePtr<TestItem> ptr_2 = ptr;
253       EXPECT_EQ(TestItem::instance_counter, 1);
254       EXPECT_EQ(TestItemDerived::derived_instance_counter, 1);
255     }
256 
257     // We still have a ptr here.
258     EXPECT_EQ(TestItem::instance_counter, 1);
259     EXPECT_EQ(TestItemDerived::derived_instance_counter, 1);
260   }
261   EXPECT_EQ(TestItem::instance_counter, 0);
262   EXPECT_EQ(TestItemDerived::derived_instance_counter, 0);
263 }
264 
TEST_F(IntrusivePtrTest,MoveAssigningPtrToBaseClassPtrDoesntCreateNewObjects)265 TEST_F(IntrusivePtrTest, MoveAssigningPtrToBaseClassPtrDoesntCreateNewObjects) {
266   {
267     IntrusivePtr<TestItemDerived> ptr(new TestItemDerived());
268     EXPECT_EQ(TestItem::instance_counter, 1);
269     EXPECT_EQ(TestItemDerived::derived_instance_counter, 1);
270 
271     {
272       IntrusivePtr<TestItem> ptr_2 = std::move(ptr);
273       EXPECT_EQ(TestItem::instance_counter, 1);
274       EXPECT_EQ(TestItemDerived::derived_instance_counter, 1);
275     }
276 
277     // ptr was moved away, object should be deleted.
278     EXPECT_EQ(TestItem::instance_counter, 0);
279     EXPECT_EQ(TestItemDerived::derived_instance_counter, 0);
280   }
281   EXPECT_EQ(TestItem::instance_counter, 0);
282   EXPECT_EQ(TestItemDerived::derived_instance_counter, 0);
283 }
284 
TEST_F(IntrusivePtrTest,CopyAssigningPtrDeletesOldObjectIfLast)285 TEST_F(IntrusivePtrTest, CopyAssigningPtrDeletesOldObjectIfLast) {
286   {
287     IntrusivePtr<TestItem> ptr(new TestItem());
288     EXPECT_EQ(TestItem::instance_counter, 1);
289 
290     {
291       IntrusivePtr<TestItem> ptr_2(new TestItem());
292       EXPECT_EQ(TestItem::instance_counter, 2);
293 
294       ptr_2 = ptr;
295 
296       // Old object in ptr_2 should be removed.
297       EXPECT_EQ(TestItem::instance_counter, 1);
298     }
299 
300     // We still have a ptr here.
301     EXPECT_EQ(TestItem::instance_counter, 1);
302   }
303   EXPECT_EQ(TestItem::instance_counter, 0);
304 }
305 
TEST_F(IntrusivePtrTest,MoveAssigningPtrDeletesOldObjectIfLast)306 TEST_F(IntrusivePtrTest, MoveAssigningPtrDeletesOldObjectIfLast) {
307   {
308     IntrusivePtr<TestItem> ptr(new TestItem());
309     EXPECT_EQ(TestItem::instance_counter, 1);
310 
311     {
312       IntrusivePtr<TestItem> ptr_2(new TestItem());
313       EXPECT_EQ(TestItem::instance_counter, 2);
314 
315       ptr_2 = std::move(ptr);
316 
317       // Old object in ptr_2 should be removed.
318       EXPECT_EQ(TestItem::instance_counter, 1);
319     }
320 
321     // ptr was moved away, object should be deleted.
322     EXPECT_EQ(TestItem::instance_counter, 0);
323   }
324   EXPECT_EQ(TestItem::instance_counter, 0);
325 }
326 
327 // Comparison tests use operators directly to cover == and != and both
328 // argument orders.
TEST_F(IntrusivePtrTest,PtrsWithDifferentObjectsAreNotEqual)329 TEST_F(IntrusivePtrTest, PtrsWithDifferentObjectsAreNotEqual) {
330   IntrusivePtr<TestItem> ptr(new TestItem());
331   IntrusivePtr<TestItem> ptr_2(new TestItem());
332 
333   EXPECT_FALSE(ptr == ptr_2);
334   EXPECT_FALSE(ptr_2 == ptr);
335   EXPECT_TRUE(ptr != ptr_2);
336   EXPECT_TRUE(ptr_2 != ptr);
337 }
338 
TEST_F(IntrusivePtrTest,PtrsWithSameObjectsAreEqual)339 TEST_F(IntrusivePtrTest, PtrsWithSameObjectsAreEqual) {
340   IntrusivePtr<TestItem> ptr(new TestItem());
341   auto ptr_2 = ptr;
342 
343   EXPECT_TRUE(ptr == ptr_2);
344   EXPECT_TRUE(ptr_2 == ptr);
345   EXPECT_FALSE(ptr != ptr_2);
346   EXPECT_FALSE(ptr_2 != ptr);
347 }
348 
TEST_F(IntrusivePtrTest,FilledPtrIsNotEqualToEmptyPtr)349 TEST_F(IntrusivePtrTest, FilledPtrIsNotEqualToEmptyPtr) {
350   IntrusivePtr<TestItem> ptr(new TestItem());
351   IntrusivePtr<TestItem> empty;
352 
353   EXPECT_FALSE(ptr == empty);
354   EXPECT_FALSE(empty == ptr);
355   EXPECT_TRUE(ptr != empty);
356   EXPECT_TRUE(empty != ptr);
357 }
358 
TEST_F(IntrusivePtrTest,FilledPtrIsNotEqualToNullptr)359 TEST_F(IntrusivePtrTest, FilledPtrIsNotEqualToNullptr) {
360   IntrusivePtr<TestItem> ptr(new TestItem());
361 
362   EXPECT_FALSE(ptr == nullptr);
363   EXPECT_FALSE(nullptr == ptr);
364   EXPECT_TRUE(ptr != nullptr);
365   EXPECT_TRUE(nullptr != ptr);
366 }
367 
TEST_F(IntrusivePtrTest,EmptyPtrIsEqualToNullptr)368 TEST_F(IntrusivePtrTest, EmptyPtrIsEqualToNullptr) {
369   IntrusivePtr<TestItem> empty;
370 
371   EXPECT_TRUE(empty == nullptr);
372   EXPECT_TRUE(nullptr == empty);
373   EXPECT_FALSE(empty != nullptr);
374   EXPECT_FALSE(nullptr != empty);
375 }
376 
TEST_F(IntrusivePtrTest,PtrsWithDifferentObjectsReturnDifferentPointers)377 TEST_F(IntrusivePtrTest, PtrsWithDifferentObjectsReturnDifferentPointers) {
378   IntrusivePtr<TestItem> ptr(new TestItem());
379   IntrusivePtr<TestItem> ptr_2(new TestItem());
380 
381   EXPECT_NE(ptr.get(), ptr_2.get());
382 }
383 
TEST_F(IntrusivePtrTest,PtrsWithSameObjectsReturnSamePointer)384 TEST_F(IntrusivePtrTest, PtrsWithSameObjectsReturnSamePointer) {
385   IntrusivePtr<TestItemDerived> ptr(new TestItemDerived());
386   auto ptr_2 = ptr;
387   IntrusivePtr<TestItem> ptr_3 = ptr;
388 
389   EXPECT_EQ(ptr.get(), ptr_2.get());
390   EXPECT_EQ(static_cast<TestItem*>(ptr.get()), ptr_3.get());
391 }
392 
TEST_F(IntrusivePtrTest,EmptyPtrReturnsNullptr)393 TEST_F(IntrusivePtrTest, EmptyPtrReturnsNullptr) {
394   IntrusivePtr<TestItem> empty;
395 
396   EXPECT_EQ(empty.get(), nullptr);
397 }
398 
TEST_F(IntrusivePtrTest,ConstifyWorks)399 TEST_F(IntrusivePtrTest, ConstifyWorks) {
400   IntrusivePtr<TestItem> ptr(new TestItem());
401   IntrusivePtr<const TestItem> ptr_2 = ptr;
402 
403   EXPECT_EQ(TestItem::instance_counter, 1);
404   EXPECT_EQ(ptr.get(), ptr_2.get());
405 }
406 
TEST_F(IntrusivePtrTest,NonRefCountedObjectWorks)407 TEST_F(IntrusivePtrTest, NonRefCountedObjectWorks) {
408   // Compilation test only.
409   IntrusivePtr<FreeTestItem> empty;
410   IntrusivePtr<FreeTestItem> free(new FreeTestItem);
411   IntrusivePtr<FreeTestItem> free_2(free);
412   IntrusivePtr<FreeTestItem> free_3(std::move(free));
413 }
414 
TEST_F(IntrusivePtrTest,MakeRefCounted)415 TEST_F(IntrusivePtrTest, MakeRefCounted) {
416   auto ptr_1 = MakeRefCounted<TestItem>();
417   EXPECT_EQ(TestItem::instance_counter, 1);
418   EXPECT_EQ(ptr_1->first, 0);
419   EXPECT_EQ(ptr_1->second, 1);
420 
421   auto ptr_2 = MakeRefCounted<TestItem>(int32_t(42));
422   EXPECT_EQ(TestItem::instance_counter, 2);
423   EXPECT_EQ(ptr_2->first, 42);
424   EXPECT_EQ(ptr_2->second, 1);
425 
426   auto ptr_3 = MakeRefCounted<TestItem>(int64_t(2));
427   EXPECT_EQ(TestItem::instance_counter, 3);
428   EXPECT_EQ(ptr_3->first, 0);
429   EXPECT_EQ(ptr_3->second, 2);
430 
431   auto ptr_4 = MakeRefCounted<TestItem>(42, 5);
432   EXPECT_EQ(TestItem::instance_counter, 4);
433   EXPECT_EQ(ptr_4->first, 42);
434   EXPECT_EQ(ptr_4->second, 5);
435 }
436 
TEST_F(IntrusivePtrTest,UseCount)437 TEST_F(IntrusivePtrTest, UseCount) {
438   IntrusivePtr<TestItem> ptr(new TestItem());
439   EXPECT_EQ(ptr.use_count(), 1);
440   {
441     IntrusivePtr<TestItem> ptr_copy = ptr;
442     EXPECT_EQ(ptr.use_count(), 2);
443   }
444   EXPECT_EQ(ptr.use_count(), 1);
445 }
446 
TEST_F(IntrusivePtrTest,UseCountForNullPtr)447 TEST_F(IntrusivePtrTest, UseCountForNullPtr) {
448   IntrusivePtr<TestItem> ptr;
449   EXPECT_EQ(ptr.use_count(), 0);
450 
451   ptr = IntrusivePtr(new TestItem);
452   EXPECT_EQ(ptr.use_count(), 1);
453 
454   ptr = nullptr;
455   EXPECT_EQ(ptr.use_count(), 0);
456 }
457 
458 }  // namespace
459 }  // namespace pw
460