1 | // Copyright 2013 The Flutter 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 | // This file tests both ref_counted.h and ref_ptr.h (which the former includes). |
6 | // TODO(vtl): Possibly we could separate these tests out better, since a lot of |
7 | // it is actually testing |RefPtr|. |
8 | |
9 | #include "flutter/fml/memory/ref_counted.h" |
10 | |
11 | #include "flutter/fml/macros.h" |
12 | #include "gtest/gtest.h" |
13 | |
14 | #if defined(__clang__) |
15 | #define ALLOW_PESSIMIZING_MOVE(code_line) \ |
16 | _Pragma("clang diagnostic push") \ |
17 | _Pragma("clang diagnostic ignored \"-Wpessimizing-move\"") code_line; \ |
18 | _Pragma("clang diagnostic pop") |
19 | #else |
20 | #define ALLOW_PESSIMIZING_MOVE(code_line) code_line; |
21 | #endif |
22 | |
23 | #if defined(__clang__) |
24 | #define ALLOW_SELF_MOVE(code_line) \ |
25 | _Pragma("clang diagnostic push") \ |
26 | _Pragma("clang diagnostic ignored \"-Wself-move\"") code_line; \ |
27 | _Pragma("clang diagnostic pop") |
28 | #else |
29 | #define ALLOW_SELF_MOVE(code_line) code_line; |
30 | #endif |
31 | |
32 | #if defined(__clang__) |
33 | #define ALLOW_SELF_ASSIGN_OVERLOADED(code_line) \ |
34 | _Pragma("clang diagnostic push") \ |
35 | _Pragma("clang diagnostic ignored \"-Wself-assign-overloaded\"") \ |
36 | code_line; \ |
37 | _Pragma("clang diagnostic pop") |
38 | #else |
39 | #define ALLOW_SELF_ASSIGN_OVERLOADED(code_line) code_line; |
40 | #endif |
41 | |
42 | namespace fml { |
43 | namespace { |
44 | |
45 | class MyClass : public RefCountedThreadSafe<MyClass> { |
46 | protected: |
47 | MyClass(MyClass** created, bool* was_destroyed) |
48 | : was_destroyed_(was_destroyed) { |
49 | if (created) { |
50 | *created = this; |
51 | } |
52 | } |
53 | virtual ~MyClass() { |
54 | if (was_destroyed_) { |
55 | *was_destroyed_ = true; |
56 | } |
57 | } |
58 | |
59 | private: |
60 | FML_FRIEND_REF_COUNTED_THREAD_SAFE(MyClass); |
61 | FML_FRIEND_MAKE_REF_COUNTED(MyClass); |
62 | |
63 | bool* was_destroyed_; |
64 | |
65 | FML_DISALLOW_COPY_AND_ASSIGN(MyClass); |
66 | }; |
67 | |
68 | class MySubclass final : public MyClass { |
69 | private: |
70 | FML_FRIEND_REF_COUNTED_THREAD_SAFE(MySubclass); |
71 | FML_FRIEND_MAKE_REF_COUNTED(MySubclass); |
72 | |
73 | MySubclass(MySubclass** created, bool* was_destroyed) |
74 | : MyClass(nullptr, was_destroyed) { |
75 | if (created) { |
76 | *created = this; |
77 | } |
78 | } |
79 | ~MySubclass() override {} |
80 | |
81 | FML_DISALLOW_COPY_AND_ASSIGN(MySubclass); |
82 | }; |
83 | |
84 | TEST(RefCountedTest, Constructors) { |
85 | bool was_destroyed; |
86 | |
87 | { |
88 | // Default. |
89 | RefPtr<MyClass> r; |
90 | EXPECT_TRUE(r.get() == nullptr); |
91 | EXPECT_FALSE(r); |
92 | } |
93 | |
94 | { |
95 | // Nullptr. |
96 | RefPtr<MyClass> r(nullptr); |
97 | EXPECT_TRUE(r.get() == nullptr); |
98 | EXPECT_FALSE(r); |
99 | } |
100 | |
101 | { |
102 | MyClass* created = nullptr; |
103 | was_destroyed = false; |
104 | // Adopt, then RVO. |
105 | RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
106 | EXPECT_TRUE(created); |
107 | EXPECT_EQ(created, r.get()); |
108 | EXPECT_TRUE(r); |
109 | EXPECT_FALSE(was_destroyed); |
110 | } |
111 | EXPECT_TRUE(was_destroyed); |
112 | |
113 | { |
114 | MyClass* created = nullptr; |
115 | was_destroyed = false; |
116 | // Adopt, then move. |
117 | ALLOW_PESSIMIZING_MOVE(RefPtr<MyClass> r( |
118 | std::move(MakeRefCounted<MyClass>(&created, &was_destroyed)))) |
119 | EXPECT_TRUE(created); |
120 | EXPECT_EQ(created, r.get()); |
121 | EXPECT_TRUE(r); |
122 | EXPECT_FALSE(was_destroyed); |
123 | } |
124 | EXPECT_TRUE(was_destroyed); |
125 | |
126 | { |
127 | MyClass* created = nullptr; |
128 | was_destroyed = false; |
129 | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
130 | // Copy. |
131 | RefPtr<MyClass> r2(r1); |
132 | EXPECT_TRUE(created); |
133 | EXPECT_EQ(created, r1.get()); |
134 | EXPECT_EQ(created, r2.get()); |
135 | EXPECT_TRUE(r1); |
136 | EXPECT_TRUE(r2); |
137 | EXPECT_FALSE(was_destroyed); |
138 | } |
139 | EXPECT_TRUE(was_destroyed); |
140 | |
141 | { |
142 | MyClass* created = nullptr; |
143 | was_destroyed = false; |
144 | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
145 | // From raw pointer. |
146 | RefPtr<MyClass> r2(created); |
147 | EXPECT_TRUE(created); |
148 | EXPECT_EQ(created, r1.get()); |
149 | EXPECT_EQ(created, r2.get()); |
150 | EXPECT_TRUE(r1); |
151 | EXPECT_TRUE(r2); |
152 | EXPECT_FALSE(was_destroyed); |
153 | } |
154 | EXPECT_TRUE(was_destroyed); |
155 | |
156 | { |
157 | MySubclass* created = nullptr; |
158 | was_destroyed = false; |
159 | // Adopt, then "move". |
160 | RefPtr<MyClass> r(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
161 | EXPECT_TRUE(created); |
162 | EXPECT_EQ(static_cast<MyClass*>(created), r.get()); |
163 | EXPECT_TRUE(r); |
164 | EXPECT_FALSE(was_destroyed); |
165 | } |
166 | EXPECT_TRUE(was_destroyed); |
167 | |
168 | { |
169 | MySubclass* created = nullptr; |
170 | was_destroyed = false; |
171 | // Adopt, then "move". |
172 | ALLOW_PESSIMIZING_MOVE(RefPtr<MyClass> r( |
173 | std::move(MakeRefCounted<MySubclass>(&created, &was_destroyed)))) |
174 | EXPECT_TRUE(created); |
175 | EXPECT_EQ(static_cast<MyClass*>(created), r.get()); |
176 | EXPECT_TRUE(r); |
177 | EXPECT_FALSE(was_destroyed); |
178 | } |
179 | EXPECT_TRUE(was_destroyed); |
180 | |
181 | { |
182 | MySubclass* created = nullptr; |
183 | was_destroyed = false; |
184 | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
185 | // "Copy". |
186 | RefPtr<MyClass> r2(r1); |
187 | EXPECT_TRUE(created); |
188 | EXPECT_EQ(static_cast<MyClass*>(created), r1.get()); |
189 | EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); |
190 | EXPECT_TRUE(r1); |
191 | EXPECT_TRUE(r2); |
192 | EXPECT_FALSE(was_destroyed); |
193 | } |
194 | EXPECT_TRUE(was_destroyed); |
195 | |
196 | { |
197 | MySubclass* created = nullptr; |
198 | was_destroyed = false; |
199 | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
200 | // From raw pointer. |
201 | RefPtr<MyClass> r2(created); |
202 | EXPECT_TRUE(created); |
203 | EXPECT_EQ(static_cast<MyClass*>(created), r1.get()); |
204 | EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); |
205 | EXPECT_TRUE(r1); |
206 | EXPECT_TRUE(r2); |
207 | EXPECT_FALSE(was_destroyed); |
208 | } |
209 | EXPECT_TRUE(was_destroyed); |
210 | } |
211 | |
212 | TEST(RefCountedTest, NullAssignmentToNull) { |
213 | RefPtr<MyClass> r1; |
214 | // No-op null assignment using |nullptr|. |
215 | r1 = nullptr; |
216 | EXPECT_TRUE(r1.get() == nullptr); |
217 | EXPECT_FALSE(r1); |
218 | |
219 | RefPtr<MyClass> r2; |
220 | // No-op null assignment using copy constructor. |
221 | r1 = r2; |
222 | EXPECT_TRUE(r1.get() == nullptr); |
223 | EXPECT_TRUE(r2.get() == nullptr); |
224 | EXPECT_FALSE(r1); |
225 | EXPECT_FALSE(r2); |
226 | |
227 | // No-op null assignment using move constructor. |
228 | r1 = std::move(r2); |
229 | EXPECT_TRUE(r1.get() == nullptr); |
230 | // The clang linter flags the method called on the moved-from reference, but |
231 | // this is testing the move implementation, so it is marked NOLINT. |
232 | EXPECT_TRUE(r2.get() == nullptr); // NOLINT(clang-analyzer-cplusplus.Move) |
233 | EXPECT_FALSE(r1); |
234 | EXPECT_FALSE(r2); |
235 | |
236 | RefPtr<MySubclass> r3; |
237 | // No-op null assignment using "copy" constructor. |
238 | r1 = r3; |
239 | EXPECT_TRUE(r1.get() == nullptr); |
240 | EXPECT_TRUE(r3.get() == nullptr); |
241 | EXPECT_FALSE(r1); |
242 | EXPECT_FALSE(r3); |
243 | |
244 | // No-op null assignment using "move" constructor. |
245 | r1 = std::move(r3); |
246 | EXPECT_TRUE(r1.get() == nullptr); |
247 | EXPECT_TRUE(r3.get() == nullptr); |
248 | EXPECT_FALSE(r1); |
249 | EXPECT_FALSE(r3); |
250 | } |
251 | |
252 | TEST(RefCountedTest, NonNullAssignmentToNull) { |
253 | bool was_destroyed; |
254 | |
255 | { |
256 | MyClass* created = nullptr; |
257 | was_destroyed = false; |
258 | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
259 | RefPtr<MyClass> r2; |
260 | // Copy assignment (to null ref pointer). |
261 | r2 = r1; |
262 | EXPECT_EQ(created, r1.get()); |
263 | EXPECT_EQ(created, r2.get()); |
264 | EXPECT_TRUE(r1); |
265 | EXPECT_TRUE(r2); |
266 | EXPECT_FALSE(was_destroyed); |
267 | } |
268 | EXPECT_TRUE(was_destroyed); |
269 | |
270 | { |
271 | MyClass* created = nullptr; |
272 | was_destroyed = false; |
273 | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
274 | RefPtr<MyClass> r2; |
275 | // Move assignment (to null ref pointer). |
276 | r2 = std::move(r1); |
277 | // The clang linter flags the method called on the moved-from reference, but |
278 | // this is testing the move implementation, so it is marked NOLINT. |
279 | EXPECT_TRUE(r1.get() == nullptr); // NOLINT(clang-analyzer-cplusplus.Move) |
280 | EXPECT_EQ(created, r2.get()); |
281 | EXPECT_FALSE(r1); |
282 | EXPECT_TRUE(r2); |
283 | EXPECT_FALSE(was_destroyed); |
284 | } |
285 | EXPECT_TRUE(was_destroyed); |
286 | |
287 | { |
288 | MySubclass* created = nullptr; |
289 | was_destroyed = false; |
290 | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
291 | RefPtr<MyClass> r2; |
292 | // "Copy" assignment (to null ref pointer). |
293 | r2 = r1; |
294 | EXPECT_EQ(created, r1.get()); |
295 | EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); |
296 | EXPECT_TRUE(r1); |
297 | EXPECT_TRUE(r2); |
298 | EXPECT_FALSE(was_destroyed); |
299 | } |
300 | EXPECT_TRUE(was_destroyed); |
301 | |
302 | { |
303 | MySubclass* created = nullptr; |
304 | was_destroyed = false; |
305 | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
306 | RefPtr<MyClass> r2; |
307 | // "Move" assignment (to null ref pointer). |
308 | r2 = std::move(r1); |
309 | EXPECT_TRUE(r1.get() == nullptr); |
310 | EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); |
311 | EXPECT_FALSE(r1); |
312 | EXPECT_TRUE(r2); |
313 | EXPECT_FALSE(was_destroyed); |
314 | } |
315 | EXPECT_TRUE(was_destroyed); |
316 | } |
317 | |
318 | TEST(RefCountedTest, NullAssignmentToNonNull) { |
319 | bool was_destroyed = false; |
320 | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(nullptr, &was_destroyed)); |
321 | // Null assignment (to non-null ref pointer) using |nullptr|. |
322 | r1 = nullptr; |
323 | EXPECT_TRUE(r1.get() == nullptr); |
324 | EXPECT_FALSE(r1); |
325 | EXPECT_TRUE(was_destroyed); |
326 | |
327 | was_destroyed = false; |
328 | r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); |
329 | RefPtr<MyClass> r2; |
330 | // Null assignment (to non-null ref pointer) using copy constructor. |
331 | r1 = r2; |
332 | EXPECT_TRUE(r1.get() == nullptr); |
333 | EXPECT_TRUE(r2.get() == nullptr); |
334 | EXPECT_FALSE(r1); |
335 | EXPECT_FALSE(r2); |
336 | EXPECT_TRUE(was_destroyed); |
337 | |
338 | was_destroyed = false; |
339 | r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); |
340 | // Null assignment using move constructor. |
341 | r1 = std::move(r2); |
342 | EXPECT_TRUE(r1.get() == nullptr); |
343 | // The clang linter flags the method called on the moved-from reference, but |
344 | // this is testing the move implementation, so it is marked NOLINT. |
345 | EXPECT_TRUE(r2.get() == nullptr); // NOLINT(clang-analyzer-cplusplus.Move) |
346 | EXPECT_FALSE(r1); |
347 | EXPECT_FALSE(r2); |
348 | EXPECT_TRUE(was_destroyed); |
349 | |
350 | was_destroyed = false; |
351 | r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); |
352 | RefPtr<MySubclass> r3; |
353 | // Null assignment (to non-null ref pointer) using "copy" constructor. |
354 | r1 = r3; |
355 | EXPECT_TRUE(r1.get() == nullptr); |
356 | EXPECT_TRUE(r3.get() == nullptr); |
357 | EXPECT_FALSE(r1); |
358 | EXPECT_FALSE(r3); |
359 | EXPECT_TRUE(was_destroyed); |
360 | |
361 | was_destroyed = false; |
362 | r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); |
363 | // Null assignment (to non-null ref pointer) using "move" constructor. |
364 | r1 = std::move(r3); |
365 | EXPECT_TRUE(r1.get() == nullptr); |
366 | EXPECT_TRUE(r3.get() == nullptr); |
367 | EXPECT_FALSE(r1); |
368 | EXPECT_FALSE(r3); |
369 | EXPECT_TRUE(was_destroyed); |
370 | } |
371 | |
372 | TEST(RefCountedTest, NonNullAssignmentToNonNull) { |
373 | bool was_destroyed1; |
374 | bool was_destroyed2; |
375 | |
376 | { |
377 | was_destroyed1 = false; |
378 | was_destroyed2 = false; |
379 | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(nullptr, &was_destroyed1)); |
380 | RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); |
381 | // Copy assignment (to non-null ref pointer). |
382 | r2 = r1; |
383 | EXPECT_EQ(r1.get(), r2.get()); |
384 | EXPECT_TRUE(r1); |
385 | EXPECT_TRUE(r2); |
386 | EXPECT_FALSE(was_destroyed1); |
387 | EXPECT_TRUE(was_destroyed2); |
388 | } |
389 | EXPECT_TRUE(was_destroyed1); |
390 | |
391 | { |
392 | was_destroyed1 = false; |
393 | was_destroyed2 = false; |
394 | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(nullptr, &was_destroyed1)); |
395 | RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); |
396 | // Move assignment (to non-null ref pointer). |
397 | r2 = std::move(r1); |
398 | // The clang linter flags the method called on the moved-from reference, but |
399 | // this is testing the move implementation, so it is marked NOLINT. |
400 | EXPECT_TRUE(r1.get() == nullptr); // NOLINT(clang-analyzer-cplusplus.Move) |
401 | EXPECT_FALSE(r2.get() == nullptr); |
402 | EXPECT_FALSE(r1); |
403 | EXPECT_TRUE(r2); |
404 | EXPECT_FALSE(was_destroyed1); |
405 | EXPECT_TRUE(was_destroyed2); |
406 | } |
407 | EXPECT_TRUE(was_destroyed1); |
408 | |
409 | { |
410 | was_destroyed1 = false; |
411 | was_destroyed2 = false; |
412 | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(nullptr, &was_destroyed1)); |
413 | RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); |
414 | // "Copy" assignment (to non-null ref pointer). |
415 | r2 = r1; |
416 | EXPECT_EQ(r1.get(), r2.get()); |
417 | EXPECT_TRUE(r1); |
418 | EXPECT_TRUE(r2); |
419 | EXPECT_FALSE(was_destroyed1); |
420 | EXPECT_TRUE(was_destroyed2); |
421 | } |
422 | EXPECT_TRUE(was_destroyed1); |
423 | |
424 | { |
425 | was_destroyed1 = false; |
426 | was_destroyed2 = false; |
427 | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(nullptr, &was_destroyed1)); |
428 | RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); |
429 | // Move assignment (to non-null ref pointer). |
430 | r2 = std::move(r1); |
431 | EXPECT_TRUE(r1.get() == nullptr); |
432 | EXPECT_FALSE(r2.get() == nullptr); |
433 | EXPECT_FALSE(r1); |
434 | EXPECT_TRUE(r2); |
435 | EXPECT_FALSE(was_destroyed1); |
436 | EXPECT_TRUE(was_destroyed2); |
437 | } |
438 | EXPECT_TRUE(was_destroyed1); |
439 | } |
440 | |
441 | TEST(RefCountedTest, SelfAssignment) { |
442 | bool was_destroyed; |
443 | |
444 | { |
445 | MyClass* created = nullptr; |
446 | was_destroyed = false; |
447 | // This line is marked NOLINT because the clang linter does not reason about |
448 | // the value of the reference count. In particular, the self-assignment |
449 | // below is handled in the copy constructor by a refcount increment then |
450 | // decrement. The linter sees only that the decrement might destroy the |
451 | // object. |
452 | RefPtr<MyClass> r(MakeRefCounted<MyClass>( // NOLINT |
453 | &created, &was_destroyed)); |
454 | // Copy. |
455 | ALLOW_SELF_ASSIGN_OVERLOADED(r = r); |
456 | EXPECT_EQ(created, r.get()); |
457 | EXPECT_FALSE(was_destroyed); |
458 | } |
459 | EXPECT_TRUE(was_destroyed); |
460 | |
461 | { |
462 | MyClass* created = nullptr; |
463 | was_destroyed = false; |
464 | RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
465 | // Move. |
466 | ALLOW_SELF_MOVE(r = std::move(r)) |
467 | EXPECT_EQ(created, r.get()); |
468 | EXPECT_FALSE(was_destroyed); |
469 | } |
470 | EXPECT_TRUE(was_destroyed); |
471 | } |
472 | |
473 | TEST(RefCountedTest, Swap) { |
474 | MyClass* created1 = nullptr; |
475 | bool was_destroyed1 = false; |
476 | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created1, &was_destroyed1)); |
477 | EXPECT_TRUE(created1); |
478 | EXPECT_EQ(created1, r1.get()); |
479 | |
480 | MyClass* created2 = nullptr; |
481 | bool was_destroyed2 = false; |
482 | RefPtr<MyClass> r2(MakeRefCounted<MyClass>(&created2, &was_destroyed2)); |
483 | EXPECT_TRUE(created2); |
484 | EXPECT_EQ(created2, r2.get()); |
485 | EXPECT_NE(created1, created2); |
486 | |
487 | r1.swap(r2); |
488 | EXPECT_EQ(created2, r1.get()); |
489 | EXPECT_EQ(created1, r2.get()); |
490 | } |
491 | |
492 | TEST(RefCountedTest, GetAndDereferenceOperators) { |
493 | // Note: We check here that .get(), operator*, and operator-> are const, but |
494 | // return non-const pointers/refs. |
495 | |
496 | MyClass* created = nullptr; |
497 | const RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, nullptr)); |
498 | MyClass* ptr = r.get(); // Assign to non-const pointer. |
499 | EXPECT_EQ(created, ptr); |
500 | ptr = r.operator->(); // Assign to non-const pointer. |
501 | EXPECT_EQ(created, ptr); |
502 | MyClass& ref = *r; // "Assign" to non-const reference. |
503 | EXPECT_EQ(created, &ref); |
504 | } |
505 | |
506 | // You can manually call |AddRef()| and |Release()| if you want. |
507 | TEST(RefCountedTest, AddRefRelease) { |
508 | MyClass* created = nullptr; |
509 | bool was_destroyed = false; |
510 | { |
511 | RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
512 | EXPECT_EQ(created, r.get()); |
513 | created->AddRef(); |
514 | } |
515 | EXPECT_FALSE(was_destroyed); |
516 | created->Release(); |
517 | EXPECT_TRUE(was_destroyed); |
518 | } |
519 | |
520 | TEST(RefCountedTest, Mix) { |
521 | MySubclass* created = nullptr; |
522 | bool was_destroyed = false; |
523 | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
524 | ASSERT_FALSE(was_destroyed); |
525 | EXPECT_TRUE(created->HasOneRef()); |
526 | created->AssertHasOneRef(); |
527 | |
528 | RefPtr<MySubclass> r2 = r1; |
529 | ASSERT_FALSE(was_destroyed); |
530 | EXPECT_FALSE(created->HasOneRef()); |
531 | |
532 | r1 = nullptr; |
533 | ASSERT_FALSE(was_destroyed); |
534 | created->AssertHasOneRef(); |
535 | |
536 | { |
537 | RefPtr<MyClass> r3 = r2; |
538 | EXPECT_FALSE(created->HasOneRef()); |
539 | { |
540 | RefPtr<MyClass> r4(r3); |
541 | r2 = nullptr; |
542 | ASSERT_FALSE(was_destroyed); |
543 | EXPECT_FALSE(created->HasOneRef()); |
544 | } |
545 | ASSERT_FALSE(was_destroyed); |
546 | EXPECT_TRUE(created->HasOneRef()); |
547 | created->AssertHasOneRef(); |
548 | |
549 | r1 = RefPtr<MySubclass>(static_cast<MySubclass*>(r3.get())); |
550 | ASSERT_FALSE(was_destroyed); |
551 | EXPECT_FALSE(created->HasOneRef()); |
552 | } |
553 | ASSERT_FALSE(was_destroyed); |
554 | EXPECT_TRUE(created->HasOneRef()); |
555 | created->AssertHasOneRef(); |
556 | |
557 | EXPECT_EQ(created, r1.get()); |
558 | |
559 | r1 = nullptr; |
560 | EXPECT_TRUE(was_destroyed); |
561 | } |
562 | |
563 | class MyPublicClass : public RefCountedThreadSafe<MyPublicClass> { |
564 | public: |
565 | // Overloaded constructors work with |MakeRefCounted()|. |
566 | MyPublicClass() : has_num_(false), num_(0) {} |
567 | explicit MyPublicClass(int num) : has_num_(true), num_(num) {} |
568 | |
569 | ~MyPublicClass() {} |
570 | |
571 | bool has_num() const { return has_num_; } |
572 | int num() const { return num_; } |
573 | |
574 | private: |
575 | bool has_num_; |
576 | int num_; |
577 | |
578 | FML_DISALLOW_COPY_AND_ASSIGN(MyPublicClass); |
579 | }; |
580 | |
581 | // You can also just keep constructors and destructors public. Make sure that |
582 | // works (mostly that it compiles). |
583 | TEST(RefCountedTest, PublicCtorAndDtor) { |
584 | RefPtr<MyPublicClass> r1 = MakeRefCounted<MyPublicClass>(); |
585 | ASSERT_TRUE(r1); |
586 | EXPECT_FALSE(r1->has_num()); |
587 | |
588 | RefPtr<MyPublicClass> r2 = MakeRefCounted<MyPublicClass>(123); |
589 | ASSERT_TRUE(r2); |
590 | EXPECT_TRUE(r2->has_num()); |
591 | EXPECT_EQ(123, r2->num()); |
592 | EXPECT_NE(r1.get(), r2.get()); |
593 | |
594 | r1 = r2; |
595 | EXPECT_TRUE(r1->has_num()); |
596 | EXPECT_EQ(123, r1->num()); |
597 | EXPECT_EQ(r1.get(), r2.get()); |
598 | |
599 | r2 = nullptr; |
600 | EXPECT_FALSE(r2); |
601 | EXPECT_TRUE(r1->has_num()); |
602 | EXPECT_EQ(123, r1->num()); |
603 | |
604 | r1 = nullptr; |
605 | EXPECT_FALSE(r1); |
606 | } |
607 | |
608 | // The danger with having a public constructor or destructor is that certain |
609 | // things will compile. You should get some protection by assertions in Debug |
610 | // builds. |
611 | #ifndef NDEBUG |
612 | TEST(RefCountedTest, DebugChecks) { |
613 | { |
614 | MyPublicClass* p = new MyPublicClass(); |
615 | EXPECT_DEATH_IF_SUPPORTED( // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) |
616 | delete p, "!adoption_required_" ); |
617 | } |
618 | |
619 | { |
620 | MyPublicClass* p = new MyPublicClass(); |
621 | EXPECT_DEATH_IF_SUPPORTED( // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) |
622 | RefPtr<MyPublicClass> r(p), "!adoption_required_" ); |
623 | } |
624 | |
625 | { |
626 | RefPtr<MyPublicClass> r(MakeRefCounted<MyPublicClass>()); |
627 | EXPECT_DEATH_IF_SUPPORTED(delete r.get(), "destruction_started_" ); |
628 | } |
629 | } |
630 | #endif |
631 | |
632 | // TODO(vtl): Add (threaded) stress tests. |
633 | |
634 | } // namespace |
635 | } // namespace fml |
636 | |