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