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