1 // Copyright 2024 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/block_allocator_testing.h"
16
17 #include <cstdint>
18
19 #include "lib/stdcompat/bit.h"
20 #include "pw_allocator/layout.h"
21 #include "pw_assert/check.h"
22 #include "pw_bytes/alignment.h"
23 #include "pw_status/status.h"
24
25 namespace pw::allocator::test {
26
27 // Test fixtures.
28
SetUp()29 void BlockAllocatorTestBase::SetUp() { ptrs_.fill(nullptr); }
30
Store(size_t index,void * ptr)31 void BlockAllocatorTestBase::Store(size_t index, void* ptr) {
32 PW_CHECK_UINT_LT(index, kNumPtrs, "index is out of bounds");
33 PW_CHECK(ptr == nullptr || ptrs_[index] == nullptr,
34 "assigning pointer would clobber existing allocation");
35 ptrs_[index] = ptr;
36 }
37
Fetch(size_t index)38 void* BlockAllocatorTestBase::Fetch(size_t index) {
39 return index < kNumPtrs ? ptrs_[index] : nullptr;
40 }
41
Swap(size_t i,size_t j)42 void BlockAllocatorTestBase::Swap(size_t i, size_t j) {
43 std::swap(ptrs_[i], ptrs_[j]);
44 }
45
UseMemory(void * ptr,size_t size)46 void BlockAllocatorTestBase::UseMemory(void* ptr, size_t size) {
47 std::memset(ptr, 0x5a, size);
48 }
49
50 // Unit tests.
51
GetCapacity(size_t expected)52 void BlockAllocatorTestBase::GetCapacity(size_t expected) {
53 Allocator& allocator = GetGenericAllocator();
54 StatusWithSize capacity = allocator.GetCapacity();
55 EXPECT_EQ(capacity.status(), OkStatus());
56 EXPECT_EQ(capacity.size(), expected);
57 }
58
AllocateLarge()59 void BlockAllocatorTestBase::AllocateLarge() {
60 Allocator& allocator = GetGenericAllocator();
61 constexpr Layout layout = Layout::Of<std::byte[kLargeInnerSize]>();
62 Store(0, allocator.Allocate(layout));
63 ASSERT_NE(Fetch(0), nullptr);
64 ByteSpan bytes = GetBytes();
65 EXPECT_GE(Fetch(0), bytes.data());
66 EXPECT_LE(Fetch(0), bytes.data() + bytes.size());
67 UseMemory(Fetch(0), layout.size());
68 }
69
AllocateSmall()70 void BlockAllocatorTestBase::AllocateSmall() {
71 Allocator& allocator = GetGenericAllocator();
72 constexpr Layout layout = Layout::Of<std::byte[kSmallInnerSize]>();
73 Store(0, allocator.Allocate(layout));
74 ASSERT_NE(Fetch(0), nullptr);
75 ByteSpan bytes = GetBytes();
76 EXPECT_GE(Fetch(0), bytes.data());
77 EXPECT_LE(Fetch(0), bytes.data() + bytes.size());
78 UseMemory(Fetch(0), layout.size());
79 }
80
AllocateTooLarge()81 void BlockAllocatorTestBase::AllocateTooLarge() {
82 Allocator& allocator = GetGenericAllocator();
83 Store(0, allocator.Allocate(Layout::Of<std::byte[kCapacity * 2]>()));
84 EXPECT_EQ(Fetch(0), nullptr);
85 }
86
DeallocateNull()87 void BlockAllocatorTestBase::DeallocateNull() {
88 Allocator& allocator = GetGenericAllocator();
89 allocator.Deallocate(nullptr);
90 }
91
DeallocateShuffled()92 void BlockAllocatorTestBase::DeallocateShuffled() {
93 Allocator& allocator = GetGenericAllocator();
94 constexpr Layout layout = Layout::Of<std::byte[kSmallInnerSize]>();
95 for (size_t i = 0; i < kNumPtrs; ++i) {
96 Store(i, allocator.Allocate(layout));
97 if (Fetch(i) == nullptr) {
98 break;
99 }
100 }
101
102 // Mix up the order of allocations.
103 for (size_t i = 0; i < kNumPtrs; ++i) {
104 if (i % 2 == 0 && i + 1 < kNumPtrs) {
105 Swap(i, i + 1);
106 }
107 if (i % 3 == 0 && i + 2 < kNumPtrs) {
108 Swap(i, i + 2);
109 }
110 }
111
112 // Deallocate everything.
113 for (size_t i = 0; i < kNumPtrs; ++i) {
114 allocator.Deallocate(Fetch(i));
115 Store(i, nullptr);
116 }
117 }
118
ResizeNull()119 void BlockAllocatorTestBase::ResizeNull() {
120 Allocator& allocator = GetGenericAllocator();
121 size_t new_size = 1;
122 EXPECT_FALSE(allocator.Resize(nullptr, new_size));
123 }
124
ResizeLargeSame()125 void BlockAllocatorTestBase::ResizeLargeSame() {
126 Allocator& allocator = GetGenericAllocator({
127 {kLargeOuterSize, Preallocation::kUsed},
128 {Preallocation::kSizeRemaining, Preallocation::kUsed},
129 });
130 size_t new_size = kLargeInnerSize;
131 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
132 UseMemory(Fetch(0), kLargeInnerSize);
133 }
134
ResizeLargeSmaller()135 void BlockAllocatorTestBase::ResizeLargeSmaller() {
136 Allocator& allocator = GetGenericAllocator({
137 {kLargeOuterSize, Preallocation::kUsed},
138 {Preallocation::kSizeRemaining, Preallocation::kUsed},
139 });
140 size_t new_size = kSmallInnerSize;
141 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
142 UseMemory(Fetch(0), kSmallInnerSize);
143 }
144
ResizeLargeLarger()145 void BlockAllocatorTestBase::ResizeLargeLarger() {
146 Allocator& allocator = GetGenericAllocator({
147 {kLargeOuterSize, Preallocation::kUsed},
148 {kLargeOuterSize, Preallocation::kFree},
149 {Preallocation::kSizeRemaining, Preallocation::kUsed},
150 });
151 size_t new_size = kLargeInnerSize * 2;
152 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
153 UseMemory(Fetch(0), kLargeInnerSize * 2);
154 }
155
ResizeLargeLargerFailure()156 void BlockAllocatorTestBase::ResizeLargeLargerFailure() {
157 Allocator& allocator = GetGenericAllocator({
158 {kLargeOuterSize, Preallocation::kUsed},
159 {Preallocation::kSizeRemaining, Preallocation::kUsed},
160 });
161 // Memory after ptr is already allocated, so `Resize` should fail.
162 size_t new_size = kLargeInnerSize * 2;
163 EXPECT_FALSE(allocator.Resize(Fetch(0), new_size));
164 }
165
ResizeSmallSame()166 void BlockAllocatorTestBase::ResizeSmallSame() {
167 Allocator& allocator = GetGenericAllocator({
168 {kSmallOuterSize, Preallocation::kUsed},
169 {Preallocation::kSizeRemaining, Preallocation::kUsed},
170 });
171 size_t new_size = kSmallInnerSize;
172 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
173 UseMemory(Fetch(0), kSmallInnerSize);
174 }
175
ResizeSmallSmaller()176 void BlockAllocatorTestBase::ResizeSmallSmaller() {
177 Allocator& allocator = GetGenericAllocator({
178 {kSmallOuterSize, Preallocation::kUsed},
179 {Preallocation::kSizeRemaining, Preallocation::kUsed},
180 });
181 size_t new_size = kSmallInnerSize / 2;
182 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
183 UseMemory(Fetch(0), kSmallInnerSize / 2);
184 }
185
ResizeSmallLarger()186 void BlockAllocatorTestBase::ResizeSmallLarger() {
187 Allocator& allocator = GetGenericAllocator({
188 {kSmallOuterSize, Preallocation::kUsed},
189 {kSmallOuterSize, Preallocation::kFree},
190 {Preallocation::kSizeRemaining, Preallocation::kUsed},
191 });
192 size_t new_size = kSmallInnerSize * 2;
193 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
194 UseMemory(Fetch(0), kSmallInnerSize * 2);
195 }
196
ResizeSmallLargerFailure()197 void BlockAllocatorTestBase::ResizeSmallLargerFailure() {
198 Allocator& allocator = GetGenericAllocator({
199 {kSmallOuterSize, Preallocation::kUsed},
200 {Preallocation::kSizeRemaining, Preallocation::kUsed},
201 });
202 // Memory after ptr is already allocated, so `Resize` should fail.
203 size_t new_size = kSmallInnerSize * 2 + kDefaultBlockOverhead;
204 EXPECT_FALSE(allocator.Resize(Fetch(0), new_size));
205 }
206
207 } // namespace pw::allocator::test
208