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 "pw_allocator/allocator.h"
16
17 #include <cstddef>
18
19 #include "pw_allocator/capability.h"
20 #include "pw_allocator/testing.h"
21 #include "pw_unit_test/framework.h"
22
23 namespace {
24
25 using ::pw::allocator::Capability;
26 using ::pw::allocator::Layout;
27 using AllocatorForTest = ::pw::allocator::test::AllocatorForTest<256>;
28
TEST(AllocatorTest,HasFlags)29 TEST(AllocatorTest, HasFlags) {
30 AllocatorForTest allocator;
31 EXPECT_TRUE(
32 allocator.HasCapability(Capability::kImplementsGetRequestedLayout));
33 EXPECT_TRUE(allocator.HasCapability(Capability::kImplementsGetUsableLayout));
34 }
35
TEST(AllocatorTest,ResizeNull)36 TEST(AllocatorTest, ResizeNull) {
37 AllocatorForTest allocator;
38 EXPECT_FALSE(allocator.Resize(nullptr, sizeof(uint32_t)));
39 }
40
TEST(AllocatorTest,ResizeZero)41 TEST(AllocatorTest, ResizeZero) {
42 AllocatorForTest allocator;
43 constexpr Layout layout = Layout::Of<uint32_t>();
44 void* ptr = allocator.Allocate(layout);
45 ASSERT_NE(ptr, nullptr);
46 EXPECT_FALSE(allocator.Resize(ptr, 0));
47 }
48
TEST(AllocatorTest,ResizeSame)49 TEST(AllocatorTest, ResizeSame) {
50 AllocatorForTest allocator;
51 constexpr Layout layout = Layout::Of<uint32_t>();
52 void* ptr = allocator.Allocate(layout);
53 ASSERT_NE(ptr, nullptr);
54 EXPECT_TRUE(allocator.Resize(ptr, layout.size()));
55 EXPECT_EQ(allocator.resize_ptr(), ptr);
56 EXPECT_EQ(allocator.resize_old_size(), layout.size());
57 EXPECT_EQ(allocator.resize_new_size(), layout.size());
58 }
59
TEST(AllocatorTest,ReallocateNull)60 TEST(AllocatorTest, ReallocateNull) {
61 AllocatorForTest allocator;
62 constexpr Layout old_layout = Layout::Of<uint32_t>();
63 size_t new_size = old_layout.size();
64 void* new_ptr = allocator.Reallocate(nullptr, new_size);
65
66 // Resize should fail and Reallocate should call Allocate.
67 EXPECT_EQ(allocator.allocate_size(), new_size);
68
69 // Deallocate should not be called.
70 EXPECT_EQ(allocator.deallocate_ptr(), nullptr);
71 EXPECT_EQ(allocator.deallocate_size(), 0U);
72
73 // Overall, Reallocate should succeed.
74 EXPECT_NE(new_ptr, nullptr);
75 }
76
TEST(AllocatorTest,ReallocateZeroNewSize)77 TEST(AllocatorTest, ReallocateZeroNewSize) {
78 AllocatorForTest allocator;
79 constexpr Layout old_layout = Layout::Of<uint32_t[3]>();
80 void* ptr = allocator.Allocate(old_layout);
81 ASSERT_EQ(allocator.allocate_size(), old_layout.size());
82 ASSERT_NE(ptr, nullptr);
83 allocator.ResetParameters();
84
85 size_t new_size = 0;
86 void* new_ptr = allocator.Reallocate(ptr, new_size);
87
88 // Reallocate does not call Resize, Allocate, or Deallocate.
89 EXPECT_EQ(allocator.resize_ptr(), nullptr);
90 EXPECT_EQ(allocator.resize_old_size(), 0U);
91 EXPECT_EQ(allocator.resize_new_size(), 0U);
92 EXPECT_EQ(allocator.allocate_size(), 0U);
93 EXPECT_EQ(allocator.deallocate_ptr(), nullptr);
94 EXPECT_EQ(allocator.deallocate_size(), 0U);
95
96 // Overall, Reallocate should fail.
97 EXPECT_EQ(new_ptr, nullptr);
98 }
99
TEST(AllocatorTest,ReallocateSame)100 TEST(AllocatorTest, ReallocateSame) {
101 AllocatorForTest allocator;
102 constexpr Layout layout = Layout::Of<uint32_t[3]>();
103 void* ptr = allocator.Allocate(layout);
104 ASSERT_EQ(allocator.allocate_size(), layout.size());
105 ASSERT_NE(ptr, nullptr);
106 allocator.ResetParameters();
107
108 void* new_ptr = allocator.Reallocate(ptr, layout);
109
110 // Reallocate should call Resize.
111 EXPECT_EQ(allocator.resize_ptr(), ptr);
112 EXPECT_EQ(allocator.resize_old_size(), layout.size());
113 EXPECT_EQ(allocator.resize_new_size(), layout.size());
114
115 // DoAllocate should not be called.
116 EXPECT_EQ(allocator.allocate_size(), 0U);
117
118 // DoDeallocate should not be called.
119 EXPECT_EQ(allocator.deallocate_ptr(), nullptr);
120 EXPECT_EQ(allocator.deallocate_size(), 0U);
121
122 // Overall, Reallocate should succeed.
123 EXPECT_EQ(new_ptr, ptr);
124 }
125
TEST(AllocatorTest,ReallocateSmaller)126 TEST(AllocatorTest, ReallocateSmaller) {
127 AllocatorForTest allocator;
128 constexpr Layout old_layout = Layout::Of<uint32_t[3]>();
129 void* ptr = allocator.Allocate(old_layout);
130 ASSERT_EQ(allocator.allocate_size(), old_layout.size());
131 ASSERT_NE(ptr, nullptr);
132 allocator.ResetParameters();
133
134 size_t new_size = sizeof(uint32_t);
135 void* new_ptr = allocator.Reallocate(ptr, new_size);
136
137 // Reallocate should call Resize.
138 EXPECT_EQ(allocator.resize_ptr(), ptr);
139 EXPECT_EQ(allocator.resize_old_size(), old_layout.size());
140 EXPECT_EQ(allocator.resize_new_size(), new_size);
141
142 // Allocate should not be called.
143 EXPECT_EQ(allocator.allocate_size(), 0U);
144
145 // Deallocate should not be called.
146 EXPECT_EQ(allocator.deallocate_ptr(), nullptr);
147 EXPECT_EQ(allocator.deallocate_size(), 0U);
148
149 // Overall, Reallocate should succeed.
150 EXPECT_EQ(new_ptr, ptr);
151 }
152
TEST(AllocatorTest,ReallocateLarger)153 TEST(AllocatorTest, ReallocateLarger) {
154 AllocatorForTest allocator;
155 constexpr Layout old_layout = Layout::Of<uint32_t>();
156 void* ptr = allocator.Allocate(old_layout);
157 ASSERT_EQ(allocator.allocate_size(), old_layout.size());
158 ASSERT_NE(ptr, nullptr);
159
160 // The abstraction is a bit leaky here: This tests relies on the details of
161 // `Resize` in order to get it to fail and fallback to
162 // allocate/copy/deallocate. Allocate a second block, which should prevent the
163 // first one from being able to grow.
164 void* next = allocator.Allocate(old_layout);
165 ASSERT_EQ(allocator.allocate_size(), old_layout.size());
166 ASSERT_NE(next, nullptr);
167 allocator.ResetParameters();
168
169 size_t new_size = sizeof(uint32_t[3]);
170 void* new_ptr = allocator.Reallocate(ptr, new_size);
171
172 // Reallocate should call Resize.
173 EXPECT_EQ(allocator.resize_ptr(), ptr);
174 EXPECT_EQ(allocator.resize_old_size(), old_layout.size());
175 EXPECT_EQ(allocator.resize_new_size(), new_size);
176
177 // Resize should fail and Reallocate should call Allocate.
178 EXPECT_EQ(allocator.allocate_size(), new_size);
179
180 // Deallocate should also be called.
181 EXPECT_EQ(allocator.deallocate_ptr(), ptr);
182 EXPECT_EQ(allocator.deallocate_size(), old_layout.size());
183
184 // Overall, Reallocate should succeed.
185 EXPECT_NE(new_ptr, nullptr);
186 EXPECT_NE(new_ptr, ptr);
187 }
188
189 // Test fixture for IsEqual tests.
190 class BaseAllocator : public pw::Allocator {
191 public:
BaseAllocator(void * ptr)192 BaseAllocator(void* ptr) : pw::Allocator(Capabilities()), ptr_(ptr) {}
193
194 private:
DoAllocate(Layout)195 void* DoAllocate(Layout) override {
196 void* ptr = ptr_;
197 ptr_ = nullptr;
198 return ptr;
199 }
200
DoDeallocate(void *)201 void DoDeallocate(void*) override {}
DoDeallocate(void *,Layout)202 void DoDeallocate(void*, Layout) override {}
203
204 void* ptr_;
205 };
206
207 // Test fixture for IsEqual tests.
208 class DerivedAllocator : public BaseAllocator {
209 public:
DerivedAllocator(size_t value,void * ptr)210 DerivedAllocator(size_t value, void* ptr)
211 : BaseAllocator(ptr), value_(value) {}
value() const212 size_t value() const { return value_; }
213
214 private:
215 size_t value_;
216 };
217
TEST(AllocatorTest,IsEqualFailsWithDifferentObjects)218 TEST(AllocatorTest, IsEqualFailsWithDifferentObjects) {
219 std::array<std::byte, 8> buffer;
220 DerivedAllocator derived1(1, buffer.data());
221 DerivedAllocator derived2(2, buffer.data());
222 EXPECT_FALSE(derived1.IsEqual(derived2));
223 EXPECT_FALSE(derived2.IsEqual(derived1));
224 }
225
TEST(AllocatorTest,IsEqualSucceedsWithSameObject)226 TEST(AllocatorTest, IsEqualSucceedsWithSameObject) {
227 std::array<std::byte, 8> buffer;
228 DerivedAllocator derived(1, buffer.data());
229 BaseAllocator* base = &derived;
230 EXPECT_TRUE(derived.IsEqual(*base));
231 EXPECT_TRUE(base->IsEqual(derived));
232 }
233
234 } // namespace
235