xref: /aosp_15_r20/external/pigweed/pw_allocator/unique_ptr_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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