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