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/fallback_allocator.h"
16
17 #include "pw_allocator/block/basic.h"
18 #include "pw_allocator/testing.h"
19 #include "pw_status/status.h"
20 #include "pw_unit_test/framework.h"
21
22 namespace {
23
24 using ::pw::allocator::FallbackAllocator;
25 using ::pw::allocator::Layout;
26 using ::pw::allocator::test::AllocatorForTest;
27
28 // Test fixtures.
29
30 class FallbackAllocatorTest : public ::testing::Test {
31 protected:
32 constexpr static size_t kCapacity = 256;
33 static_assert(sizeof(uintptr_t) >= AllocatorForTest<kCapacity>::kMinSize);
34
FallbackAllocatorTest()35 FallbackAllocatorTest() : allocator_(primary_, secondary_) {}
36
37 AllocatorForTest<kCapacity> primary_;
38 AllocatorForTest<kCapacity> secondary_;
39 FallbackAllocator allocator_;
40 };
41
42 // Unit tests.
43
TEST_F(FallbackAllocatorTest,GetCapacity)44 TEST_F(FallbackAllocatorTest, GetCapacity) {
45 pw::StatusWithSize capacity = allocator_.GetCapacity();
46 EXPECT_EQ(capacity.status(), pw::OkStatus());
47 EXPECT_EQ(capacity.size(), kCapacity);
48 }
49
TEST_F(FallbackAllocatorTest,AllocateFromPrimary)50 TEST_F(FallbackAllocatorTest, AllocateFromPrimary) {
51 Layout layout = Layout::Of<uintptr_t>();
52 void* ptr = allocator_.Allocate(layout);
53 EXPECT_NE(ptr, nullptr);
54 EXPECT_EQ(primary_.allocate_size(), layout.size());
55 EXPECT_EQ(secondary_.allocate_size(), 0U);
56 }
57
TEST_F(FallbackAllocatorTest,AllocateFromSecondary)58 TEST_F(FallbackAllocatorTest, AllocateFromSecondary) {
59 primary_.Exhaust();
60 Layout layout = Layout::Of<uintptr_t>();
61 void* ptr = allocator_.Allocate(layout);
62 EXPECT_NE(ptr, nullptr);
63 EXPECT_EQ(primary_.allocate_size(), layout.size());
64 EXPECT_EQ(secondary_.allocate_size(), layout.size());
65 }
66
TEST_F(FallbackAllocatorTest,AllocateFailure)67 TEST_F(FallbackAllocatorTest, AllocateFailure) {
68 Layout layout = Layout::Of<uintptr_t[0x10000]>();
69 void* ptr = allocator_.Allocate(layout);
70 EXPECT_EQ(ptr, nullptr);
71 EXPECT_EQ(primary_.allocate_size(), layout.size());
72 EXPECT_EQ(secondary_.allocate_size(), layout.size());
73 }
74
TEST_F(FallbackAllocatorTest,DeallocateUsingPrimary)75 TEST_F(FallbackAllocatorTest, DeallocateUsingPrimary) {
76 Layout layout = Layout::Of<uintptr_t>();
77 void* ptr = allocator_.Allocate(layout);
78 ASSERT_NE(ptr, nullptr);
79 allocator_.Deallocate(ptr);
80 EXPECT_EQ(primary_.deallocate_ptr(), ptr);
81 EXPECT_EQ(primary_.deallocate_size(), layout.size());
82 EXPECT_EQ(secondary_.deallocate_ptr(), nullptr);
83 EXPECT_EQ(secondary_.deallocate_size(), 0U);
84 }
85
TEST_F(FallbackAllocatorTest,DeallocateUsingSecondary)86 TEST_F(FallbackAllocatorTest, DeallocateUsingSecondary) {
87 primary_.Exhaust();
88 Layout layout = Layout::Of<uintptr_t>();
89 void* ptr = allocator_.Allocate(layout);
90 ASSERT_NE(ptr, nullptr);
91 allocator_.Deallocate(ptr);
92 EXPECT_EQ(primary_.deallocate_ptr(), nullptr);
93 EXPECT_EQ(primary_.deallocate_size(), 0U);
94 EXPECT_EQ(secondary_.deallocate_ptr(), ptr);
95 EXPECT_EQ(secondary_.deallocate_size(), layout.size());
96 }
97
TEST_F(FallbackAllocatorTest,ResizePrimary)98 TEST_F(FallbackAllocatorTest, ResizePrimary) {
99 Layout old_layout = Layout::Of<uintptr_t>();
100 void* ptr = allocator_.Allocate(old_layout);
101 ASSERT_NE(ptr, nullptr);
102
103 size_t new_size = sizeof(uintptr_t[3]);
104 EXPECT_TRUE(allocator_.Resize(ptr, new_size));
105 EXPECT_EQ(primary_.resize_ptr(), ptr);
106 EXPECT_EQ(primary_.resize_old_size(), old_layout.size());
107 EXPECT_EQ(primary_.resize_new_size(), new_size);
108
109 // Secondary should not be touched.
110 EXPECT_EQ(secondary_.resize_ptr(), nullptr);
111 EXPECT_EQ(secondary_.resize_old_size(), 0U);
112 EXPECT_EQ(secondary_.resize_new_size(), 0U);
113 }
114
TEST_F(FallbackAllocatorTest,ResizePrimaryFailure)115 TEST_F(FallbackAllocatorTest, ResizePrimaryFailure) {
116 Layout old_layout = Layout::Of<uintptr_t>();
117 void* ptr = allocator_.Allocate(old_layout);
118 ASSERT_NE(ptr, nullptr);
119 primary_.Exhaust();
120
121 size_t new_size = sizeof(uintptr_t[3]);
122 EXPECT_FALSE(allocator_.Resize(ptr, new_size));
123 EXPECT_EQ(primary_.resize_ptr(), ptr);
124 EXPECT_EQ(primary_.resize_old_size(), old_layout.size());
125 EXPECT_EQ(primary_.resize_new_size(), new_size);
126
127 // Secondary should not be touched.
128 EXPECT_EQ(secondary_.resize_ptr(), nullptr);
129 EXPECT_EQ(secondary_.resize_old_size(), 0U);
130 EXPECT_EQ(secondary_.resize_new_size(), 0U);
131 }
132
TEST_F(FallbackAllocatorTest,ResizeSecondary)133 TEST_F(FallbackAllocatorTest, ResizeSecondary) {
134 primary_.Exhaust();
135 Layout old_layout = Layout::Of<uintptr_t>();
136 void* ptr = allocator_.Allocate(old_layout);
137 ASSERT_NE(ptr, nullptr);
138
139 size_t new_size = sizeof(uintptr_t[3]);
140 EXPECT_TRUE(allocator_.Resize(ptr, new_size));
141 EXPECT_EQ(secondary_.resize_ptr(), ptr);
142 EXPECT_EQ(secondary_.resize_old_size(), old_layout.size());
143 EXPECT_EQ(secondary_.resize_new_size(), new_size);
144
145 // Primary should not be touched.
146 EXPECT_EQ(primary_.resize_ptr(), nullptr);
147 EXPECT_EQ(primary_.resize_old_size(), 0U);
148 EXPECT_EQ(primary_.resize_new_size(), 0U);
149 }
150
TEST_F(FallbackAllocatorTest,ResizeSecondaryFailure)151 TEST_F(FallbackAllocatorTest, ResizeSecondaryFailure) {
152 primary_.Exhaust();
153 Layout old_layout = Layout::Of<uintptr_t>();
154 void* ptr = allocator_.Allocate(old_layout);
155 ASSERT_NE(ptr, nullptr);
156 secondary_.Exhaust();
157
158 size_t new_size = sizeof(uintptr_t[3]);
159 EXPECT_FALSE(allocator_.Resize(ptr, new_size));
160 EXPECT_EQ(secondary_.resize_ptr(), ptr);
161 EXPECT_EQ(secondary_.resize_old_size(), old_layout.size());
162 EXPECT_EQ(secondary_.resize_new_size(), new_size);
163
164 // Primary should not be touched.
165 EXPECT_EQ(primary_.resize_ptr(), nullptr);
166 EXPECT_EQ(primary_.resize_old_size(), 0U);
167 EXPECT_EQ(primary_.resize_new_size(), 0U);
168 }
169
TEST_F(FallbackAllocatorTest,ReallocateSameAllocator)170 TEST_F(FallbackAllocatorTest, ReallocateSameAllocator) {
171 Layout old_layout = Layout::Of<uintptr_t>();
172 void* ptr1 = allocator_.Allocate(old_layout);
173 ASSERT_NE(ptr1, nullptr);
174
175 // Claim subsequent memeory to force reallocation.
176 void* ptr2 = allocator_.Allocate(old_layout);
177 ASSERT_NE(ptr2, nullptr);
178
179 Layout new_layout = Layout::Of<uintptr_t[3]>();
180 void* new_ptr = allocator_.Reallocate(ptr1, new_layout);
181 EXPECT_NE(new_ptr, nullptr);
182 EXPECT_EQ(primary_.deallocate_ptr(), ptr1);
183 EXPECT_EQ(primary_.deallocate_size(), old_layout.size());
184 EXPECT_EQ(primary_.allocate_size(), new_layout.size());
185 }
186
TEST_F(FallbackAllocatorTest,ReallocateDifferentAllocator)187 TEST_F(FallbackAllocatorTest, ReallocateDifferentAllocator) {
188 Layout old_layout = Layout::Of<uintptr_t>();
189 void* ptr = allocator_.Allocate(old_layout);
190 primary_.Exhaust();
191
192 Layout new_layout = Layout::Of<uintptr_t[3]>();
193 void* new_ptr = allocator_.Reallocate(ptr, new_layout);
194 EXPECT_NE(new_ptr, nullptr);
195 EXPECT_EQ(primary_.deallocate_ptr(), ptr);
196 EXPECT_EQ(primary_.deallocate_size(), old_layout.size());
197 EXPECT_EQ(secondary_.allocate_size(), new_layout.size());
198 }
199
200 } // namespace
201