| // Copyright 2019 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "base/ref_ptr.h" |
| |
| #include "gtest/gtest.h" |
| |
| namespace iree { |
| namespace { |
| |
| class MyType : public RefObject<MyType> { |
| public: |
| int x = 5; |
| |
| using RefObject<MyType>::counter_; // Expose for testing. |
| }; |
| |
| TEST(RefPtrTest, Construction) { |
| // Empty. |
| ref_ptr<MyType> n1; |
| EXPECT_EQ(nullptr, n1.get()); |
| ref_ptr<MyType> n2(nullptr); |
| EXPECT_EQ(nullptr, n2.get()); |
| |
| // Assign a new ptr and add ref. |
| MyType* a_ptr = new MyType(); |
| EXPECT_EQ(1, a_ptr->counter_); |
| ref_ptr<MyType> a(a_ptr); |
| EXPECT_EQ(1, a->counter_); |
| |
| // Assign existing ptr without adding a ref. |
| ref_ptr<MyType> b(a_ptr); |
| EXPECT_EQ(1, b->counter_); |
| |
| // Add a new ref. |
| ref_ptr<MyType> c = add_ref(b); |
| EXPECT_EQ(2, c->counter_); |
| |
| b.release(); |
| } |
| |
| TEST(RefPtrTest, Assign) { |
| // Ok to assign nothing. |
| ref_ptr<MyType> n1 = assign_ref<MyType>(nullptr); |
| EXPECT_EQ(nullptr, n1.get()); |
| |
| ref_ptr<MyType> mt = make_ref<MyType>(); |
| EXPECT_EQ(1, mt->counter_); |
| ref_ptr<MyType> n2 = assign_ref(mt.get()); |
| EXPECT_EQ(1, mt->counter_); |
| mt.release(); // must release, as we assigned to n2. |
| EXPECT_EQ(1, n2->counter_); |
| n2.reset(); |
| } |
| |
| TEST(RefPtrTest, Retain) { |
| // Ok to retain nothing. |
| ref_ptr<MyType> n1 = add_ref<MyType>(nullptr); |
| EXPECT_EQ(nullptr, n1.get()); |
| |
| ref_ptr<MyType> mt = make_ref<MyType>(); |
| EXPECT_EQ(1, mt->counter_); |
| ref_ptr<MyType> n2 = add_ref(mt.get()); |
| EXPECT_EQ(2, mt->counter_); |
| mt.reset(); |
| EXPECT_EQ(1, n2->counter_); |
| n2.reset(); |
| } |
| |
| TEST(RefPtrTest, Reset) { |
| ref_ptr<MyType> a(new MyType()); |
| ref_ptr<MyType> b(new MyType()); |
| |
| // Reset to drop reference. |
| ref_ptr<MyType> a_copy = add_ref(a); |
| EXPECT_EQ(2, a_copy->counter_); |
| a.reset(); |
| EXPECT_EQ(1, a_copy->counter_); |
| |
| // Reset via = operator. |
| a = nullptr; |
| EXPECT_EQ(1, a_copy->counter_); |
| a = add_ref(a_copy); |
| EXPECT_EQ(2, a_copy->counter_); |
| |
| // No-op on empty ptrs. |
| ref_ptr<MyType> n; |
| n.reset(); |
| n.assign(nullptr); |
| } |
| |
| TEST(RefPtrTest, ReleaseAssign) { |
| ref_ptr<MyType> a(new MyType()); |
| |
| // Release a's pointer. |
| MyType* a_raw_ptr = a.get(); |
| MyType* a_ptr = a.release(); |
| EXPECT_EQ(a_raw_ptr, a_ptr); |
| EXPECT_EQ(nullptr, a.get()); |
| EXPECT_EQ(1, a_ptr->counter_); |
| |
| // Re-wrap in a ref_ptr. |
| a.assign(a_ptr); |
| EXPECT_EQ(1, a->counter_); |
| |
| // No-op on empty ptrs. |
| ref_ptr<MyType> n; |
| EXPECT_EQ(nullptr, n.release()); |
| } |
| |
| TEST(RefPtrTest, Accessors) { |
| ref_ptr<MyType> a(new MyType()); |
| EXPECT_EQ(5, a->x); |
| a->x = 100; |
| EXPECT_EQ(100, a->x); |
| |
| MyType& ra = *a; |
| ra.x = 200; |
| EXPECT_EQ(200, ra.x); |
| |
| const MyType& cra = *a; |
| EXPECT_EQ(200, cra.x); |
| } |
| |
| TEST(RefPtrTest, BooleanExpressions) { |
| ref_ptr<MyType> a(new MyType()); |
| ref_ptr<MyType> n; |
| |
| EXPECT_NE(nullptr, a.get()); |
| EXPECT_TRUE(a); |
| EXPECT_FALSE(!a); |
| EXPECT_EQ(true, static_cast<bool>(a)); |
| |
| EXPECT_EQ(nullptr, n.get()); |
| EXPECT_FALSE(n); |
| EXPECT_TRUE(!n); |
| EXPECT_EQ(false, static_cast<bool>(n)); |
| } |
| |
| TEST(RefPtrTest, Comparisons) { |
| ref_ptr<MyType> a(new MyType()); |
| ref_ptr<MyType> b(new MyType()); |
| ref_ptr<MyType> n; |
| |
| EXPECT_TRUE(a == a); |
| EXPECT_TRUE(a == a.get()); |
| EXPECT_TRUE(a.get() == a); |
| EXPECT_FALSE(a != a); |
| EXPECT_FALSE(a != a.get()); |
| EXPECT_FALSE(a.get() != a); |
| |
| EXPECT_FALSE(a == b); |
| EXPECT_FALSE(a == b.get()); |
| EXPECT_FALSE(a.get() == b); |
| EXPECT_TRUE(a != b); |
| EXPECT_TRUE(a != b.get()); |
| EXPECT_TRUE(a.get() != b); |
| |
| EXPECT_TRUE(n == n); |
| EXPECT_TRUE(n == n.get()); |
| EXPECT_TRUE(n.get() == n); |
| EXPECT_FALSE(n != n); |
| EXPECT_FALSE(n != n.get()); |
| EXPECT_FALSE(n.get() != n); |
| |
| EXPECT_FALSE(a < a); |
| EXPECT_TRUE(n < a); |
| } |
| |
| TEST(RefPtrTest, Swap) { |
| ref_ptr<MyType> a(new MyType()); |
| ref_ptr<MyType> b(new MyType()); |
| MyType* a_ptr = a.get(); |
| MyType* b_ptr = b.get(); |
| |
| swap(a, a); |
| EXPECT_EQ(a_ptr, a); |
| |
| swap(a, b); |
| EXPECT_EQ(a_ptr, b.get()); |
| EXPECT_EQ(b_ptr, a.get()); |
| |
| swap(a, b); |
| EXPECT_EQ(a_ptr, a.get()); |
| EXPECT_EQ(b_ptr, b.get()); |
| |
| ref_ptr<MyType> c; |
| swap(a, c); |
| EXPECT_EQ(a_ptr, c.get()); |
| EXPECT_EQ(nullptr, a.get()); |
| } |
| |
| TEST(RefPtrTest, Move) { |
| auto a = make_ref<MyType>(); |
| auto b = make_ref<MyType>(); |
| ref_ptr<MyType> c; |
| EXPECT_EQ(nullptr, c.get()); |
| |
| c = std::move(a); |
| EXPECT_NE(nullptr, c.get()); |
| |
| b = std::move(c); |
| EXPECT_NE(nullptr, b.get()); |
| } |
| |
| TEST(RefPtrTest, MoveCompatible) { |
| struct MyBaseType : public RefObject<MyBaseType> { |
| int x = 5; |
| using RefObject<MyBaseType>::counter_; // Expose for testing. |
| }; |
| struct MyTypeA : public MyBaseType { |
| int a = 6; |
| }; |
| struct MyTypeB : public MyBaseType { |
| int b = 7; |
| }; |
| |
| ref_ptr<MyTypeA> a = make_ref<MyTypeA>(); |
| EXPECT_EQ(1, a->counter_); |
| ref_ptr<MyBaseType> base = add_ref(a); |
| EXPECT_EQ(a.get(), base.get()); |
| EXPECT_EQ(2, a->counter_); |
| |
| base = make_ref<MyTypeB>(); |
| EXPECT_EQ(1, a->counter_); |
| EXPECT_EQ(1, base->counter_); |
| } |
| |
| TEST(RefPtrTest, StackAllocation) { |
| static int alloc_count = 0; |
| class StackAllocationType : public RefObject<StackAllocationType> { |
| public: |
| StackAllocationType() { ++alloc_count; } |
| ~StackAllocationType() { --alloc_count; } |
| }; |
| { |
| StackAllocationType a; |
| EXPECT_EQ(1, alloc_count); |
| } |
| EXPECT_EQ(0, alloc_count); |
| } |
| |
| TEST(RefPtrTest, DefaultDeleter) { |
| static int alloc_count = 0; |
| class DefaultDeleterType : public RefObject<DefaultDeleterType> { |
| public: |
| DefaultDeleterType() { ++alloc_count; } |
| ~DefaultDeleterType() { --alloc_count; } |
| }; |
| |
| // Empty is ok. |
| ref_ptr<DefaultDeleterType> n; |
| n.reset(); |
| |
| // Lifecycle. |
| EXPECT_EQ(0, alloc_count); |
| ref_ptr<DefaultDeleterType> a = make_ref<DefaultDeleterType>(); |
| EXPECT_EQ(1, alloc_count); |
| a.reset(); |
| EXPECT_EQ(0, alloc_count); |
| } |
| |
| TEST(RefPtrTest, InlineDeallocator) { |
| static int alloc_count = 0; |
| class CustomDeleterType : public RefObject<CustomDeleterType> { |
| public: |
| CustomDeleterType() { ++alloc_count; } |
| static void Delete(CustomDeleterType* ptr) { |
| --alloc_count; |
| ::operator delete(ptr); |
| } |
| }; |
| |
| // Empty is ok. |
| ref_ptr<CustomDeleterType> n; |
| n.reset(); |
| |
| // Lifecycle. |
| EXPECT_EQ(0, alloc_count); |
| auto a = make_ref<CustomDeleterType>(); |
| EXPECT_EQ(1, alloc_count); |
| a.reset(); |
| EXPECT_EQ(0, alloc_count); |
| } |
| |
| class VirtualDtorTypeA : public RefObject<VirtualDtorTypeA> { |
| public: |
| VirtualDtorTypeA() { ++alloc_count_a; } |
| virtual ~VirtualDtorTypeA() { --alloc_count_a; } |
| static int alloc_count_a; |
| }; |
| int VirtualDtorTypeA::alloc_count_a = 0; |
| |
| class VirtualDtorTypeB : public VirtualDtorTypeA { |
| public: |
| VirtualDtorTypeB() { ++alloc_count_b; } |
| ~VirtualDtorTypeB() override { --alloc_count_b; } |
| static int alloc_count_b; |
| }; |
| int VirtualDtorTypeB::alloc_count_b = 0; |
| |
| TEST(RefPtrTest, VirtualDestructor) { |
| // Empty is ok. |
| ref_ptr<VirtualDtorTypeB> n; |
| n.reset(); |
| |
| // Lifecycle. |
| EXPECT_EQ(0, VirtualDtorTypeA::alloc_count_a); |
| EXPECT_EQ(0, VirtualDtorTypeB::alloc_count_b); |
| ref_ptr<VirtualDtorTypeB> a = make_ref<VirtualDtorTypeB>(); |
| EXPECT_EQ(1, VirtualDtorTypeA::alloc_count_a); |
| EXPECT_EQ(1, VirtualDtorTypeB::alloc_count_b); |
| a.reset(); |
| EXPECT_EQ(0, VirtualDtorTypeA::alloc_count_a); |
| EXPECT_EQ(0, VirtualDtorTypeB::alloc_count_b); |
| } |
| |
| } // namespace |
| } // namespace iree |