|  | // Copyright 2013 The Flutter Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | // This file tests both ref_counted.h and ref_ptr.h (which the former includes). | 
|  | // TODO(vtl): Possibly we could separate these tests out better, since a lot of | 
|  | // it is actually testing |RefPtr|. | 
|  |  | 
|  | #include "flutter/fml/memory/ref_counted.h" | 
|  |  | 
|  | #include "flutter/fml/macros.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | #if defined(__clang__) | 
|  | #define ALLOW_PESSIMIZING_MOVE(code_line)                                   \ | 
|  | _Pragma("clang diagnostic push")                                          \ | 
|  | _Pragma("clang diagnostic ignored \"-Wpessimizing-move\"") code_line; \ | 
|  | _Pragma("clang diagnostic pop") | 
|  | #else | 
|  | #define ALLOW_PESSIMIZING_MOVE(code_line) code_line; | 
|  | #endif | 
|  |  | 
|  | #if defined(__clang__) | 
|  | #define ALLOW_SELF_MOVE(code_line)                                   \ | 
|  | _Pragma("clang diagnostic push")                                   \ | 
|  | _Pragma("clang diagnostic ignored \"-Wself-move\"") code_line; \ | 
|  | _Pragma("clang diagnostic pop") | 
|  | #else | 
|  | #define ALLOW_SELF_MOVE(code_line) code_line; | 
|  | #endif | 
|  |  | 
|  | #if defined(__clang__) | 
|  | #define ALLOW_SELF_ASSIGN_OVERLOADED(code_line)                        \ | 
|  | _Pragma("clang diagnostic push")                                     \ | 
|  | _Pragma("clang diagnostic ignored \"-Wself-assign-overloaded\"") \ | 
|  | code_line;                                                   \ | 
|  | _Pragma("clang diagnostic pop") | 
|  | #else | 
|  | #define ALLOW_SELF_ASSIGN_OVERLOADED(code_line) code_line; | 
|  | #endif | 
|  |  | 
|  | namespace fml { | 
|  | namespace { | 
|  |  | 
|  | class MyClass : public RefCountedThreadSafe<MyClass> { | 
|  | protected: | 
|  | MyClass(MyClass** created, bool* was_destroyed) | 
|  | : was_destroyed_(was_destroyed) { | 
|  | if (created) { | 
|  | *created = this; | 
|  | } | 
|  | } | 
|  | virtual ~MyClass() { | 
|  | if (was_destroyed_) { | 
|  | *was_destroyed_ = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | FML_FRIEND_REF_COUNTED_THREAD_SAFE(MyClass); | 
|  | FML_FRIEND_MAKE_REF_COUNTED(MyClass); | 
|  |  | 
|  | bool* was_destroyed_; | 
|  |  | 
|  | FML_DISALLOW_COPY_AND_ASSIGN(MyClass); | 
|  | }; | 
|  |  | 
|  | class MySubclass final : public MyClass { | 
|  | private: | 
|  | FML_FRIEND_REF_COUNTED_THREAD_SAFE(MySubclass); | 
|  | FML_FRIEND_MAKE_REF_COUNTED(MySubclass); | 
|  |  | 
|  | MySubclass(MySubclass** created, bool* was_destroyed) | 
|  | : MyClass(nullptr, was_destroyed) { | 
|  | if (created) { | 
|  | *created = this; | 
|  | } | 
|  | } | 
|  | ~MySubclass() override {} | 
|  |  | 
|  | FML_DISALLOW_COPY_AND_ASSIGN(MySubclass); | 
|  | }; | 
|  |  | 
|  | TEST(RefCountedTest, Constructors) { | 
|  | bool was_destroyed; | 
|  |  | 
|  | { | 
|  | // Default. | 
|  | RefPtr<MyClass> r; | 
|  | EXPECT_TRUE(r.get() == nullptr); | 
|  | EXPECT_FALSE(r); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Nullptr. | 
|  | RefPtr<MyClass> r(nullptr); | 
|  | EXPECT_TRUE(r.get() == nullptr); | 
|  | EXPECT_FALSE(r); | 
|  | } | 
|  |  | 
|  | { | 
|  | MyClass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | // Adopt, then RVO. | 
|  | RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, &was_destroyed)); | 
|  | EXPECT_TRUE(created); | 
|  | EXPECT_EQ(created, r.get()); | 
|  | EXPECT_TRUE(r); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MyClass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | // Adopt, then move. | 
|  | ALLOW_PESSIMIZING_MOVE(RefPtr<MyClass> r( | 
|  | std::move(MakeRefCounted<MyClass>(&created, &was_destroyed)))) | 
|  | EXPECT_TRUE(created); | 
|  | EXPECT_EQ(created, r.get()); | 
|  | EXPECT_TRUE(r); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MyClass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); | 
|  | // Copy. | 
|  | // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) | 
|  | RefPtr<MyClass> r2(r1); | 
|  | EXPECT_TRUE(created); | 
|  | EXPECT_EQ(created, r1.get()); | 
|  | EXPECT_EQ(created, r2.get()); | 
|  | EXPECT_TRUE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MyClass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); | 
|  | // From raw pointer. | 
|  | RefPtr<MyClass> r2(created); | 
|  | EXPECT_TRUE(created); | 
|  | EXPECT_EQ(created, r1.get()); | 
|  | EXPECT_EQ(created, r2.get()); | 
|  | EXPECT_TRUE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MySubclass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | // Adopt, then "move". | 
|  | RefPtr<MyClass> r(MakeRefCounted<MySubclass>(&created, &was_destroyed)); | 
|  | EXPECT_TRUE(created); | 
|  | EXPECT_EQ(static_cast<MyClass*>(created), r.get()); | 
|  | EXPECT_TRUE(r); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MySubclass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | // Adopt, then "move". | 
|  | ALLOW_PESSIMIZING_MOVE(RefPtr<MyClass> r( | 
|  | std::move(MakeRefCounted<MySubclass>(&created, &was_destroyed)))) | 
|  | EXPECT_TRUE(created); | 
|  | EXPECT_EQ(static_cast<MyClass*>(created), r.get()); | 
|  | EXPECT_TRUE(r); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MySubclass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); | 
|  | // "Copy". | 
|  | RefPtr<MyClass> r2(r1); | 
|  | EXPECT_TRUE(created); | 
|  | EXPECT_EQ(static_cast<MyClass*>(created), r1.get()); | 
|  | EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); | 
|  | EXPECT_TRUE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MySubclass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); | 
|  | // From raw pointer. | 
|  | RefPtr<MyClass> r2(created); | 
|  | EXPECT_TRUE(created); | 
|  | EXPECT_EQ(static_cast<MyClass*>(created), r1.get()); | 
|  | EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); | 
|  | EXPECT_TRUE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  | } | 
|  |  | 
|  | TEST(RefCountedTest, NullAssignmentToNull) { | 
|  | RefPtr<MyClass> r1; | 
|  | // No-op null assignment using |nullptr|. | 
|  | r1 = nullptr; | 
|  | EXPECT_TRUE(r1.get() == nullptr); | 
|  | EXPECT_FALSE(r1); | 
|  |  | 
|  | RefPtr<MyClass> r2; | 
|  | // No-op null assignment using copy constructor. | 
|  | r1 = r2; | 
|  | EXPECT_TRUE(r1.get() == nullptr); | 
|  | EXPECT_TRUE(r2.get() == nullptr); | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_FALSE(r2); | 
|  |  | 
|  | // No-op null assignment using move constructor. | 
|  | r1 = std::move(r2); | 
|  | EXPECT_TRUE(r1.get() == nullptr); | 
|  | // The clang linter flags the method called on the moved-from reference, but | 
|  | // this is testing the move implementation, so it is marked NOLINT. | 
|  | EXPECT_TRUE(r2.get() == nullptr);  // NOLINT(clang-analyzer-cplusplus.Move, | 
|  | // bugprone-use-after-move) | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_FALSE(r2); | 
|  |  | 
|  | RefPtr<MySubclass> r3; | 
|  | // No-op null assignment using "copy" constructor. | 
|  | r1 = r3; | 
|  | EXPECT_TRUE(r1.get() == nullptr); | 
|  | EXPECT_TRUE(r3.get() == nullptr); | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_FALSE(r3); | 
|  |  | 
|  | // No-op null assignment using "move" constructor. | 
|  | r1 = std::move(r3); | 
|  | EXPECT_TRUE(r1.get() == nullptr); | 
|  | EXPECT_TRUE(r3.get() == nullptr);  // NOLINT(bugprone-use-after-move) | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_FALSE(r3); | 
|  | } | 
|  |  | 
|  | TEST(RefCountedTest, NonNullAssignmentToNull) { | 
|  | bool was_destroyed; | 
|  |  | 
|  | { | 
|  | MyClass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); | 
|  | RefPtr<MyClass> r2; | 
|  | // Copy assignment (to null ref pointer). | 
|  | r2 = r1; | 
|  | EXPECT_EQ(created, r1.get()); | 
|  | EXPECT_EQ(created, r2.get()); | 
|  | EXPECT_TRUE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MyClass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); | 
|  | RefPtr<MyClass> r2; | 
|  | // Move assignment (to null ref pointer). | 
|  | r2 = std::move(r1); | 
|  | // The clang linter flags the method called on the moved-from reference, but | 
|  | // this is testing the move implementation, so it is marked NOLINT. | 
|  | EXPECT_TRUE(r1.get() == nullptr);  // NOLINT(clang-analyzer-cplusplus.Move, | 
|  | // bugprone-use-after-move) | 
|  | EXPECT_EQ(created, r2.get()); | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MySubclass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); | 
|  | RefPtr<MyClass> r2; | 
|  | // "Copy" assignment (to null ref pointer). | 
|  | r2 = r1; | 
|  | EXPECT_EQ(created, r1.get()); | 
|  | EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); | 
|  | EXPECT_TRUE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MySubclass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); | 
|  | RefPtr<MyClass> r2; | 
|  | // "Move" assignment (to null ref pointer). | 
|  | r2 = std::move(r1); | 
|  | EXPECT_TRUE(r1.get() == nullptr);  // NOLINT(bugprone-use-after-move) | 
|  | EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  | } | 
|  |  | 
|  | TEST(RefCountedTest, NullAssignmentToNonNull) { | 
|  | bool was_destroyed = false; | 
|  | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(nullptr, &was_destroyed)); | 
|  | // Null assignment (to non-null ref pointer) using |nullptr|. | 
|  | r1 = nullptr; | 
|  | EXPECT_TRUE(r1.get() == nullptr); | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | was_destroyed = false; | 
|  | r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); | 
|  | RefPtr<MyClass> r2; | 
|  | // Null assignment (to non-null ref pointer) using copy constructor. | 
|  | r1 = r2; | 
|  | EXPECT_TRUE(r1.get() == nullptr); | 
|  | EXPECT_TRUE(r2.get() == nullptr); | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_FALSE(r2); | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | was_destroyed = false; | 
|  | r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); | 
|  | // Null assignment using move constructor. | 
|  | r1 = std::move(r2); | 
|  | EXPECT_TRUE(r1.get() == nullptr); | 
|  | // The clang linter flags the method called on the moved-from reference, but | 
|  | // this is testing the move implementation, so it is marked NOLINT. | 
|  | EXPECT_TRUE(r2.get() == nullptr);  // NOLINT(clang-analyzer-cplusplus.Move, | 
|  | // bugprone-use-after-move) | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_FALSE(r2); | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | was_destroyed = false; | 
|  | r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); | 
|  | RefPtr<MySubclass> r3; | 
|  | // Null assignment (to non-null ref pointer) using "copy" constructor. | 
|  | r1 = r3; | 
|  | EXPECT_TRUE(r1.get() == nullptr); | 
|  | EXPECT_TRUE(r3.get() == nullptr); | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_FALSE(r3); | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | was_destroyed = false; | 
|  | r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); | 
|  | // Null assignment (to non-null ref pointer) using "move" constructor. | 
|  | r1 = std::move(r3); | 
|  | EXPECT_TRUE(r1.get() == nullptr); | 
|  | EXPECT_TRUE(r3.get() == nullptr);  // NOLINT(bugprone-use-after-move) | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_FALSE(r3); | 
|  | EXPECT_TRUE(was_destroyed); | 
|  | } | 
|  |  | 
|  | TEST(RefCountedTest, NonNullAssignmentToNonNull) { | 
|  | bool was_destroyed1; | 
|  | bool was_destroyed2; | 
|  |  | 
|  | { | 
|  | was_destroyed1 = false; | 
|  | was_destroyed2 = false; | 
|  | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(nullptr, &was_destroyed1)); | 
|  | RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); | 
|  | // Copy assignment (to non-null ref pointer). | 
|  | r2 = r1; | 
|  | EXPECT_EQ(r1.get(), r2.get()); | 
|  | EXPECT_TRUE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed1); | 
|  | EXPECT_TRUE(was_destroyed2); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed1); | 
|  |  | 
|  | { | 
|  | was_destroyed1 = false; | 
|  | was_destroyed2 = false; | 
|  | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(nullptr, &was_destroyed1)); | 
|  | RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); | 
|  | // Move assignment (to non-null ref pointer). | 
|  | r2 = std::move(r1); | 
|  | // The clang linter flags the method called on the moved-from reference, but | 
|  | // this is testing the move implementation, so it is marked NOLINT. | 
|  | EXPECT_TRUE(r1.get() == nullptr);  // NOLINT(clang-analyzer-cplusplus.Move, | 
|  | // bugprone-use-after-move) | 
|  | EXPECT_FALSE(r2.get() == nullptr); | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed1); | 
|  | EXPECT_TRUE(was_destroyed2); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed1); | 
|  |  | 
|  | { | 
|  | was_destroyed1 = false; | 
|  | was_destroyed2 = false; | 
|  | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(nullptr, &was_destroyed1)); | 
|  | RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); | 
|  | // "Copy" assignment (to non-null ref pointer). | 
|  | r2 = r1; | 
|  | EXPECT_EQ(r1.get(), r2.get()); | 
|  | EXPECT_TRUE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed1); | 
|  | EXPECT_TRUE(was_destroyed2); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed1); | 
|  |  | 
|  | { | 
|  | was_destroyed1 = false; | 
|  | was_destroyed2 = false; | 
|  | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(nullptr, &was_destroyed1)); | 
|  | RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); | 
|  | // Move assignment (to non-null ref pointer). | 
|  | r2 = std::move(r1); | 
|  | EXPECT_TRUE(r1.get() == nullptr);  // NOLINT(bugprone-use-after-move) | 
|  | EXPECT_FALSE(r2.get() == nullptr); | 
|  | EXPECT_FALSE(r1); | 
|  | EXPECT_TRUE(r2); | 
|  | EXPECT_FALSE(was_destroyed1); | 
|  | EXPECT_TRUE(was_destroyed2); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed1); | 
|  | } | 
|  |  | 
|  | TEST(RefCountedTest, SelfAssignment) { | 
|  | bool was_destroyed; | 
|  |  | 
|  | { | 
|  | MyClass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | // This line is marked NOLINT because the clang linter does not reason about | 
|  | // the value of the reference count. In particular, the self-assignment | 
|  | // below is handled in the copy constructor by a refcount increment then | 
|  | // decrement. The linter sees only that the decrement might destroy the | 
|  | // object. | 
|  | RefPtr<MyClass> r(MakeRefCounted<MyClass>(  // NOLINT | 
|  | &created, &was_destroyed)); | 
|  | // Copy. | 
|  | ALLOW_SELF_ASSIGN_OVERLOADED(r = r); | 
|  | EXPECT_EQ(created, r.get()); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  |  | 
|  | { | 
|  | MyClass* created = nullptr; | 
|  | was_destroyed = false; | 
|  | RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, &was_destroyed)); | 
|  | // Move. | 
|  | ALLOW_SELF_MOVE(r = std::move(r)) | 
|  | EXPECT_EQ(created, r.get()); | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | } | 
|  | EXPECT_TRUE(was_destroyed); | 
|  | } | 
|  |  | 
|  | TEST(RefCountedTest, Swap) { | 
|  | MyClass* created1 = nullptr; | 
|  | static bool was_destroyed1; | 
|  | was_destroyed1 = false; | 
|  | RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created1, &was_destroyed1)); | 
|  | EXPECT_TRUE(created1); | 
|  | EXPECT_EQ(created1, r1.get()); | 
|  |  | 
|  | MyClass* created2 = nullptr; | 
|  | static bool was_destroyed2; | 
|  | was_destroyed2 = false; | 
|  | RefPtr<MyClass> r2(MakeRefCounted<MyClass>(&created2, &was_destroyed2)); | 
|  | EXPECT_TRUE(created2); | 
|  | EXPECT_EQ(created2, r2.get()); | 
|  | EXPECT_NE(created1, created2); | 
|  |  | 
|  | r1.swap(r2); | 
|  | EXPECT_EQ(created2, r1.get()); | 
|  | EXPECT_EQ(created1, r2.get()); | 
|  | } | 
|  |  | 
|  | TEST(RefCountedTest, GetAndDereferenceOperators) { | 
|  | // Note: We check here that .get(), operator*, and operator-> are const, but | 
|  | // return non-const pointers/refs. | 
|  |  | 
|  | MyClass* created = nullptr; | 
|  | const RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, nullptr)); | 
|  | MyClass* ptr = r.get();  // Assign to non-const pointer. | 
|  | EXPECT_EQ(created, ptr); | 
|  | ptr = r.operator->();  // Assign to non-const pointer. | 
|  | EXPECT_EQ(created, ptr); | 
|  | MyClass& ref = *r;  // "Assign" to non-const reference. | 
|  | EXPECT_EQ(created, &ref); | 
|  | } | 
|  |  | 
|  | // You can manually call |AddRef()| and |Release()| if you want. | 
|  | TEST(RefCountedTest, AddRefRelease) { | 
|  | MyClass* created = nullptr; | 
|  | bool was_destroyed = false; | 
|  | { | 
|  | RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, &was_destroyed)); | 
|  | EXPECT_EQ(created, r.get()); | 
|  | created->AddRef(); | 
|  | } | 
|  | EXPECT_FALSE(was_destroyed); | 
|  | created->Release(); | 
|  | EXPECT_TRUE(was_destroyed); | 
|  | } | 
|  |  | 
|  | TEST(RefCountedTest, Mix) { | 
|  | MySubclass* created = nullptr; | 
|  | bool was_destroyed = false; | 
|  | RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); | 
|  | ASSERT_FALSE(was_destroyed); | 
|  | EXPECT_TRUE(created->HasOneRef()); | 
|  | created->AssertHasOneRef(); | 
|  |  | 
|  | RefPtr<MySubclass> r2 = r1; | 
|  | ASSERT_FALSE(was_destroyed); | 
|  | EXPECT_FALSE(created->HasOneRef()); | 
|  |  | 
|  | r1 = nullptr; | 
|  | ASSERT_FALSE(was_destroyed); | 
|  | created->AssertHasOneRef(); | 
|  |  | 
|  | { | 
|  | RefPtr<MyClass> r3 = r2; | 
|  | EXPECT_FALSE(created->HasOneRef()); | 
|  | { | 
|  | // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) | 
|  | RefPtr<MyClass> r4(r3); | 
|  | r2 = nullptr; | 
|  | ASSERT_FALSE(was_destroyed); | 
|  | EXPECT_FALSE(created->HasOneRef()); | 
|  | } | 
|  | ASSERT_FALSE(was_destroyed); | 
|  | EXPECT_TRUE(created->HasOneRef()); | 
|  | created->AssertHasOneRef(); | 
|  |  | 
|  | r1 = RefPtr<MySubclass>(static_cast<MySubclass*>(r3.get())); | 
|  | ASSERT_FALSE(was_destroyed); | 
|  | EXPECT_FALSE(created->HasOneRef()); | 
|  | } | 
|  | ASSERT_FALSE(was_destroyed); | 
|  | EXPECT_TRUE(created->HasOneRef()); | 
|  | created->AssertHasOneRef(); | 
|  |  | 
|  | EXPECT_EQ(created, r1.get()); | 
|  |  | 
|  | r1 = nullptr; | 
|  | EXPECT_TRUE(was_destroyed); | 
|  | } | 
|  |  | 
|  | class MyPublicClass : public RefCountedThreadSafe<MyPublicClass> { | 
|  | public: | 
|  | // Overloaded constructors work with |MakeRefCounted()|. | 
|  | MyPublicClass() : has_num_(false), num_(0) {} | 
|  | explicit MyPublicClass(int num) : has_num_(true), num_(num) {} | 
|  |  | 
|  | ~MyPublicClass() {} | 
|  |  | 
|  | bool has_num() const { return has_num_; } | 
|  | int num() const { return num_; } | 
|  |  | 
|  | private: | 
|  | bool has_num_; | 
|  | int num_; | 
|  |  | 
|  | FML_DISALLOW_COPY_AND_ASSIGN(MyPublicClass); | 
|  | }; | 
|  |  | 
|  | // You can also just keep constructors and destructors public. Make sure that | 
|  | // works (mostly that it compiles). | 
|  | TEST(RefCountedTest, PublicCtorAndDtor) { | 
|  | RefPtr<MyPublicClass> r1 = MakeRefCounted<MyPublicClass>(); | 
|  | ASSERT_TRUE(r1); | 
|  | EXPECT_FALSE(r1->has_num()); | 
|  |  | 
|  | RefPtr<MyPublicClass> r2 = MakeRefCounted<MyPublicClass>(123); | 
|  | ASSERT_TRUE(r2); | 
|  | EXPECT_TRUE(r2->has_num()); | 
|  | EXPECT_EQ(123, r2->num()); | 
|  | EXPECT_NE(r1.get(), r2.get()); | 
|  |  | 
|  | r1 = r2; | 
|  | EXPECT_TRUE(r1->has_num()); | 
|  | EXPECT_EQ(123, r1->num()); | 
|  | EXPECT_EQ(r1.get(), r2.get()); | 
|  |  | 
|  | r2 = nullptr; | 
|  | EXPECT_FALSE(r2); | 
|  | EXPECT_TRUE(r1->has_num()); | 
|  | EXPECT_EQ(123, r1->num()); | 
|  |  | 
|  | r1 = nullptr; | 
|  | EXPECT_FALSE(r1); | 
|  | } | 
|  |  | 
|  | // The danger with having a public constructor or destructor is that certain | 
|  | // things will compile. You should get some protection by assertions in Debug | 
|  | // builds. | 
|  | #ifndef NDEBUG | 
|  | TEST(RefCountedTest, DebugChecks) { | 
|  | { | 
|  | MyPublicClass* p = new MyPublicClass(); | 
|  | EXPECT_DEATH_IF_SUPPORTED(  // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) | 
|  | delete p, "!adoption_required_"); | 
|  | } | 
|  |  | 
|  | { | 
|  | MyPublicClass* p = new MyPublicClass(); | 
|  | EXPECT_DEATH_IF_SUPPORTED(  // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) | 
|  | RefPtr<MyPublicClass> r(p), "!adoption_required_"); | 
|  | } | 
|  |  | 
|  | { | 
|  | RefPtr<MyPublicClass> r(MakeRefCounted<MyPublicClass>()); | 
|  | EXPECT_DEATH_IF_SUPPORTED(delete r.get(), "destruction_started_"); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // TODO(vtl): Add (threaded) stress tests. | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace fml |