blob: 50dfb954f2ec18d78de0ed7f55f95595ec43ed56 [file] [log] [blame]
// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <cstddef>
#include "pw_allocator/allocator.h"
#include "pw_allocator/block.h"
#include "pw_allocator/buffer.h"
#include "pw_allocator/first_fit_block_allocator.h"
#include "pw_allocator/metrics.h"
#include "pw_allocator/tracking_allocator.h"
#include "pw_bytes/span.h"
#include "pw_result/result.h"
#include "pw_status/status.h"
#include "pw_tokenizer/tokenize.h"
#include "pw_unit_test/framework.h"
namespace pw::allocator {
namespace test {
// A token that can be used in tests.
constexpr pw::tokenizer::Token kToken = PW_TOKENIZE_STRING("test");
/// An `AllocatorForTest` that is automatically initialized on construction.
template <size_t kBufferSize, typename MetricsType = internal::AllMetrics>
class AllocatorForTest : public Allocator {
public:
using AllocatorType = FirstFitBlockAllocator<uint32_t>;
using BlockType = AllocatorType::BlockType;
AllocatorForTest()
: Allocator(AllocatorType::kCapabilities), tracker_(kToken, *allocator_) {
ResetParameters();
EXPECT_EQ(allocator_->Init(allocator_.as_bytes()), OkStatus());
}
~AllocatorForTest() override {
for (auto* block : allocator_->blocks()) {
BlockType::Free(block);
}
allocator_->Reset();
}
const metric::Group& metric_group() const { return tracker_.metric_group(); }
metric::Group& metric_group() { return tracker_.metric_group(); }
const MetricsType& metrics() const { return tracker_.metrics(); }
size_t allocate_size() const { return allocate_size_; }
void* deallocate_ptr() const { return deallocate_ptr_; }
size_t deallocate_size() const { return deallocate_size_; }
void* resize_ptr() const { return resize_ptr_; }
size_t resize_old_size() const { return resize_old_size_; }
size_t resize_new_size() const { return resize_new_size_; }
/// Resets the recorded parameters to an initial state.
void ResetParameters() {
allocate_size_ = 0;
deallocate_ptr_ = nullptr;
deallocate_size_ = 0;
resize_ptr_ = nullptr;
resize_old_size_ = 0;
resize_new_size_ = 0;
}
/// Allocates all the memory from this object.
void Exhaust() {
for (auto* block : allocator_->blocks()) {
block->MarkUsed();
}
}
private:
/// @copydoc Allocator::Allocate
void* DoAllocate(Layout layout) override {
allocate_size_ = layout.size();
return tracker_.Allocate(layout);
}
/// @copydoc Allocator::Deallocate
void DoDeallocate(void* ptr) override {
Result<Layout> requested = GetRequestedLayout(tracker_, ptr);
deallocate_ptr_ = ptr;
deallocate_size_ = requested.ok() ? requested->size() : 0;
tracker_.Deallocate(ptr);
}
/// @copydoc Allocator::Deallocate
void DoDeallocate(void* ptr, Layout) override { DoDeallocate(ptr); }
/// @copydoc Allocator::Resize
bool DoResize(void* ptr, size_t new_size) override {
Result<Layout> requested = GetRequestedLayout(tracker_, ptr);
resize_ptr_ = ptr;
resize_old_size_ = requested.ok() ? requested->size() : 0;
resize_new_size_ = new_size;
return tracker_.Resize(ptr, new_size);
}
/// @copydoc Allocator::GetCapacity
StatusWithSize DoGetCapacity() const override {
return tracker_.GetCapacity();
}
/// @copydoc Allocator::GetRequestedLayout
Result<Layout> DoGetRequestedLayout(const void* ptr) const override {
return GetRequestedLayout(tracker_, ptr);
}
/// @copydoc Allocator::GetUsableLayout
Result<Layout> DoGetUsableLayout(const void* ptr) const override {
return GetUsableLayout(tracker_, ptr);
}
/// @copydoc Allocator::GetAllocatedLayout
Result<Layout> DoGetAllocatedLayout(const void* ptr) const override {
return GetAllocatedLayout(tracker_, ptr);
}
/// @copydoc Allocator::Query
Status DoQuery(const void* ptr) const override {
return Query(tracker_, ptr);
}
WithBuffer<AllocatorType, kBufferSize> allocator_;
TrackingAllocator<MetricsType> tracker_;
size_t allocate_size_;
void* deallocate_ptr_;
size_t deallocate_size_;
void* resize_ptr_;
size_t resize_old_size_;
size_t resize_new_size_;
};
} // namespace test
} // namespace pw::allocator