blob: a14b0e9f416067625b42cb9654a163deb10dfc2f [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 <lib/stdcompat/span.h>
#include <memory>
#include <utility>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <fbl/string.h>
#include <fbl/tests/lfsr.h>
#include <fbl/vector.h>
#include <zxtest/zxtest.h>
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 = std::unique_ptr<TestObject>;
static ItemType Create(ValueType val) {
fbl::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) {
fbl::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); }
fbl::tests::Lfsr<ValueType> key_lfsr_{seed};
};
struct TestAllocatorTraits : public fbl::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>
void AccessRelease(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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.data();
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());
}
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>
void PushBackInCapacity(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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());
}
template <typename ItemTraits>
void PushBackByConstRefInCapacity(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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());
}
template <typename ItemTraits>
void PushBackBeyondCapacity(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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());
}
template <typename ItemTraits>
void PushBackByConstRefBeyondCapacity(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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());
}
template <typename ItemTraits>
void PopBack(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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());
}
struct FailingAllocatorTraits {
static void* Allocate(size_t size) { return nullptr; }
static void Deallocate(void* object) { return; }
};
// Allocator which will fail allocations over a given size "S".
//
// Thread-unsafe: unprotected static variables in use.
class PartiallyFailingAllocatorTraits : public fbl::DefaultAllocatorTraits {
public:
static void* Allocate(size_t size) {
if (size <= failure_threshold_) {
return DefaultAllocatorTraits::Allocate(size);
}
return nullptr;
}
// Fail all allocations exceeding size "s".
static void SetFailureThreshold(size_t s) { failure_threshold_ = s; }
private:
static size_t failure_threshold_;
};
size_t PartiallyFailingAllocatorTraits::failure_threshold_;
template <typename ItemTraits>
void AllocationFailure(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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
{
PartiallyFailingAllocatorTraits::SetFailureThreshold(size * sizeof(ItemType));
fbl::Vector<ItemType, PartiallyFailingAllocatorTraits> 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());
}
template <typename ItemTraits>
void Move(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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());
}
template <typename ItemTraits>
void Swap(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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());
}
template <typename ItemTraits>
void Iterator(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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());
}
template <typename ItemTraits>
void Resize(size_t size) {
using ItemType = typename ItemTraits::ItemType;
Generator<ItemTraits> gen;
CountedAllocatorTraits::allocation_count = 0;
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
{
fbl::AllocChecker ac;
fbl::Vector<ItemType, CountedAllocatorTraits> vector;
ASSERT_EQ(CountedAllocatorTraits::allocation_count, 0);
vector.resize(size, &ac);
ASSERT_TRUE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
ASSERT_EQ(CountedAllocatorTraits::allocation_count, 1);
ASSERT_EQ(vector.size(), size);
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(vector[i], ItemType());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
for (size_t i = 0; i < size; i++) {
const ItemType item = gen.NextItem();
vector[i] = item;
ASSERT_TRUE(ItemTraits::CheckLiveCount(size));
}
{
const ItemType item = gen.NextItem();
vector.resize(size + 10, item, &ac);
}
ASSERT_TRUE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 10));
ASSERT_EQ(vector.size(), size + 10);
vector.resize(size + 1, &ac);
ASSERT_TRUE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 1));
ASSERT_EQ(vector.size(), size + 1);
{
const ItemType item = gen.NextItem();
vector.resize(size + 40, item, &ac);
}
ASSERT_TRUE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 40));
ASSERT_EQ(vector.size(), size + 40);
vector.resize(size + 2, &ac);
ASSERT_TRUE(ac.check());
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 2));
ASSERT_EQ(vector.size(), size + 2);
gen.Reset();
for (size_t i = 0; i < size + 2; i++) {
ASSERT_EQ(ItemTraits::GetValue(vector[i]), gen.NextValue());
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(size + 2));
}
ASSERT_TRUE(ItemTraits::CheckLiveCount(0));
ASSERT_TRUE(ItemTraits::CheckCtorDtorCount());
}
template <typename ItemTraits>
void InsertDelete(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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());
}
template <typename ItemTraits>
void NoAllocCheck(size_t size) {
using ItemType = typename ItemTraits::ItemType;
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());
}
template <typename ItemTraits>
void InitializerList() {
using ItemType = typename ItemTraits::ItemType;
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());
}
TEST(VectorTest, ImplicitConversion) {
{
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);
}
}
TEST(VectorTest, VectorDataConstness) {
fbl::Vector<int> vector_int;
auto& ref_vector_int = vector_int;
const auto& const_ref_vector_int = vector_int;
auto int_ptr = ref_vector_int.data();
auto const_int_ptr = const_ref_vector_int.data();
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)>>);
}
// The set of sizes we run each vector test with.
const size_t kTestSizes[] = {1, 2, 10, 32, 64, 100};
#define RUN_TRAIT_TEST(test_base, test_trait) \
TEST(VectorTest, test_base##_##test_trait) { \
auto fn = test_base<test_trait>; \
for (size_t size : kTestSizes) { \
ASSERT_NO_FAILURES(fn(size)); \
} \
}
#define RUN_FOR_ALL_TRAITS(test_base) \
RUN_TRAIT_TEST(test_base, ValueTypeTraits) \
RUN_TRAIT_TEST(test_base, StructTypeTraits) \
RUN_TRAIT_TEST(test_base, UniquePtrTraits) \
RUN_TRAIT_TEST(test_base, RefPtrTraits)
#define RUN_FOR_ALL(test_base) RUN_FOR_ALL_TRAITS(test_base)
RUN_FOR_ALL(AccessRelease)
RUN_FOR_ALL(PushBackInCapacity)
RUN_TRAIT_TEST(PushBackByConstRefInCapacity, ValueTypeTraits)
RUN_FOR_ALL(PushBackBeyondCapacity)
RUN_TRAIT_TEST(PushBackByConstRefBeyondCapacity, ValueTypeTraits)
RUN_FOR_ALL(PopBack)
RUN_FOR_ALL(AllocationFailure)
RUN_FOR_ALL(Move)
RUN_FOR_ALL(Swap)
RUN_FOR_ALL(Iterator)
RUN_FOR_ALL(InsertDelete)
RUN_TRAIT_TEST(Resize, ValueTypeTraits)
RUN_FOR_ALL(NoAllocCheck)
TEST(VectorTest, InitializerListValueType) {
ASSERT_NO_FAILURES(InitializerList<ValueTypeTraits>());
}
TEST(VectorTest, InitializerListRefPtr) { ASSERT_NO_FAILURES(InitializerList<RefPtrTraits>()); }
TEST(VectorTest, ImplicitlyConvertibleToSpan) {
fbl::Vector<int> vector_int = {1, 2, 3, 4, 5};
cpp20::span<int> vector_span(vector_int);
EXPECT_EQ(vector_int.size(), vector_span.size());
EXPECT_EQ(vector_int.data(), vector_span.data());
}
TEST(VectorTest, ImplicitlyConvertibleToConstSpan) {
const fbl::Vector<int> vector_int = {1, 2, 3, 4, 5};
cpp20::span<const int> vector_span(vector_int);
EXPECT_EQ(vector_int.size(), vector_span.size());
EXPECT_EQ(vector_int.data(), vector_span.data());
}
} // namespace