1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include <cstddef>
16
17 #include "pw_allocator/allocator.h"
18 #include "pw_allocator/testing.h"
19 #include "pw_unit_test/framework.h"
20
21 namespace {
22
23 using AllocatorForTest = ::pw::allocator::test::AllocatorForTest<256>;
24
25 class UniquePtrTest : public ::testing::Test {
26 protected:
27 AllocatorForTest allocator_;
28 };
29
TEST_F(UniquePtrTest,DefaultInitializationIsNullptr)30 TEST_F(UniquePtrTest, DefaultInitializationIsNullptr) {
31 pw::UniquePtr<int> empty;
32 EXPECT_EQ(empty.get(), nullptr);
33 }
34
TEST_F(UniquePtrTest,OperatorEqNullptrOnEmptyUniquePtrSucceeds)35 TEST_F(UniquePtrTest, OperatorEqNullptrOnEmptyUniquePtrSucceeds) {
36 pw::UniquePtr<int> empty;
37 EXPECT_TRUE(empty == nullptr);
38 EXPECT_FALSE(empty != nullptr);
39 }
40
TEST_F(UniquePtrTest,OperatorEqNullptrAfterMakeUniqueFails)41 TEST_F(UniquePtrTest, OperatorEqNullptrAfterMakeUniqueFails) {
42 pw::UniquePtr<int> ptr = allocator_.MakeUnique<int>(5);
43 EXPECT_TRUE(ptr != nullptr);
44 EXPECT_FALSE(ptr == nullptr);
45 }
46
TEST_F(UniquePtrTest,OperatorEqNullptrAfterMakeUniqueNullptrTypeFails)47 TEST_F(UniquePtrTest, OperatorEqNullptrAfterMakeUniqueNullptrTypeFails) {
48 pw::UniquePtr<std::nullptr_t> ptr =
49 allocator_.MakeUnique<std::nullptr_t>(nullptr);
50 EXPECT_TRUE(ptr != nullptr);
51 EXPECT_FALSE(ptr == nullptr);
52 EXPECT_TRUE(*ptr == nullptr);
53 EXPECT_FALSE(*ptr != nullptr);
54 }
55
TEST_F(UniquePtrTest,MakeUniqueForwardsConstructorArguments)56 TEST_F(UniquePtrTest, MakeUniqueForwardsConstructorArguments) {
57 class MoveOnly {
58 public:
59 MoveOnly(int value) : value_(value) {}
60 MoveOnly(MoveOnly&) = delete;
61 MoveOnly(MoveOnly&&) {}
62 int Value() const { return value_; }
63
64 private:
65 int value_;
66 };
67
68 class BuiltWithMoveOnly {
69 public:
70 BuiltWithMoveOnly() = delete;
71 BuiltWithMoveOnly(MoveOnly&& mo) : value_(mo.Value()) {}
72 int Value() const { return value_; }
73
74 private:
75 int value_;
76 };
77
78 MoveOnly mo(6);
79 pw::UniquePtr<BuiltWithMoveOnly> ptr =
80 allocator_.MakeUnique<BuiltWithMoveOnly>(std::move(mo));
81 ASSERT_NE(ptr, nullptr);
82 EXPECT_EQ(ptr->Value(), 6);
83 }
84
TEST_F(UniquePtrTest,MoveConstructsFromSubClassAndFreesTotalSize)85 TEST_F(UniquePtrTest, MoveConstructsFromSubClassAndFreesTotalSize) {
86 struct Base {};
87 struct LargerSub : public Base {
88 std::array<std::byte, 128> mem;
89 };
90 pw::UniquePtr<LargerSub> ptr = allocator_.MakeUnique<LargerSub>();
91 ASSERT_NE(ptr, nullptr);
92 EXPECT_EQ(allocator_.allocate_size(), 128ul);
93 pw::UniquePtr<Base> base_ptr(std::move(ptr));
94
95 EXPECT_EQ(allocator_.deallocate_size(), 0ul);
96 // The size that is deallocated here should be the size of the larger
97 // subclass, not the size of the smaller base class.
98 base_ptr.Reset();
99 EXPECT_EQ(allocator_.deallocate_size(), 128ul);
100 }
101
TEST_F(UniquePtrTest,MoveAssignsFromSubClassAndFreesTotalSize)102 TEST_F(UniquePtrTest, MoveAssignsFromSubClassAndFreesTotalSize) {
103 struct Base {};
104 struct LargerSub : public Base {
105 std::array<std::byte, 128> mem;
106 };
107 pw::UniquePtr<LargerSub> ptr = allocator_.MakeUnique<LargerSub>();
108 ASSERT_NE(ptr, nullptr);
109 EXPECT_EQ(allocator_.allocate_size(), 128ul);
110 pw::UniquePtr<Base> base_ptr = std::move(ptr);
111
112 EXPECT_EQ(allocator_.deallocate_size(), 0ul);
113 // The size that is deallocated here should be the size of the larger
114 // subclass, not the size of the smaller base class.
115 base_ptr.Reset();
116 EXPECT_EQ(allocator_.deallocate_size(), 128ul);
117 }
118
TEST_F(UniquePtrTest,MoveAssignsToExistingDeallocates)119 TEST_F(UniquePtrTest, MoveAssignsToExistingDeallocates) {
120 pw::UniquePtr<size_t> size1 = allocator_.MakeUnique<size_t>(1);
121 ASSERT_NE(size1, nullptr);
122 EXPECT_EQ(*size1, 1U);
123
124 pw::UniquePtr<size_t> size2 = allocator_.MakeUnique<size_t>(2);
125 ASSERT_NE(size1, nullptr);
126 EXPECT_EQ(*size2, 2U);
127
128 EXPECT_EQ(allocator_.deallocate_size(), 0U);
129 size1 = std::move(size2);
130 EXPECT_EQ(allocator_.deallocate_size(), sizeof(size_t));
131 EXPECT_EQ(*size1, 2U);
132 }
133
TEST_F(UniquePtrTest,DestructorDestroysAndFrees)134 TEST_F(UniquePtrTest, DestructorDestroysAndFrees) {
135 int count = 0;
136 class DestructorCounter {
137 public:
138 DestructorCounter(int& count) : count_(&count) {}
139 ~DestructorCounter() { (*count_)++; }
140
141 private:
142 int* count_;
143 };
144 pw::UniquePtr<DestructorCounter> ptr =
145 allocator_.MakeUnique<DestructorCounter>(count);
146 ASSERT_NE(ptr, nullptr);
147
148 EXPECT_EQ(count, 0);
149 EXPECT_EQ(allocator_.deallocate_size(), 0ul);
150 ptr.Reset(); // Reset the UniquePtr, destroying its contents.
151 EXPECT_EQ(count, 1);
152 EXPECT_EQ(allocator_.deallocate_size(), sizeof(DestructorCounter));
153 }
154
155 class ConstructorCounter {
156 public:
ConstructorCounter()157 ConstructorCounter() { ++count_; }
158
getCount()159 size_t getCount() { return count_; }
160
161 private:
162 static size_t count_;
163 };
164 size_t ConstructorCounter::count_ = 0;
165
TEST_F(UniquePtrTest,ArrayElementsAreConstructed)166 TEST_F(UniquePtrTest, ArrayElementsAreConstructed) {
167 constexpr static size_t kArraySize = 5;
168
169 pw::UniquePtr<ConstructorCounter[]> ptr =
170 allocator_.MakeUniqueArray<ConstructorCounter>(kArraySize);
171 ASSERT_NE(ptr, nullptr);
172 EXPECT_EQ(ptr[0].getCount(), kArraySize);
173 }
174
175 class DestructorCounter {
176 public:
~DestructorCounter()177 ~DestructorCounter() { ++count_; }
178
179 static size_t count_;
180 };
181 size_t DestructorCounter::count_ = 0;
182
TEST_F(UniquePtrTest,DestructorDestroysAndFreesArray)183 TEST_F(UniquePtrTest, DestructorDestroysAndFreesArray) {
184 constexpr static size_t kArraySize = 5;
185 pw::UniquePtr<DestructorCounter[]> ptr =
186 allocator_.MakeUniqueArray<DestructorCounter>(kArraySize);
187 ASSERT_NE(ptr, nullptr);
188
189 EXPECT_EQ(DestructorCounter::count_, 0ul);
190 EXPECT_EQ(allocator_.deallocate_size(), 0ul);
191 ptr.Reset(); // Reset the UniquePtr, destroying its contents.
192 EXPECT_EQ(DestructorCounter::count_, kArraySize);
193 EXPECT_EQ(allocator_.deallocate_size(),
194 sizeof(DestructorCounter) * kArraySize);
195 }
196
TEST_F(UniquePtrTest,CanRelease)197 TEST_F(UniquePtrTest, CanRelease) {
198 struct Size final {
199 size_t value;
200 };
201 Size* size_ptr = nullptr;
202 {
203 pw::UniquePtr<Size> ptr = allocator_.MakeUnique<Size>(Size{.value = 1});
204 ASSERT_NE(ptr, nullptr);
205 EXPECT_EQ(ptr.deallocator(), &allocator_);
206 size_ptr = ptr.Release();
207
208 // Allocator pointer parameter is optional. Re-releasing returns null.
209 EXPECT_EQ(ptr.Release(), nullptr);
210 }
211
212 // Deallocate should not be called, even though UniquePtr goes out of scope.
213 EXPECT_EQ(allocator_.deallocate_size(), 0U);
214 allocator_.Delete(size_ptr);
215 EXPECT_EQ(allocator_.deallocate_size(), sizeof(Size));
216 }
217
TEST_F(UniquePtrTest,SizeReturnsCorrectSize)218 TEST_F(UniquePtrTest, SizeReturnsCorrectSize) {
219 pw::UniquePtr<int[]> ptr_array = allocator_.MakeUniqueArray<int>(5);
220 EXPECT_EQ(ptr_array.size(), 5U);
221 }
222
223 // Verify that the UniquePtr implementation is the size of 2 pointers for the
224 // non-array case. This should not contain the size_t size_ parameter.
225 static_assert(
226 sizeof(pw::UniquePtr<int>) == 2 * sizeof(void*),
227 "size_ parameter must be disabled for non-array UniquePtr instances");
228
229 } // namespace
230