1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2017 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#include "config.h"
19
20#include "atomic.h"
21#include "tagged_ptr.h"
22
23#include <gtest/gtest.h>
24
25/*
26 * Unit tests for the TaggedPtr class.
27 */
28
29/// Test constructor taking object
30TEST(TaggedPtrTest, constructorObjectTest) {
31    uint32_t data = 123;
32    TaggedPtr<uint32_t> taggedPtr(&data);
33    ASSERT_EQ(&data, taggedPtr.get());
34}
35
36/// Test constructor taking object and tag
37TEST(TaggedPtrTest, constructorObjectAndTagTest) {
38    uint32_t data = 123;
39    TaggedPtr<uint32_t> taggedPtr(&data, 456);
40    EXPECT_EQ(&data, taggedPtr.get());
41    EXPECT_EQ(456, taggedPtr.getTag());
42}
43
44/// Test equal operator of TaggedPtr
45TEST(TaggedPtrTest, equalTest) {
46    uint32_t data = 0;
47    TaggedPtr<uint32_t> taggedPtr(&data);
48    EXPECT_TRUE(taggedPtr.get() == &data);
49}
50
51/// Test not equal operator of TaggedPtr
52TEST(TaggedPtrTest, notEqualTest) {
53    uint32_t data = 0;
54    uint32_t newData = 0;
55    TaggedPtr<uint32_t> taggedPtr(&data);
56    EXPECT_TRUE(taggedPtr.get() != &newData);
57}
58
59/// Test boolean operator of TaggedPtr - True case
60TEST(TaggedPtrTest, boolTrueTest) {
61    uint32_t data = 123;
62    TaggedPtr<uint32_t> taggedPtr(&data);
63    EXPECT_TRUE(taggedPtr);
64}
65
66/// Test boolean operator of TaggedPtr - False case
67TEST(TaggedPtrTest, boolFalseTest) {
68    TaggedPtr<uint32_t> taggedPtr;
69    EXPECT_FALSE(taggedPtr);
70}
71
72/// Check that when the tag is set if the pointer is nullptr then calling the
73/// bool method on the taggedptr will return false
74TEST(TaggedPtrTest, boolWithTagFalseTest) {
75    TaggedPtr<uint32_t> taggedPtr(nullptr, 123);
76    ASSERT_EQ(123, taggedPtr.getTag());
77    EXPECT_FALSE(taggedPtr);
78}
79
80/// Test the -> operator of TaggedPtr
81TEST(TaggedPtrTest, ptrTest) {
82    class TestObject {
83    public:
84        TestObject() {
85        }
86        uint32_t data;
87    };
88
89    TestObject testObject;
90    testObject.data = 123;
91
92    TaggedPtr<TestObject> taggedPtr(&testObject);
93    EXPECT_EQ(123, taggedPtr->data);
94}
95
96/// Test set and get of TaggedPtr
97TEST(TaggedPtrTest, setObjTest) {
98    uint32_t data = 0;
99    TaggedPtr<uint32_t> taggedPtr(nullptr);
100    taggedPtr.set(&data);
101    EXPECT_EQ(&data, taggedPtr.get());
102}
103
104/// Test setTag and getTag of TaggedPtr
105TEST(TaggedPtrTest, setTagTest) {
106    TaggedPtr<uint32_t> taggedPtr(nullptr);
107    taggedPtr.setTag(123);
108    EXPECT_EQ(123, taggedPtr.getTag());
109}
110
111/// Check that the tagged pointer can have its tag set without affecting where
112/// the pointer points to.
113TEST(TaggedPtrTest, pointerUnaffectedTest) {
114    uint32_t data = 123;
115
116    TaggedPtr<uint32_t> taggedPtr(&data);
117    auto obj = taggedPtr.get();
118
119    // Tag should start at zero i.e. empty
120    ASSERT_EQ(0, taggedPtr.getTag());
121    taggedPtr.setTag(456);
122    ASSERT_EQ(456, taggedPtr.getTag());
123    EXPECT_EQ(obj, taggedPtr.get());
124    EXPECT_EQ(123, *(taggedPtr.get()));
125}
126
127/// Check that the tagged pointer can have its pointer set without affecting the
128/// data held in the tag
129TEST(TaggedPtrTest, tagUnaffectedTest) {
130    uint32_t data = 0;
131
132    TaggedPtr<uint32_t> taggedPtr(nullptr, 123);
133    ASSERT_EQ(123, taggedPtr.getTag());
134    taggedPtr.set(&data);
135    ASSERT_EQ(&data, taggedPtr.get());
136    EXPECT_EQ(123, taggedPtr.getTag());
137}
138
139/// Check that the tag can be set using the updateTag helper method
140TEST(TaggedPtrTest, updateTagTest) {
141    // TestObject needs to inherit from RCValue because SingleThreadedRCPtr
142    // only takes RCValues
143    class TestObject : public RCValue {
144    public:
145        TestObject() : data(123) {
146        }
147
148        uint32_t getData() {
149            return data;
150        }
151
152    private:
153        uint32_t data;
154    };
155
156    // Custom deleter for TestObject objects
157    struct Deleter {
158        void operator()(TaggedPtr<TestObject> val) {
159            // Does not do anything
160        }
161    };
162
163    TestObject to;
164    SingleThreadedRCPtr<TestObject, TaggedPtr<TestObject>, Deleter> ptr{
165            TaggedPtr<TestObject>(&to)};
166    TaggedPtr<TestObject>::updateTag(ptr, 456);
167    EXPECT_EQ(456, ptr.get().getTag());
168}
169
170/// Check that the tag can be set using the updateTag helper method when pointer
171/// is a std::unique_ptr
172TEST(TaggedPtrTest, updateTagTestUniquePtr) {
173    class TestObject {
174    public:
175        TestObject() : data(123) {
176        }
177
178        uint32_t getData() {
179            return data;
180        }
181
182    private:
183        uint32_t data;
184    };
185
186    // Custom deleter for TestObject objects
187    struct Deleter {
188        void operator()(TaggedPtr<TestObject> val) {
189            // Does not do anything
190        }
191    };
192
193    using UniquePtr =
194            std::unique_ptr<TestObject, TaggedPtrDeleter<TestObject, Deleter>>;
195
196    TestObject to;
197    UniquePtr ptr{TaggedPtr<TestObject>(&to)};
198    TaggedPtr<TestObject>::updateTag(ptr, 456);
199    EXPECT_EQ(456, ptr.get().getTag());
200}
201