C/C++ API Reference
Loading...
Searching...
No Matches
testing.h
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#pragma once
15
16#include <cstddef>
17#include <mutex>
18
19#include "pw_allocator/allocator.h"
20#include "pw_allocator/first_fit.h"
21#include "pw_allocator/hardening.h"
22#include "pw_allocator/metrics.h"
23#include "pw_allocator/tracking_allocator.h"
24#include "pw_assert/assert.h"
25#include "pw_bytes/span.h"
26#include "pw_result/result.h"
27#include "pw_status/status.h"
28#include "pw_tokenizer/tokenize.h"
29#include "pw_unit_test/framework.h"
30
31namespace pw::allocator::test {
32
34
35static_assert(Hardening::kIncludesDebugChecks,
36 "Tests must use a config that enables strict validation");
37
38// A token that can be used in tests.
39inline constexpr pw::tokenizer::Token kToken = PW_TOKENIZE_STRING("test");
40
42template <typename BlockType>
43void FreeAll(typename BlockType::Range range) {
44 BlockType* block = *(range.begin());
45 if (block == nullptr) {
46 return;
47 }
48
49 // Rewind to the first block.
50 BlockType* prev = block->Prev();
51 while (prev != nullptr) {
52 block = prev;
53 prev = block->Prev();
54 }
55
56 // Free and merge blocks.
57 while (block != nullptr) {
58 if (!block->IsFree()) {
59 auto result = BlockType::Free(std::move(block));
60 block = result.block();
61 }
62 block = block->Next();
63 }
64}
65
67template <size_t kBufferSize,
68 typename BlockType_ = FirstFitBlock<uint32_t>,
69 typename MetricsType_ = internal::AllMetrics>
71 public:
72 using BlockType = BlockType_;
73 using MetricsType = MetricsType_;
75
76 // Since the unbderlying first-fit allocator uses an intrusive free list, all
77 // allocations will be at least this size.
78 static constexpr size_t kMinSize = BlockType::kAlignment;
79
81 : Allocator(AllocatorType::kCapabilities),
82 allocator_(buffer_),
83 tracker_(kToken, allocator_) {
85 }
86
87 ~AllocatorForTest() override { FreeAll<BlockType>(blocks()); }
88
89 typename BlockType::Range blocks() const { return allocator_.blocks(); }
90 typename BlockType::Range blocks() { return allocator_.blocks(); }
91
92 const metric::Group& metric_group() const { return tracker_.metric_group(); }
93 metric::Group& metric_group() { return tracker_.metric_group(); }
94
95 const MetricsType& metrics() const { return tracker_.metrics(); }
96
97 size_t allocate_size() const { return allocate_size_; }
98 void* deallocate_ptr() const { return deallocate_ptr_; }
99 size_t deallocate_size() const { return deallocate_size_; }
100 void* resize_ptr() const { return resize_ptr_; }
101 size_t resize_old_size() const { return resize_old_size_; }
102 size_t resize_new_size() const { return resize_new_size_; }
103
106 allocate_size_ = 0;
107 deallocate_ptr_ = nullptr;
108 deallocate_size_ = 0;
109 resize_ptr_ = nullptr;
110 resize_old_size_ = 0;
111 resize_new_size_ = 0;
112 }
113
115 void Exhaust() {
116 for (auto* block : allocator_.blocks()) {
117 if (block->IsFree()) {
118 auto result = BlockType::AllocLast(std::move(block),
119 Layout(block->InnerSize(), 1));
120 PW_ASSERT(result.status() == OkStatus());
121
122 using Prev = internal::GenericBlockResult::Prev;
123 PW_ASSERT(result.prev() == Prev::kUnchanged);
124
125 using Next = internal::GenericBlockResult::Next;
126 PW_ASSERT(result.next() == Next::kUnchanged);
127 }
128 }
129 }
130
133 return allocator_.MeasureFragmentation();
134 }
135
136 protected:
139
141 std::optional<allocator::Fragmentation> DoMeasureFragmentation()
142 const override {
143 return allocator_.MeasureFragmentation();
144 }
145
146 private:
148 void* DoAllocate(Layout layout) override {
149 allocate_size_ = layout.size();
150 void* ptr = tracker_.Allocate(layout);
151 return ptr;
152 }
153
155 void DoDeallocate(void* ptr) override {
156 Result<Layout> requested = GetRequestedLayout(ptr);
157 deallocate_ptr_ = ptr;
158 deallocate_size_ = requested.ok() ? requested->size() : 0;
159 tracker_.Deallocate(ptr);
160 }
161
163 bool DoResize(void* ptr, size_t new_size) override {
164 Result<Layout> requested = GetRequestedLayout(ptr);
165 resize_ptr_ = ptr;
166 resize_old_size_ = requested.ok() ? requested->size() : 0;
167 resize_new_size_ = new_size;
168 return tracker_.Resize(ptr, new_size);
169 }
170
172 size_t DoGetAllocated() const override { return tracker_.GetAllocated(); }
173
175 Result<Layout> DoGetInfo(InfoType info_type, const void* ptr) const override {
176 return GetInfo(tracker_, info_type, ptr);
177 }
178
179 alignas(BlockType::kAlignment) std::array<std::byte, kBufferSize> buffer_{};
180 AllocatorType allocator_;
182 size_t allocate_size_;
183 void* deallocate_ptr_;
184 size_t deallocate_size_;
185 void* resize_ptr_;
186 size_t resize_old_size_;
187 size_t resize_new_size_;
188};
189
191
192} // namespace pw::allocator::test
Definition: allocator.h:42
constexpr Allocator()=default
TODO(b/326509341): Remove when downstream consumers migrate.
Definition: result.h:145
constexpr bool ok() const
Definition: result.h:451
Fragmentation MeasureFragmentation() const
Returns fragmentation information for the block allocator's memory region.
Definition: block_allocator.h:478
Range blocks() const
Returns a Range of blocks tracking the memory of this allocator.
Definition: block_allocator.h:301
Definition: detailed_block.h:88
Definition: layout.h:58
Definition: tracking_allocator.h:55
An AllocatorForTest that is automatically initialized on construction.
Definition: testing.h:70
size_t DoGetAllocated() const override
Definition: testing.h:172
std::optional< allocator::Fragmentation > DoMeasureFragmentation() const override
Definition: testing.h:141
void ResetParameters()
Resets the recorded parameters to an initial state.
Definition: testing.h:105
void DoDeallocate(void *ptr) override
Definition: testing.h:155
void * DoAllocate(Layout layout) override
Definition: testing.h:148
void Exhaust()
Allocates all the memory from this object.
Definition: testing.h:115
Fragmentation MeasureFragmentation() const
Returns fragmentation information for the block allocator's memory region.
Definition: testing.h:132
bool DoResize(void *ptr, size_t new_size) override
Definition: testing.h:163
TrackingAllocator< MetricsType > & GetTracker()
Returns the underlying tracking allocator.
Definition: testing.h:138
Result< Layout > DoGetInfo(InfoType info_type, const void *ptr) const override
Definition: testing.h:175
void FreeAll(typename BlockType::Range range)
Free all the blocks reachable by the given block. Useful for test cleanup.
Definition: testing.h:43
constexpr Status OkStatus()
Definition: status.h:450
#define PW_TOKENIZE_STRING(...)
Definition: tokenize.h:67
Definition: fragmentation.h:46
Definition: metrics.h:173