blob: ea98ede18f61e793086f9b493019e08c26311e68 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <fbl/string.h>
#include <fbl/tests/lfsr.h>
#include <fbl/unique_ptr.h>
#include <fbl/vector.h>
#include <unittest/unittest.h>
#include <utility>
namespace fbl::tests {
namespace {
// Different container classes for the types of objects
// which should be tested within a vector (raw types,
// unique pointers, ref pointers).
using ValueType = size_t;
struct TestObject {
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(TestObject);
TestObject() = delete;
explicit TestObject(ValueType val)
: alive_(true), val_(val) {
++live_obj_count_;
++ctor_count_;
}
TestObject(TestObject&& r)
: alive_(r.alive_), val_(r.val_) {
r.alive_ = false;
++ctor_count_;
}
TestObject& operator=(TestObject&& r) {
val_ = r.val_;
alive_ = r.alive_;
r.alive_ = false;
return *this;
}
~TestObject() {
if (alive_) {
--live_obj_count_;
}
++dtor_count_;
}
ValueType value() const { return val_; }
static size_t live_obj_count() { return live_obj_count_; }
static void ResetLiveObjCount() { live_obj_count_ = 0; }
bool alive_;
ValueType val_;
static size_t live_obj_count_;
static size_t ctor_count_;
static size_t dtor_count_;
};
size_t TestObject::live_obj_count_ = 0;
size_t TestObject::ctor_count_ = 0;
size_t TestObject::dtor_count_ = 0;
struct ValueTypeTraits {
using ItemType = ValueType;
static ItemType Create(ValueType val) { return val; }
static ValueType GetValue(const ItemType& c) { return c; }
// We have no way of managing the "live count" of raw types, so we don't.
static bool CheckLiveCount(size_t expected) { return true; }
static bool CheckCtorDtorCount() { return true; }
};
struct StructTypeTraits {
using ItemType = TestObject;
static ItemType Create(ValueType val) { return TestObject(val); }
static ValueType GetValue(const ItemType& c) { return c.value(); }
static bool CheckLiveCount(size_t expected) { return TestObject::live_obj_count() == expected; }
static bool CheckCtorDtorCount() { return TestObject::ctor_count_ == TestObject::dtor_count_; }
};
struct UniquePtrTraits {
using ItemType = fbl::unique_ptr<TestObject>;
static ItemType Create(ValueType val) {
AllocChecker ac;
ItemType ptr(new (&ac) TestObject(val));
ZX_ASSERT(ac.check());
return ptr;
}
static ValueType GetValue(const ItemType& c) { return c->value(); }
static bool CheckLiveCount(size_t expected) { return TestObject::live_obj_count() == expected; }
static bool CheckCtorDtorCount() { return TestObject::ctor_count_ == TestObject::dtor_count_; }
};
template <typename T>
struct RefCountedItem : public fbl::RefCounted<RefCountedItem<T>> {
RefCountedItem(T v)
: val(std::move(v)) {}
DISALLOW_COPY_ASSIGN_AND_MOVE(RefCountedItem);
T val;
};
struct RefPtrTraits {
using ItemType = fbl::RefPtr<RefCountedItem<TestObject>>;
static ItemType Create(ValueType val) {
AllocChecker ac;
auto ptr = AdoptRef(new (&ac) RefCountedItem<TestObject>(TestObject(val)));
ZX_ASSERT(ac.check());
return ptr;
}
static ValueType GetValue(const ItemType& c) { return c->val.value(); }
static bool CheckLiveCount(size_t expected) { return TestObject::live_obj_count() == expected; }
static bool CheckCtorDtorCount() { return TestObject::ctor_count_ == TestObject::dtor_count_; }
};
// Helper classes
template <typename ItemTraits>
struct Generator {
using ItemType = typename ItemTraits::ItemType;
constexpr static ValueType seed = 0xa2328b73e323fd0f;
ValueType NextValue() { return key_lfsr_.GetNext(); }
ItemType NextItem() { return ItemTraits::Create(NextValue()); }
void Reset() { key_lfsr_.SetCore(seed); }
Lfsr<ValueType> key_lfsr_ = Lfsr<ValueType>(seed);
};
struct TestAllocatorTraits : public DefaultAllocatorTraits {
static void* Allocate(size_t size) {
void* result = DefaultAllocatorTraits::Allocate(size);
// Intentionally fill the allocated portion of memory
// with non-zero data to break the assumption that
// the heap will return "clean" data. This helps
// catch bugs where the vector might call move assignment
// operators into portions of uninitialized memory.
if (result) {
memset(result, 'f', size);
}
return result;
}
};
// Actual tests
template <typename ItemTraits, size_t size>
bool VectorTestAccessRelease() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
Generator<ItemTraits> gen;
// Create the vector, verify its contents
{
fbl::Vector<ItemType, TestAllocatorTraits> vector;
fbl::AllocChecker ac;
vector.reserve(size, &ac);
ASSERT_TRUE(ac.check());
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
vector.push_back(gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
gen.Reset();
ItemType* data = vector.get();
for (size_t i = 0; i < size; i++) {
auto base = gen.NextValue();
// Verify the contents using the [] operator
ASSERT_EQ(ItemTraits::GetValue(vector[i]), base);
// Verify the contents using the underlying array
ASSERT_EQ(ItemTraits::GetValue(data[i]), base);
}
// Release the vector's underlying array before it is destructed
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
vector.reset();
ASSERT_EQ(vector.size(), 0);
ASSERT_EQ(vector.capacity(), 0);
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
struct CountedAllocatorTraits : public TestAllocatorTraits {
static void* Allocate(size_t size) {
allocation_count++;
return TestAllocatorTraits::Allocate(size);
}
static size_t allocation_count;
};
size_t CountedAllocatorTraits::allocation_count = 0;
template <typename ItemTraits, size_t size>
bool VectorTestPushBackInCapacity() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
CountedAllocatorTraits::allocation_count = 0;
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
{
fbl::Vector<ItemType, CountedAllocatorTraits> vector;
ASSERT_EQ(CountedAllocatorTraits::allocation_count, 0);
fbl::AllocChecker ac;
vector.reserve(size, &ac);
ASSERT_TRUE(ac.check());
ASSERT_EQ(CountedAllocatorTraits::allocation_count, 1);
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
vector.push_back(gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
}
ASSERT_EQ(CountedAllocatorTraits::allocation_count, 1);
gen.Reset();
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
template <typename ItemTraits, size_t size>
bool VectorTestPushBackByConstRefInCapacity() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
CountedAllocatorTraits::allocation_count = 0;
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
{
fbl::Vector<ItemType, CountedAllocatorTraits> vector;
ASSERT_EQ(CountedAllocatorTraits::allocation_count, 0);
fbl::AllocChecker ac;
vector.reserve(size, &ac);
ASSERT_TRUE(ac.check());
ASSERT_EQ(CountedAllocatorTraits::allocation_count, 1);
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
const ItemType item = gen.NextItem();
vector.push_back(item, &ac);
ASSERT_TRUE(ac.check());
}
ASSERT_EQ(CountedAllocatorTraits::allocation_count, 1);
gen.Reset();
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
template <typename ItemTraits, size_t size>
bool VectorTestPushBackBeyondCapacity() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
{
// Create an empty vector, push back beyond its capacity
fbl::Vector<ItemType, TestAllocatorTraits> vector;
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
fbl::AllocChecker ac;
vector.push_back(gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
}
gen.Reset();
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
template <typename ItemTraits, size_t size>
bool VectorTestPushBackByConstRefBeyondCapacity() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
{
// Create an empty vector, push back beyond its capacity
fbl::Vector<ItemType, TestAllocatorTraits> vector;
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
const ItemType item = gen.NextItem();
fbl::AllocChecker ac;
vector.push_back(item, &ac);
ASSERT_TRUE(ac.check());
}
gen.Reset();
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
template <typename ItemTraits, size_t size>
bool VectorTestPopBack() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
{
// Create a vector filled with objects
fbl::Vector<ItemType, TestAllocatorTraits> vector;
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
fbl::AllocChecker ac;
vector.push_back(gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
}
gen.Reset();
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
// Pop one at a time, and check the vector is still valid
while (vector.size()) {
vector.pop_back();
ASSERT_TRUE(ItemTraits::CheckLiveCount(vector.size()));
gen.Reset();
for (size_t i = 0; i < vector.size(); i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
struct FailingAllocatorTraits {
static void* Allocate(size_t size) { return nullptr; }
static void Deallocate(void* object) { return; }
};
template <typename ItemType, size_t S>
struct PartiallyFailingAllocatorTraits : public DefaultAllocatorTraits {
static void* Allocate(size_t size) {
if (size <= sizeof(ItemType) * S) {
return DefaultAllocatorTraits::Allocate(size);
}
return nullptr;
}
};
template <typename ItemTraits, size_t size>
bool VectorTestAllocationFailure() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
// Test that a failing allocator cannot take on additional elements
{
fbl::Vector<ItemType, FailingAllocatorTraits> vector;
fbl::AllocChecker ac;
vector.reserve(0, &ac);
ASSERT_TRUE(ac.check());
vector.reserve(1, &ac);
ASSERT_FALSE(ac.check());
vector.reserve(size, &ac);
ASSERT_FALSE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
vector.push_back(gen.NextItem(), &ac);
ASSERT_FALSE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
// Test that a partially failing allocator stops taking on additional
// elements
{
fbl::Vector<ItemType, PartiallyFailingAllocatorTraits<ItemType, size>> vector;
fbl::AllocChecker ac;
vector.reserve(0, &ac);
ASSERT_TRUE(ac.check());
vector.reserve(1, &ac);
ASSERT_TRUE(ac.check());
vector.reserve(size, &ac);
ASSERT_TRUE(ac.check());
ASSERT_EQ(vector.capacity(), size);
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
gen.Reset();
while (vector.size() < size) {
vector.push_back(gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(vector.size()));
}
vector.push_back(gen.NextItem(), &ac);
ASSERT_FALSE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
ASSERT_EQ(vector.size(), size);
ASSERT_EQ(vector.capacity(), size);
// All the elements we were able to push back should still be present
gen.Reset();
for (size_t i = 0; i < vector.size(); i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
template <typename ItemTraits, size_t size>
bool VectorTestMove() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
// Test move constructor
{
fbl::Vector<ItemType, TestAllocatorTraits> vectorA;
ASSERT_TRUE(vectorA.is_empty());
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
fbl::AllocChecker ac;
vectorA.push_back(gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
}
gen.Reset();
ASSERT_FALSE(vectorA.is_empty());
ASSERT_EQ(vectorA.size(), size);
fbl::Vector<ItemType, TestAllocatorTraits> vectorB(std::move(vectorA));
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
ASSERT_TRUE(vectorA.is_empty());
ASSERT_EQ(vectorA.size(), 0);
ASSERT_EQ(vectorB.size(), size);
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vectorB[i]), gen.NextValue());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
// Test move assignment operator
{
gen.Reset();
fbl::Vector<ItemType, TestAllocatorTraits> vectorA;
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
fbl::AllocChecker ac;
vectorA.push_back(gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
}
gen.Reset();
ASSERT_EQ(vectorA.size(), size);
fbl::Vector<ItemType, TestAllocatorTraits> vectorB;
vectorB = std::move(vectorA);
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
ASSERT_EQ(vectorA.size(), 0);
ASSERT_EQ(vectorB.size(), size);
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vectorB[i]), gen.NextValue());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
template <typename ItemTraits, size_t size>
bool VectorTestSwap() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
{
fbl::Vector<ItemType, TestAllocatorTraits> vectorA;
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
fbl::AllocChecker ac;
vectorA.push_back(gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
}
fbl::Vector<ItemType, TestAllocatorTraits> vectorB;
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + i));
fbl::AllocChecker ac;
vectorB.push_back(gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
}
gen.Reset();
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vectorA[i]), gen.NextValue());
}
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vectorB[i]), gen.NextValue());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size * 2));
vectorA.swap(vectorB);
ASSERT_TRUE(ItemTraits::CheckLiveCount(size * 2));
gen.Reset();
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vectorB[i]), gen.NextValue());
}
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vectorA[i]), gen.NextValue());
}
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
template <typename ItemTraits, size_t size>
bool VectorTestIterator() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
{
fbl::Vector<ItemType, TestAllocatorTraits> vector;
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
fbl::AllocChecker ac;
vector.push_back(gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
}
gen.Reset();
for (auto& e : vector) {
auto base = gen.NextValue();
ASSERT_EQ(ItemTraits::GetValue(e), base);
// Take the element out, and put it back... just to check
// that we can.
auto other = std::move(e);
e = std::move(other);
ASSERT_EQ(ItemTraits::GetValue(e), base);
}
gen.Reset();
const auto* cvector = &vector;
for (const auto& e : *cvector) {
ASSERT_EQ(ItemTraits::GetValue(e), gen.NextValue());
}
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
template <typename ItemTraits, size_t size>
bool VectorTestInsertDelete() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
{
fbl::Vector<ItemType, TestAllocatorTraits> vector;
fbl::AllocChecker ac;
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
vector.insert(i, gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
}
// Insert at position zero and one
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
vector.insert(0, gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 1));
vector.insert(1, gen.NextItem(), &ac);
ASSERT_TRUE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 2));
gen.Reset();
// Verify the contents
for (size_t i = 2; i < size + 2; i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
ASSERT_EQ(ItemTraits::GetValue(vector[0]), gen.NextValue());
ASSERT_EQ(ItemTraits::GetValue(vector[1]), gen.NextValue());
gen.Reset();
{
// Erase from position one
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 2));
auto erasedval1 = vector.erase(1);
// Erase from position zero
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 2));
auto erasedval0 = vector.erase(0);
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 2));
// Verify the remaining contents
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
ASSERT_EQ(ItemTraits::GetValue(erasedval0), gen.NextValue());
ASSERT_EQ(ItemTraits::GetValue(erasedval1), gen.NextValue());
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 2));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
gen.Reset();
// Erase the remainder of the vector
for (size_t i = 0; i < size; i++) {
vector.erase(0);
}
ASSERT_EQ(vector.size(), 0);
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
template <typename ItemTraits, size_t size>
bool VectorTestNoAllocCheck() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
{
Generator<ItemTraits> gen;
fbl::Vector<ItemType, TestAllocatorTraits> vector;
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
vector.push_back(gen.NextItem());
}
gen.Reset();
for (auto& e : vector) {
auto base = gen.NextValue();
ASSERT_EQ(ItemTraits::GetValue(e), base);
}
}
{
Generator<ItemTraits> gen;
fbl::Vector<ItemType, TestAllocatorTraits> vector;
vector.reserve(size);
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
vector.push_back(gen.NextItem());
}
gen.Reset();
for (auto& e : vector) {
auto base = gen.NextValue();
ASSERT_EQ(ItemTraits::GetValue(e), base);
}
}
{
Generator<ItemTraits> gen;
fbl::Vector<ItemType, TestAllocatorTraits> vector;
for (size_t i = 0; i < size; i++) {
ASSERT_TRUE(ItemTraits::CheckLiveCount(i));
vector.insert(i, gen.NextItem());
}
gen.Reset();
for (auto& e : vector) {
auto base = gen.NextValue();
ASSERT_EQ(ItemTraits::GetValue(e), base);
}
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
template <typename ItemTraits>
bool VectorTestInitializerList() {
using ItemType = typename ItemTraits::ItemType;
BEGIN_TEST;
Generator<ItemTraits> gen;
CountedAllocatorTraits::allocation_count = 0;
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
// empty
{
fbl::Vector<ItemType, CountedAllocatorTraits> vector{};
ASSERT_EQ(CountedAllocatorTraits::allocation_count, 0);
ASSERT_EQ(0, vector.size());
ASSERT_EQ(0, vector.capacity());
}
// 5 items
{
fbl::Vector<ItemType, CountedAllocatorTraits> vector{
gen.NextItem(), gen.NextItem(), gen.NextItem(),
gen.NextItem(), gen.NextItem()};
ASSERT_EQ(CountedAllocatorTraits::allocation_count, 1);
ASSERT_EQ(5, vector.size());
ASSERT_EQ(5, vector.capacity());
gen.Reset();
for (size_t i = 0; i < 5; i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(5));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
END_TEST;
}
bool VectorTestImplicitConversion() {
BEGIN_TEST;
{
fbl::Vector<fbl::String> v;
v.push_back(fbl::String("First"));
v.push_back("Second");
v.insert(2, fbl::String("Third"));
v.insert(3, "Fourth");
ASSERT_EQ(strcmp(v[0].c_str(), "First"), 0);
ASSERT_EQ(strcmp(v[1].c_str(), "Second"), 0);
ASSERT_EQ(strcmp(v[2].c_str(), "Third"), 0);
ASSERT_EQ(strcmp(v[3].c_str(), "Fourth"), 0);
}
{
fbl::Vector<fbl::String> v;
fbl::AllocChecker ac;
v.push_back(fbl::String("First"), &ac);
ASSERT_TRUE(ac.check());
v.push_back("Second", &ac);
ASSERT_TRUE(ac.check());
v.insert(2, fbl::String("Third"), &ac);
ASSERT_TRUE(ac.check());
v.insert(3, "Fourth", &ac);
ASSERT_TRUE(ac.check());
ASSERT_EQ(strcmp(v[0].c_str(), "First"), 0);
ASSERT_EQ(strcmp(v[1].c_str(), "Second"), 0);
ASSERT_EQ(strcmp(v[2].c_str(), "Third"), 0);
ASSERT_EQ(strcmp(v[3].c_str(), "Fourth"), 0);
}
END_TEST;
}
bool VectorGetConstness() {
BEGIN_TEST;
fbl::Vector<int> vector_int;
auto& ref_vector_int = vector_int;
const auto& const_ref_vector_int = vector_int;
auto __UNUSED int_ptr = ref_vector_int.get();
auto __UNUSED const_int_ptr = const_ref_vector_int.get();
static_assert(!std::is_const_v<std::remove_pointer_t<decltype(int_ptr)>>);
static_assert(std::is_const_v<std::remove_pointer_t<decltype(const_int_ptr)>>);
END_TEST;
}
} // namespace
#define RUN_FOR_ALL_TRAITS(test_base, test_size) \
RUN_TEST((test_base<ValueTypeTraits, test_size>)) \
RUN_TEST((test_base<StructTypeTraits, test_size>)) \
RUN_TEST((test_base<UniquePtrTraits, test_size>)) \
RUN_TEST((test_base<RefPtrTraits, test_size>))
#define RUN_FOR_ALL(test_base) \
RUN_FOR_ALL_TRAITS(test_base, 1) \
RUN_FOR_ALL_TRAITS(test_base, 2) \
RUN_FOR_ALL_TRAITS(test_base, 10) \
RUN_FOR_ALL_TRAITS(test_base, 32) \
RUN_FOR_ALL_TRAITS(test_base, 64) \
RUN_FOR_ALL_TRAITS(test_base, 100)
BEGIN_TEST_CASE(vector_tests)
RUN_FOR_ALL(VectorTestAccessRelease)
RUN_FOR_ALL(VectorTestPushBackInCapacity)
RUN_TEST((VectorTestPushBackByConstRefInCapacity<ValueTypeTraits, 100>))
RUN_FOR_ALL(VectorTestPushBackBeyondCapacity)
RUN_TEST((VectorTestPushBackByConstRefBeyondCapacity<ValueTypeTraits, 100>))
RUN_FOR_ALL(VectorTestPopBack)
RUN_FOR_ALL(VectorTestAllocationFailure)
RUN_FOR_ALL(VectorTestMove)
RUN_FOR_ALL(VectorTestSwap)
RUN_FOR_ALL(VectorTestIterator)
RUN_FOR_ALL(VectorTestInsertDelete)
RUN_FOR_ALL(VectorTestNoAllocCheck)
RUN_TEST(VectorTestInitializerList<ValueTypeTraits>)
RUN_TEST(VectorTestInitializerList<RefPtrTraits>)
RUN_TEST(VectorTestImplicitConversion)
RUN_TEST(VectorGetConstness)
END_TEST_CASE(vector_tests)
} // namespace fbl::tests