blob: 5642eef43f7bd67349ed5b4e022349c46c912abc [file] [log] [blame]
// 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