blob: f2a3bced970638726e98503459755c153841776f [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.
#include <cstddef>
#include "pw_allocator/allocator.h"
#include "pw_allocator/testing.h"
#include "pw_unit_test/framework.h"
namespace pw::allocator {
namespace {
TEST(UniquePtr, DefaultInitializationIsNullptr) {
UniquePtr<int> empty;
EXPECT_EQ(empty.get(), nullptr);
}
TEST(UniquePtr, OperatorEqNullptrOnEmptyUniquePtrSucceeds) {
UniquePtr<int> empty;
EXPECT_TRUE(empty == nullptr);
EXPECT_FALSE(empty != nullptr);
}
TEST(UniquePtr, OperatorEqNullptrAfterMakeUniqueFails) {
test::AllocatorForTest<256> allocator;
UniquePtr<int> ptr = allocator.MakeUnique<int>(5);
EXPECT_TRUE(ptr != nullptr);
EXPECT_FALSE(ptr == nullptr);
}
TEST(UniquePtr, OperatorEqNullptrAfterMakeUniqueNullptrTypeFails) {
test::AllocatorForTest<256> allocator;
UniquePtr<std::nullptr_t> ptr = allocator.MakeUnique<std::nullptr_t>(nullptr);
EXPECT_TRUE(ptr != nullptr);
EXPECT_FALSE(ptr == nullptr);
EXPECT_TRUE(*ptr == nullptr);
EXPECT_FALSE(*ptr != nullptr);
}
TEST(UniquePtr, MakeUniqueForwardsConstructorArguments) {
class MoveOnly {
public:
MoveOnly(int value) : value_(value) {}
MoveOnly(MoveOnly&) = delete;
MoveOnly(MoveOnly&&) {}
int Value() const { return value_; }
private:
int value_;
};
class BuiltWithMoveOnly {
public:
BuiltWithMoveOnly() = delete;
BuiltWithMoveOnly(MoveOnly&& mo) : value_(mo.Value()) {}
int Value() const { return value_; }
private:
int value_;
};
test::AllocatorForTest<256> allocator;
MoveOnly mo(6);
UniquePtr<BuiltWithMoveOnly> ptr =
allocator.MakeUnique<BuiltWithMoveOnly>(std::move(mo));
ASSERT_NE(ptr, nullptr);
EXPECT_EQ(ptr->Value(), 6);
}
TEST(UniquePtr, MoveConstructsFromSubClassAndFreesTotalSize) {
struct Base {};
struct LargerSub : public Base {
std::array<std::byte, 128> mem;
};
test::AllocatorForTest<256> allocator;
UniquePtr<LargerSub> ptr = allocator.MakeUnique<LargerSub>();
ASSERT_NE(ptr, nullptr);
EXPECT_EQ(allocator.allocate_size(), 128ul);
UniquePtr<Base> base_ptr(std::move(ptr));
EXPECT_EQ(allocator.deallocate_size(), 0ul);
// The size that is deallocated here should be the size of the larger
// subclass, not the size of the smaller base class.
base_ptr.Reset();
EXPECT_EQ(allocator.deallocate_size(), 128ul);
}
TEST(UniquePtr, MoveAssignsFromSubClassAndFreesTotalSize) {
struct Base {};
struct LargerSub : public Base {
std::array<std::byte, 128> mem;
};
test::AllocatorForTest<256> allocator;
UniquePtr<LargerSub> ptr = allocator.MakeUnique<LargerSub>();
ASSERT_NE(ptr, nullptr);
EXPECT_EQ(allocator.allocate_size(), 128ul);
UniquePtr<Base> base_ptr = std::move(ptr);
EXPECT_EQ(allocator.deallocate_size(), 0ul);
// The size that is deallocated here should be the size of the larger
// subclass, not the size of the smaller base class.
base_ptr.Reset();
EXPECT_EQ(allocator.deallocate_size(), 128ul);
}
TEST(UniquePtr, MoveAssignsToExistingDeallocates) {
test::AllocatorForTest<256> allocator;
UniquePtr<size_t> size1 = allocator.MakeUnique<size_t>(1);
ASSERT_NE(size1, nullptr);
EXPECT_EQ(*size1, 1U);
UniquePtr<size_t> size2 = allocator.MakeUnique<size_t>(2);
ASSERT_NE(size1, nullptr);
EXPECT_EQ(*size2, 2U);
EXPECT_EQ(allocator.deallocate_size(), 0U);
size1 = std::move(size2);
EXPECT_EQ(allocator.deallocate_size(), sizeof(size_t));
EXPECT_EQ(*size1, 2U);
}
TEST(UniquePtr, DestructorDestroysAndFrees) {
int count = 0;
class DestructorCounter {
public:
DestructorCounter(int& count) : count_(&count) {}
~DestructorCounter() { (*count_)++; }
private:
int* count_;
};
test::AllocatorForTest<256> allocator;
UniquePtr<DestructorCounter> ptr =
allocator.MakeUnique<DestructorCounter>(count);
ASSERT_NE(ptr, nullptr);
EXPECT_EQ(count, 0);
EXPECT_EQ(allocator.deallocate_size(), 0ul);
ptr.Reset(); // Reset the UniquePtr, destroying its contents.
EXPECT_EQ(count, 1);
EXPECT_EQ(allocator.deallocate_size(), sizeof(DestructorCounter));
}
TEST(UniquePtr, CanRelease) {
struct Size final {
size_t value;
};
Size* size_ptr = nullptr;
test::AllocatorForTest<256> allocator;
{
UniquePtr<Size> ptr = allocator.MakeUnique<Size>(Size{.value = 1});
ASSERT_NE(ptr, nullptr);
EXPECT_EQ(ptr.deallocator(), &allocator);
size_ptr = ptr.Release();
// Allocator pointer parameter is optional. Re-releasing returns null.
EXPECT_EQ(ptr.Release(), nullptr);
}
// Deallocate should not be called, even though UniquePtr goes out of scope.
EXPECT_EQ(allocator.deallocate_size(), 0U);
allocator.Delete(size_ptr);
EXPECT_EQ(allocator.deallocate_size(), sizeof(Size));
}
} // namespace
} // namespace pw::allocator