compute-runtime/shared/test/unit_test/utilities/containers_tests.cpp

1925 lines
58 KiB
C++

/*
* Copyright (C) 2018-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "shared/source/utilities/arrayref.h"
#include "shared/source/utilities/idlist.h"
#include "shared/source/utilities/iflist.h"
#include "shared/source/utilities/range.h"
#include "shared/source/utilities/stackvec.h"
#include "shared/test/unit_test/utilities/containers_tests_helpers.h"
#include "gtest/gtest.h"
#include <cinttypes>
#include <memory>
#include <numeric>
#include <type_traits>
#include <vector>
using namespace NEO;
struct DummyFNode : IFNode<DummyFNode> {
DummyFNode(uint32_t *destructorsCounter = nullptr)
: destructorsCounter(destructorsCounter) {
}
~DummyFNode() {
if (destructorsCounter != nullptr) {
++(*destructorsCounter);
}
}
void setPrev(DummyFNode *) {
}
uint32_t *destructorsCounter;
};
struct DummyDNode : IDNode<DummyDNode> {
DummyDNode(uint32_t *destructorsCounter = nullptr)
: destructorsCounter(destructorsCounter) {
}
~DummyDNode() override {
if (destructorsCounter != nullptr) {
++(*destructorsCounter);
}
}
void setPrev(DummyDNode *nd) {
prev = nd;
}
uint32_t *destructorsCounter;
};
template <typename NodeType = DummyFNode, size_t ArraySize>
void makeList(NodeType *(&nodes)[ArraySize], uint32_t *destructorsCounter = nullptr) {
NodeType *prev = nullptr;
for (NodeType *&nd : nodes) {
nd = new NodeType(destructorsCounter);
if (prev != nullptr) {
prev->next = nd;
nd->setPrev(prev);
}
prev = nd;
}
}
TEST(IFNode, WhenInsertingOneThenNodeIsAppendedAtEnd) {
DummyFNode head;
ASSERT_EQ(nullptr, head.next);
DummyFNode successor;
successor.next = &head; // garbage value
head.insertOneNext(successor);
ASSERT_EQ(&successor, head.next);
ASSERT_EQ(nullptr, successor.next);
DummyFNode successor2;
successor2.next = &head; // garbage value
head.insertOneNext(successor2);
ASSERT_EQ(&successor2, head.next);
ASSERT_EQ(&successor, successor2.next);
ASSERT_EQ(nullptr, successor.next);
}
TEST(IFNode, WhenInsertingAllThenAllNodesAreInserted) {
DummyFNode *listA[5];
DummyFNode *listB[7];
makeList(listA);
makeList(listB);
listA[2]->insertAllNext(*listB[0]);
ASSERT_EQ(listB[0], listA[2]->next);
ASSERT_EQ(listA[3], listB[sizeof(listB) / sizeof(listB[0]) - 1]->next);
DummyFNode *curr = listA[0];
int nodesCount = 0;
while (curr) {
auto next = curr->next;
delete curr;
++nodesCount;
curr = next;
}
int totalNodes = sizeof(listB) / sizeof(listB[0]) + sizeof(listA) / sizeof(listA[0]);
ASSERT_EQ(totalNodes, nodesCount);
}
TEST(IFNode, GivenFirstElementWhenDeletingThisAndAllNextThenAllElementsAreDeleted) {
DummyFNode *list[7];
uint32_t destructorCounter = 0;
makeList(list, &destructorCounter);
list[0]->deleteThisAndAllNext();
ASSERT_EQ(sizeof(list) / sizeof(list[0]), destructorCounter);
}
TEST(IFNode, WhenGettingTailThenLastElementIsReturned) {
DummyFNode *list[7];
makeList(list, nullptr);
auto tail = list[0]->getTail();
list[0]->deleteThisAndAllNext();
ASSERT_EQ(list[sizeof(list) / sizeof(list[0]) - 1], tail);
}
TEST(IFNode, WhenSlicingThenListIsSplitCorrectly) {
DummyFNode *list[7];
uint32_t destructorCounter = 0;
makeList(list, &destructorCounter);
auto listB = list[3]->slice();
ASSERT_EQ(nullptr, list[3]->next);
ASSERT_EQ(list[4], listB);
list[0]->deleteThisAndAllNext();
listB->deleteThisAndAllNext();
ASSERT_EQ(sizeof(list) / sizeof(list[0]), destructorCounter);
}
TEST(IFNode, WhenCountingSuccessorsThenCorrectValueIsReturned) {
DummyFNode *nodes[7];
makeList(nodes, 0);
ASSERT_EQ(sizeof(nodes) / sizeof(nodes[0]), 1 + nodes[0]->countSuccessors());
for (DummyFNode *n : nodes) {
delete n;
}
}
TEST(IFNode, WhenVerifingSequenceThenCorrectValueIsReturned) {
DummyFNode *nodes[4];
DummyFNode additional;
makeList(nodes, 0);
// valid list
ASSERT_EQ(-1, verifyFListOrder(nodes[0], nodes[0], nodes[1], nodes[2], nodes[3]));
// last->next != nullptr
nodes[3]->next = &additional;
ASSERT_EQ(3, verifyFListOrder(nodes[0], nodes[0], nodes[1], nodes[2], nodes[3]));
// valid list
nodes[2]->next = nullptr;
ASSERT_EQ(-1, verifyFListOrder(nodes[0], nodes[0], nodes[1], nodes[2]));
// list too short
ASSERT_EQ(2, verifyFListOrder(nodes[0], nodes[0], nodes[1], nodes[2], nodes[3]));
// wrong order (first)
ASSERT_EQ(0, verifyFListOrder(nodes[0], nodes[1], nodes[0], nodes[2]));
// wrong order (second)
ASSERT_EQ(1, verifyFListOrder(nodes[0], nodes[0], nodes[2], nodes[1]));
// wrong order (last)
ASSERT_EQ(2, verifyFListOrder(nodes[0], nodes[0], nodes[1], nodes[3]));
for (DummyFNode *n : nodes) {
delete n;
}
}
template <bool ThreadSafe>
void iFListTestPushFrontOne() {
IFList<DummyFNode, ThreadSafe, false> list;
ASSERT_TRUE(list.peekIsEmpty());
DummyFNode node1;
node1.next = &node1; // garbage
list.pushFrontOne(node1);
ASSERT_FALSE(list.peekIsEmpty());
ASSERT_EQ(&node1, list.peekHead());
ASSERT_EQ(nullptr, node1.next);
DummyFNode node2;
node2.next = &node1; // garbage
list.pushFrontOne(node2);
ASSERT_FALSE(list.peekIsEmpty());
ASSERT_EQ(&node2, list.peekHead());
ASSERT_EQ(&node1, node2.next);
}
TEST(IFList, GivenThreadSafeWhenPushingFrontOneThenResultIsCorrect) {
iFListTestPushFrontOne<true>();
}
TEST(IFList, GivenNonThreadSafeWhenPushingFrontOneThenResultIsCorrect) {
iFListTestPushFrontOne<false>();
}
TEST(IFList, WhenResettingThenListIsEmpty) {
DummyFNode *nodes[7];
uint32_t destructorCounter = 0;
makeList(nodes, &destructorCounter);
auto list = std::unique_ptr<IFList<DummyFNode, false, true>>(new IFList<DummyFNode, false, true>(nodes[0]));
ASSERT_FALSE(list->peekIsEmpty());
ASSERT_EQ(nodes[0], list->peekHead());
list.reset();
ASSERT_EQ(sizeof(nodes) / sizeof(nodes[0]), destructorCounter);
}
TEST(IFList, WhenSplicingAndDeletingAllThenListIsEmpty) {
DummyFNode *nodes[7];
DummyFNode *nodes2[sizeof(nodes) / sizeof(nodes[0])];
uint32_t destructorCounter = 0;
makeList(nodes, &destructorCounter);
makeList(nodes2, &destructorCounter);
IFList<DummyFNode, false, false> list;
list.splice(*nodes[0]);
ASSERT_FALSE(list.peekIsEmpty());
ASSERT_EQ(nodes[0], list.peekHead());
list.splice(*nodes2[0]);
list.deleteAll();
ASSERT_EQ(2 * sizeof(nodes) / sizeof(nodes[0]), destructorCounter);
}
template <bool ThreadSafe>
void iFListTestDetachNodes() {
IFList<DummyFNode, ThreadSafe, false> list;
uint32_t destructorCounter = 0;
static const uint32_t maxNodes = 17;
for (uint32_t i = 0; i < maxNodes; ++i) {
list.pushFrontOne(*new DummyFNode(&destructorCounter));
}
ASSERT_FALSE(list.peekIsEmpty());
auto nodes = list.detachNodes();
ASSERT_TRUE(list.peekIsEmpty());
nodes->deleteThisAndAllNext();
ASSERT_EQ(maxNodes, destructorCounter);
}
TEST(IFList, GivenThreadSafeWhenDetachingNodesThenResultIsCorrect) {
iFListTestDetachNodes<true>();
}
TEST(IFList, GivenNonThreadSafeWhenDetachingNodesThenResultIsCorrect) {
iFListTestDetachNodes<false>();
}
TEST(IFList, WhenExchangingHeadThenResultIsCorrect) {
struct DummyList : IFList<DummyFNode, true, false> {
void testCompareExchangeHead(DummyFNode *preSet, DummyFNode *&expected, DummyFNode *desired) {
head = preSet;
compareExchangeHead(expected, desired);
}
};
DummyList list;
DummyFNode nd;
nd.next = nullptr;
list.testCompareExchangeHead(nullptr, nd.next, &nd);
EXPECT_EQ(list.peekHead(), &nd);
EXPECT_EQ(nullptr, nd.next);
DummyFNode nd2;
nd2.next = &nd;
list.testCompareExchangeHead(nullptr, nd2.next, &nd2);
EXPECT_EQ(list.peekHead(), &nd2);
EXPECT_EQ(nullptr, nd2.next);
}
template <bool ThreadSafe>
void iFRefListTestPushFrontOne() {
auto list = std::unique_ptr<IFRefList<DummyFNode, ThreadSafe, true>>(new IFRefList<DummyFNode, ThreadSafe, true>());
ASSERT_TRUE(list->peekIsEmpty());
DummyFNode node1;
node1.next = nullptr; // garbage
list->pushRefFrontOne(node1);
ASSERT_FALSE(list->peekIsEmpty());
ASSERT_EQ(&node1, list->peekHead()->ref);
ASSERT_EQ(nullptr, node1.next);
DummyFNode node2;
node2.next = nullptr; // garbage
list->pushRefFrontOne(node2);
ASSERT_FALSE(list->peekIsEmpty());
ASSERT_EQ(&node2, list->peekHead()->ref);
ASSERT_EQ(nullptr, node2.next);
ASSERT_EQ(&node2, list->peekHead()->ref);
ASSERT_EQ(&node1, list->peekHead()->next->ref);
list.reset();
}
TEST(IFRefList, GivenThreadSafeWhenPushingFrontOneThenResultIsCorrect) {
iFRefListTestPushFrontOne<true>();
}
TEST(IFRefList, GivenNonThreadSafeWhenPushingFrontOneThenResultIsCorrect) {
iFRefListTestPushFrontOne<false>();
}
TEST(IDNode, GivenNextOrPrevWhenInsertingOneThenNodeInsertedInCorrectPlace) {
DummyDNode head;
ASSERT_EQ(nullptr, head.prev);
ASSERT_EQ(nullptr, head.next);
DummyDNode successor, successor2;
successor.next = &head; // garbage value
successor.prev = &successor; // garbage value
head.insertOneNext(successor);
ASSERT_EQ(&successor, head.next);
ASSERT_EQ(&head, successor.prev);
ASSERT_EQ(nullptr, successor.next);
head.insertOneNext(successor2);
ASSERT_EQ(&successor2, successor.prev);
DummyDNode predecessor, predecessor2;
predecessor.next = &successor; // garbage value
predecessor.prev = &head; // garbage value
head.insertOnePrev(predecessor);
ASSERT_EQ(&predecessor, head.prev);
ASSERT_EQ(&head, predecessor.next);
ASSERT_EQ(nullptr, predecessor.prev);
head.insertOnePrev(predecessor2);
ASSERT_EQ(&predecessor2, predecessor.next);
}
TEST(IDNode, WhenInsertingAllNextThenAllNodesInsertedAtEnd) {
DummyDNode *listA[5];
DummyDNode *listB[7];
DummyDNode *listC[1];
makeList(listA);
makeList(listB);
makeList(listC);
listA[2]->insertAllNext(*listB[0]);
EXPECT_EQ(listB[0], listA[2]->next);
EXPECT_EQ(listB[0]->prev, listA[2]);
EXPECT_EQ(listA[3], listB[sizeof(listB) / sizeof(listB[0]) - 1]->next);
listA[4]->insertAllNext(*listC[0]);
int totalNodes = sizeof(listA) / sizeof(listA[0]) + sizeof(listB) / sizeof(listB[0]) + sizeof(listC) / sizeof(listC[0]);
DummyDNode *curr = listA[0];
int nodesCount = 0;
while (curr && curr->next) {
auto next = curr->next;
++nodesCount;
curr = next;
}
++nodesCount;
ASSERT_EQ(totalNodes, nodesCount);
nodesCount = 0;
while (curr) {
auto prev = curr->prev;
delete curr;
++nodesCount;
curr = prev;
}
ASSERT_EQ(totalNodes, nodesCount);
}
TEST(IDNode, WhenDeletingThisAndAllNextThenAllCorrectNodesAreDeleted) {
DummyFNode *list[7];
uint32_t destructorCounter = 0;
makeList(list, &destructorCounter);
list[0]->deleteThisAndAllNext();
ASSERT_EQ(sizeof(list) / sizeof(list[0]), destructorCounter);
}
TEST(IDNode, WhenDeletingThisAndAllPrevThenAllCorrectNodesAreDeleted) {
DummyDNode *list[7];
uint32_t destructorCounter = 0;
makeList(list, &destructorCounter);
list[sizeof(list) / sizeof(list[0]) - 2]->deleteThisAndAllPrev();
ASSERT_EQ(sizeof(list) / sizeof(list[0]) - 1, destructorCounter);
destructorCounter = 0;
list[sizeof(list) / sizeof(list[0]) - 1]->deleteThisAndAllPrev();
ASSERT_EQ(1U, destructorCounter);
}
TEST(IDNode, WhenDeletingThisAndAllConnectedThenAllCorrectNodesAreDeleted) {
DummyDNode *list[7];
uint32_t destructorCounter = 0;
makeList(list, &destructorCounter);
list[sizeof(list) / sizeof(list[0]) / 2]->deleteThisAndAllConnected();
ASSERT_EQ(sizeof(list) / sizeof(list[0]), destructorCounter);
}
TEST(IDNode, WhenGettingHeadAndGettingTailThenCorrectNodeIsReturned) {
DummyDNode *list[7];
makeList(list, nullptr);
auto head = list[5]->getHead();
auto tail = list[0]->getTail();
list[0]->deleteThisAndAllNext();
ASSERT_EQ(list[0], head);
ASSERT_EQ(list[sizeof(list) / sizeof(list[0]) - 1], tail);
}
TEST(IDNode, WhenSlicingThenListsAreCorrect) {
DummyDNode *list[7];
uint32_t destructorCounter = 0;
makeList(list, &destructorCounter);
auto listB = list[3]->slice();
ASSERT_EQ(nullptr, list[6]->slice());
ASSERT_EQ(nullptr, list[3]->next);
ASSERT_EQ(list[4], listB);
ASSERT_EQ(nullptr, listB->prev);
list[0]->deleteThisAndAllNext();
listB->deleteThisAndAllNext();
ASSERT_EQ(sizeof(list) / sizeof(list[0]), destructorCounter);
}
TEST(IDNode, WhenCountingConnectedThenCountIsCorrect) {
DummyDNode *nodes[7];
makeList(nodes, 0);
ASSERT_EQ(3U, nodes[3]->countSuccessors());
ASSERT_EQ(2U, nodes[2]->countPredecessors());
ASSERT_EQ(7U, nodes[2]->countThisAndAllConnected());
for (auto &d : nodes) {
delete d;
}
}
TEST(IDNode, WhenCheckingRelationshipThenResultIsCorrect) {
DummyDNode *nodes[7];
makeList(nodes, 0);
DummyDNode unatachedNode;
ASSERT_FALSE(nodes[3]->isPredecessorOf(*nodes[0]));
ASSERT_FALSE(nodes[3]->isPredecessorOf(*nodes[1]));
ASSERT_FALSE(nodes[3]->isPredecessorOf(*nodes[2]));
ASSERT_FALSE(nodes[3]->isPredecessorOf(*nodes[3]));
ASSERT_TRUE(nodes[3]->isPredecessorOf(*nodes[4]));
ASSERT_TRUE(nodes[3]->isPredecessorOf(*nodes[5]));
ASSERT_TRUE(nodes[3]->isPredecessorOf(*nodes[6]));
ASSERT_FALSE(nodes[3]->isPredecessorOf(unatachedNode));
ASSERT_TRUE(nodes[3]->isSuccessorOf(*nodes[0]));
ASSERT_TRUE(nodes[3]->isSuccessorOf(*nodes[1]));
ASSERT_TRUE(nodes[3]->isSuccessorOf(*nodes[2]));
ASSERT_FALSE(nodes[3]->isSuccessorOf(*nodes[3]));
ASSERT_FALSE(nodes[3]->isSuccessorOf(*nodes[4]));
ASSERT_FALSE(nodes[3]->isSuccessorOf(*nodes[5]));
ASSERT_FALSE(nodes[3]->isSuccessorOf(*nodes[6]));
ASSERT_FALSE(nodes[3]->isSuccessorOf(unatachedNode));
ASSERT_TRUE(nodes[3]->isConnectedWith(*nodes[0]));
ASSERT_TRUE(nodes[3]->isConnectedWith(*nodes[1]));
ASSERT_TRUE(nodes[3]->isConnectedWith(*nodes[2]));
ASSERT_TRUE(nodes[3]->isConnectedWith(*nodes[3]));
ASSERT_TRUE(nodes[3]->isConnectedWith(*nodes[4]));
ASSERT_TRUE(nodes[3]->isConnectedWith(*nodes[5]));
ASSERT_TRUE(nodes[3]->isConnectedWith(*nodes[6]));
ASSERT_FALSE(nodes[3]->isConnectedWith(unatachedNode));
for (auto n : nodes) {
delete n;
}
}
TEST(IDNode, WhenVerifyingSequenceThenResultIsCorrect) {
DummyDNode *nodes[4];
makeList(nodes, 0);
// valid list
ASSERT_EQ(-1, verifyDListOrder(nodes[0], nodes[0], nodes[1], nodes[2], nodes[3]));
// first->prev != nullptr
nodes[0]->prev = nodes[1];
ASSERT_EQ(0, verifyDListOrder(nodes[0], nodes[0], nodes[1], nodes[2], nodes[3]));
for (DummyDNode *n : nodes) {
delete n;
}
}
template <bool ThreadSafe>
void iDListTestPushOne() {
IDList<DummyDNode, ThreadSafe, false, false> list(nullptr);
ASSERT_TRUE(list.peekIsEmpty());
ASSERT_EQ(nullptr, list.peekHead());
ASSERT_EQ(nullptr, list.peekTail());
DummyDNode node1;
node1.next = &node1; // garbage
node1.prev = &node1; // garbage
list.pushFrontOne(node1);
ASSERT_FALSE(list.peekIsEmpty());
ASSERT_EQ(&node1, list.peekHead());
ASSERT_EQ(nullptr, node1.next);
ASSERT_EQ(nullptr, node1.prev);
ASSERT_EQ(&node1, list.peekHead());
ASSERT_EQ(&node1, list.peekTail());
DummyDNode node2;
node2.next = &node2; // garbage
node2.prev = &node2; // garbage
list.pushFrontOne(node2);
ASSERT_FALSE(list.peekIsEmpty());
ASSERT_EQ(&node2, list.peekHead());
ASSERT_EQ(&node1, node2.next);
ASSERT_EQ(&node2, node1.prev);
ASSERT_EQ(nullptr, node2.prev);
ASSERT_EQ(nullptr, node1.next);
ASSERT_EQ(&node2, list.peekHead());
ASSERT_EQ(&node1, list.peekTail());
DummyDNode node3;
node3.next = &node3; // garbage
node3.prev = &node3; // garbage
list.pushTailOne(node3);
ASSERT_FALSE(list.peekIsEmpty());
ASSERT_EQ(&node2, list.peekHead());
ASSERT_EQ(&node1, node2.next);
ASSERT_EQ(&node2, node1.prev);
ASSERT_EQ(nullptr, node2.prev);
ASSERT_EQ(&node3, node1.next);
ASSERT_EQ(&node1, node3.prev);
ASSERT_EQ(nullptr, node3.next);
ASSERT_EQ(&node2, list.peekHead());
ASSERT_EQ(&node3, list.peekTail());
list.detachNodes();
ASSERT_TRUE(list.peekIsEmpty());
ASSERT_EQ(nullptr, list.peekHead());
ASSERT_EQ(nullptr, list.peekTail());
node1.next = &node1; // garbage
node1.prev = &node1; // garbage
list.pushTailOne(node1);
ASSERT_FALSE(list.peekIsEmpty());
ASSERT_EQ(&node1, list.peekHead());
ASSERT_EQ(nullptr, node1.next);
ASSERT_EQ(nullptr, node1.prev);
ASSERT_EQ(&node1, list.peekHead());
ASSERT_EQ(&node1, list.peekTail());
node2.next = &node2; // garbage
node2.prev = &node2; // garbage
list.pushTailOne(node2);
ASSERT_FALSE(list.peekIsEmpty());
ASSERT_EQ(&node1, list.peekHead());
ASSERT_EQ(&node2, node1.next);
ASSERT_EQ(&node1, node2.prev);
ASSERT_EQ(nullptr, node1.prev);
ASSERT_EQ(nullptr, node2.next);
ASSERT_EQ(&node1, list.peekHead());
ASSERT_EQ(&node2, list.peekTail());
}
TEST(IDList, GivenThreadSafeWhenPushingOneThenResultIsCorrect) {
iDListTestPushOne<true>();
}
TEST(IDList, GivenNonThreadSafeWhenPushingOneThenResultIsCorrect) {
iDListTestPushOne<false>();
}
TEST(IDList, WhenResettingThenListIsEmpty) {
DummyDNode *nodes[7];
uint32_t destructorCounter = 0;
makeList(nodes, &destructorCounter);
auto list = std::unique_ptr<IDList<DummyDNode, false, true, false>>(new IDList<DummyDNode, false, true, false>(nodes[0]));
ASSERT_FALSE(list->peekIsEmpty());
ASSERT_EQ(nodes[0], list->peekHead());
list.reset();
ASSERT_EQ(sizeof(nodes) / sizeof(nodes[0]), destructorCounter);
}
template <bool ThreadSafe>
void iDListSpliceAndDeleteAll() {
DummyDNode *nodes[7];
DummyDNode *nodes2[sizeof(nodes) / sizeof(nodes[0])];
uint32_t destructorCounter = 0;
makeList(nodes, &destructorCounter);
makeList(nodes2, &destructorCounter);
IDList<DummyDNode, ThreadSafe, false, false> list;
list.splice(*nodes[0]);
EXPECT_FALSE(list.peekIsEmpty());
EXPECT_EQ(nodes[0], list.peekHead());
list.splice(*nodes2[0]);
DummyDNode *nd = list.peekHead();
while (nd && nd->next) {
if (nd->prev) {
EXPECT_EQ(nd, nd->prev->next);
}
nd = nd->next;
}
if (nd && nd->prev) {
EXPECT_EQ(nd, nd->prev->next);
}
EXPECT_EQ(list.peekTail(), nd);
list.deleteAll();
EXPECT_EQ(2 * sizeof(nodes) / sizeof(nodes[0]), destructorCounter);
}
TEST(IDList, GivenThreadSafeWhenSplicingAndDeletingAllThenResultIsCorrect) {
iDListSpliceAndDeleteAll<true>();
}
TEST(IDList, GivenNonThreadSafeWhenSplicingAndDeletingAllThenResultIsCorrect) {
iDListSpliceAndDeleteAll<false>();
}
template <bool ThreadSafe>
void iDListTestDetachNodes() {
IDList<DummyDNode, ThreadSafe, false, false> list;
uint32_t destructorCounter = 0;
static const uint32_t maxNodes = 17;
for (uint32_t i = 0; i < maxNodes; ++i) {
list.pushFrontOne(*new DummyDNode(&destructorCounter));
}
ASSERT_FALSE(list.peekIsEmpty());
auto nodes = list.detachNodes();
ASSERT_TRUE(list.peekIsEmpty());
nodes->deleteThisAndAllNext();
ASSERT_EQ(maxNodes, destructorCounter);
}
TEST(IDList, GivenThreadSafeWhenDetachingNodesThenResultIsCorrect) {
iDListTestDetachNodes<true>();
}
TEST(IDList, GivenNonThreadSafeWhenDetachingNodesThenResultIsCorrect) {
iDListTestDetachNodes<false>();
}
template <bool ThreadSafe>
void iDListTestRemoveOne() {
IDList<DummyDNode, ThreadSafe, false, false> list;
DummyDNode nodes[3];
list.pushFrontOne(nodes[0]);
list.pushFrontOne(nodes[1]);
list.pushFrontOne(nodes[2]);
ASSERT_FALSE(list.peekIsEmpty());
ASSERT_EQ(&nodes[2], list.peekHead());
ASSERT_EQ(&nodes[0], list.peekTail());
auto removedNode = list.removeOne(nodes[1]);
ASSERT_EQ(&nodes[2], list.peekHead());
ASSERT_EQ(&nodes[0], list.peekTail());
ASSERT_EQ(&nodes[2], nodes[0].prev);
ASSERT_EQ(&nodes[0], nodes[2].next);
ASSERT_EQ(nullptr, removedNode->prev);
ASSERT_EQ(nullptr, removedNode->next);
removedNode.release();
removedNode = list.removeOne(nodes[2]);
ASSERT_EQ(&nodes[0], list.peekHead());
ASSERT_EQ(&nodes[0], list.peekTail());
ASSERT_EQ(nullptr, nodes[0].prev);
ASSERT_EQ(nullptr, nodes[0].next);
ASSERT_EQ(nullptr, removedNode->prev);
ASSERT_EQ(nullptr, removedNode->next);
removedNode.release();
removedNode = list.removeOne(nodes[0]);
ASSERT_TRUE(list.peekIsEmpty());
ASSERT_EQ(nullptr, list.peekHead());
ASSERT_EQ(nullptr, list.peekTail());
ASSERT_EQ(nullptr, removedNode->prev);
ASSERT_EQ(nullptr, removedNode->next);
removedNode.release();
list.pushFrontOne(nodes[0]);
list.pushFrontOne(nodes[1]);
ASSERT_FALSE(list.peekIsEmpty());
ASSERT_EQ(&nodes[1], list.peekHead());
ASSERT_EQ(&nodes[0], list.peekTail());
removedNode = list.removeOne(nodes[0]);
ASSERT_EQ(&nodes[1], list.peekHead());
ASSERT_EQ(&nodes[1], list.peekTail());
ASSERT_EQ(nullptr, removedNode->prev);
ASSERT_EQ(nullptr, removedNode->next);
removedNode.release();
}
TEST(IDList, GivenThreadSafeWhenRemovingOneThenResultIsCorrect) {
iDListTestRemoveOne<true>();
}
TEST(IDList, GivenNonThreadSafeWhenRemovingOneThenResultIsCorrect) {
iDListTestRemoveOne<false>();
}
template <bool ThreadSafe>
void iDListTestRemoveFrontOne() {
IDList<DummyDNode, ThreadSafe, false, false> list;
DummyDNode nodes[3];
DummyDNode *head = nullptr;
DummyDNode *tail = nullptr;
list.pushFrontOne(nodes[0]);
list.pushFrontOne(nodes[1]);
list.pushFrontOne(nodes[2]);
ASSERT_FALSE(list.peekIsEmpty());
head = list.peekHead();
tail = list.peekTail();
ASSERT_NE(nullptr, head);
ASSERT_NE(nullptr, tail);
EXPECT_NE(head, tail);
EXPECT_EQ(head, list.removeFrontOne().release());
EXPECT_EQ(tail, list.peekTail());
EXPECT_NE(head, list.peekHead());
ASSERT_FALSE(list.peekIsEmpty());
head = list.peekHead();
tail = list.peekTail();
ASSERT_NE(nullptr, head);
ASSERT_NE(nullptr, tail);
EXPECT_NE(head, tail);
EXPECT_EQ(head, list.removeFrontOne().release());
EXPECT_EQ(tail, list.peekTail());
EXPECT_NE(head, list.peekHead());
ASSERT_FALSE(list.peekIsEmpty());
head = list.peekHead();
tail = list.peekTail();
ASSERT_NE(nullptr, head);
ASSERT_NE(nullptr, tail);
EXPECT_EQ(head, tail);
EXPECT_EQ(head, list.removeFrontOne().release());
EXPECT_EQ(nullptr, list.peekTail());
EXPECT_EQ(nullptr, list.peekHead());
EXPECT_EQ(nullptr, list.removeFrontOne().release());
EXPECT_EQ(nullptr, list.peekHead());
EXPECT_EQ(nullptr, list.peekTail());
EXPECT_TRUE(list.peekIsEmpty());
EXPECT_EQ(nullptr, list.removeFrontOne().release());
EXPECT_EQ(nullptr, list.peekHead());
EXPECT_EQ(nullptr, list.peekTail());
EXPECT_TRUE(list.peekIsEmpty());
}
TEST(IDList, GivenThreadSafeWhenRemovingFrontOneThenResultIsCorrect) {
iDListTestRemoveFrontOne<true>();
}
TEST(IDList, GivenNonThreadSafeWhenRemovingFrontOneThenResultIsCorrect) {
iDListTestRemoveFrontOne<false>();
}
template <bool ThreadSafe>
void iDListTestDetachSequence() {
DummyDNode *nodes[10];
makeList(nodes);
IDList<DummyDNode, ThreadSafe, false, false> list(nodes[0]);
DummyDNode *detachedNodes = nullptr;
detachedNodes = list.detachSequence(*nodes[1], *nodes[3]);
ASSERT_EQ(nodes[1], detachedNodes);
ASSERT_EQ(nullptr, nodes[1]->prev);
ASSERT_EQ(nodes[2], nodes[1]->next);
ASSERT_EQ(nodes[2], nodes[3]->prev);
ASSERT_EQ(nullptr, nodes[3]->next);
ASSERT_EQ(nodes[4], nodes[0]->next);
ASSERT_NE(nullptr, nodes[4]);
ASSERT_EQ(nodes[0], nodes[4]->prev);
ASSERT_EQ(nodes[0], list.peekHead());
ASSERT_EQ(nodes[9], list.peekTail());
detachedNodes = list.detachSequence(*nodes[0], *nodes[0]);
ASSERT_EQ(nodes[0], detachedNodes);
ASSERT_EQ(nodes[4], list.peekHead());
ASSERT_EQ(nodes[9], list.peekTail());
ASSERT_EQ(nullptr, nodes[0]->next);
ASSERT_EQ(nullptr, nodes[0]->prev);
ASSERT_EQ(nullptr, nodes[4]->prev);
detachedNodes = list.detachSequence(*nodes[4], *nodes[5]);
ASSERT_EQ(nodes[4], detachedNodes);
ASSERT_EQ(nodes[6], list.peekHead());
ASSERT_EQ(nodes[9], list.peekTail());
ASSERT_EQ(nullptr, nodes[4]->prev);
ASSERT_EQ(nodes[5], nodes[4]->next);
ASSERT_EQ(nodes[4], nodes[5]->prev);
ASSERT_EQ(nullptr, nodes[5]->next);
detachedNodes = list.detachSequence(*nodes[8], *nodes[9]);
ASSERT_EQ(nodes[8], detachedNodes);
ASSERT_EQ(nodes[6], list.peekHead());
ASSERT_EQ(nodes[7], list.peekTail());
ASSERT_EQ(nullptr, nodes[8]->prev);
ASSERT_EQ(nodes[9], nodes[8]->next);
ASSERT_EQ(nodes[8], nodes[9]->prev);
ASSERT_EQ(nullptr, nodes[9]->next);
detachedNodes = list.detachSequence(*nodes[6], *nodes[7]);
ASSERT_EQ(nodes[6], detachedNodes);
ASSERT_TRUE(list.peekIsEmpty());
ASSERT_EQ(nullptr, list.peekHead());
ASSERT_EQ(nullptr, list.peekTail());
ASSERT_EQ(nullptr, nodes[6]->prev);
ASSERT_EQ(nodes[7], nodes[6]->next);
ASSERT_EQ(nodes[6], nodes[7]->prev);
ASSERT_EQ(nullptr, nodes[7]->next);
for (auto n : nodes) {
delete n;
}
}
TEST(IDList, GivenThreadSafeWhenDetachingSequenceThenResultIsCorrect) {
iDListTestDetachSequence<true>();
}
TEST(IDList, GivenNonThreadSafeWhenDetachingSequenceThenResultIsCorrect) {
iDListTestDetachSequence<false>();
}
template <bool ThreadSafe>
void iDListTestPeekContains() {
IDList<DummyDNode, ThreadSafe, false, false> list;
DummyDNode *node1 = new DummyDNode;
EXPECT_FALSE(list.peekContains(*node1));
list.pushFrontOne(*node1);
EXPECT_TRUE(list.peekContains(*node1));
DummyDNode *node2 = new DummyDNode;
EXPECT_FALSE(list.peekContains(*node2));
list.pushTailOne(*node2);
EXPECT_TRUE(list.peekContains(*node2));
DummyDNode *node3 = new DummyDNode;
EXPECT_FALSE(list.peekContains(*node3));
list.pushFrontOne(*node3);
EXPECT_TRUE(list.peekContains(*node3));
list.removeOne(*node2);
EXPECT_FALSE(list.peekContains(*node2));
EXPECT_TRUE(list.peekContains(*node1));
EXPECT_TRUE(list.peekContains(*node3));
list.removeOne(*node1);
EXPECT_FALSE(list.peekContains(*node2));
EXPECT_FALSE(list.peekContains(*node1));
EXPECT_TRUE(list.peekContains(*node3));
list.removeOne(*node3);
EXPECT_FALSE(list.peekContains(*node2));
EXPECT_FALSE(list.peekContains(*node1));
EXPECT_FALSE(list.peekContains(*node3));
}
TEST(IDList, GivenThreadSafeWhenPeekingThenResultIsCorrect) {
iDListTestPeekContains<true>();
}
TEST(IDList, GivenNonThreadSafeWhenPeekingThenResultIsCorrect) {
iDListTestPeekContains<false>();
}
template <typename NodeObjectType, bool ThreadSafe, bool OwnsNodes, bool SupportRecursiveLock>
void testIDListSpinlock() {
struct ListMock : IDList<NodeObjectType, ThreadSafe, OwnsNodes, SupportRecursiveLock> {
std::atomic_flag &getLockedRef() { return this->locked; }
std::atomic<std::thread::id> &getLockOwnerRef() { return this->lockOwner; }
using ListenerT = void (*)(IDList<NodeObjectType, ThreadSafe, OwnsNodes, SupportRecursiveLock> &list);
ListenerT &getSpinLockedListenerRef() { return this->spinLockedListener; }
int32_t lockLoopCount = 0;
static void listener(IDList<NodeObjectType, ThreadSafe, OwnsNodes, SupportRecursiveLock> &list) {
ListMock &l = reinterpret_cast<ListMock &>(list);
EXPECT_LT(0, l.lockLoopCount);
--l.lockLoopCount;
if (l.lockLoopCount == 0) {
l.locked.clear(); // unlock
}
}
void callNotifySpinlockedListener() {
this->notifySpinLocked();
}
};
ListMock l;
l.getSpinLockedListenerRef() = &ListMock::listener;
l.lockLoopCount = 1;
l.callNotifySpinlockedListener();
EXPECT_EQ(0, l.lockLoopCount);
l.getSpinLockedListenerRef() = nullptr;
l.callNotifySpinlockedListener();
EXPECT_EQ(0, l.lockLoopCount);
std::thread::id invalidThreadId;
l.getLockedRef().test_and_set(std::memory_order_seq_cst);
l.getLockOwnerRef() = invalidThreadId;
l.getSpinLockedListenerRef() = &ListMock::listener;
constexpr auto spinlockLoopCount = 10;
l.lockLoopCount = spinlockLoopCount;
auto ret = l.detachNodes();
EXPECT_EQ(0, l.lockLoopCount);
EXPECT_EQ(nullptr, ret);
}
TEST(IDList, GivenLockedIDListWhenProcessLockedIsUsedThenWaitsInSpinlock) {
testIDListSpinlock<DummyDNode, true, false, true>();
testIDListSpinlock<DummyDNode, true, false, false>();
}
template <typename NodeObjectType, bool ThreadSafe, bool OwnsNodes, bool SupportRecursiveLock>
void testIDListUnlockOnException() {
using ExType = std::runtime_error;
struct ListMock : IDList<NodeObjectType, ThreadSafe, OwnsNodes, SupportRecursiveLock> {
void throwExFromLock() {
this->template processLocked<ListMock, &ListMock::throwExFromLockImpl>(nullptr, nullptr);
}
std::atomic_flag &getLockedRef() { return this->locked; }
protected:
NodeObjectType *throwExFromLockImpl(NodeObjectType *, void *) {
throw ExType{""};
}
};
ListMock l;
EXPECT_THROW(l.throwExFromLock(), ExType);
EXPECT_FALSE(l.getLockedRef().test_and_set(std::memory_order_seq_cst));
}
TEST(IDList, GivenInsideLockWhenExceptionIsThrownThenUnlocksBeforeRethrowingException) {
testIDListUnlockOnException<DummyDNode, true, false, true>();
testIDListUnlockOnException<DummyDNode, true, false, false>();
}
template <bool ThreadSafe>
void iDRefListTestPushFrontOne() {
auto list = std::unique_ptr<IDRefList<DummyDNode, ThreadSafe, true>>(new IDRefList<DummyDNode, ThreadSafe, true>());
ASSERT_TRUE(list->peekIsEmpty());
DummyDNode node1;
node1.prev = nullptr; // garbage
node1.next = nullptr; // garbage
list->pushRefFrontOne(node1);
ASSERT_FALSE(list->peekIsEmpty());
ASSERT_EQ(&node1, list->peekHead()->ref);
ASSERT_EQ(nullptr, node1.next);
ASSERT_EQ(nullptr, node1.prev);
DummyDNode node2;
node2.prev = nullptr; // garbage
node2.next = nullptr; // garbage
list->pushRefFrontOne(node2);
ASSERT_FALSE(list->peekIsEmpty());
ASSERT_EQ(&node2, list->peekHead()->ref);
ASSERT_EQ(nullptr, node2.prev);
ASSERT_EQ(nullptr, node2.next);
ASSERT_EQ(&node2, list->peekHead()->ref);
ASSERT_EQ(&node1, list->peekHead()->next->ref);
ASSERT_EQ(&node2, list->peekTail()->prev->ref);
list.reset();
}
TEST(IDRefList, GivenThreadSafeWhenPushingFrontOneThenResultIsCorrect) {
iDRefListTestPushFrontOne<true>();
}
TEST(IDRefList, GivenNonThreadSafeWhenPushingFrontOneThenResultIsCorrect) {
iDRefListTestPushFrontOne<false>();
}
TEST(IDRefList, GivenRecursiveLockWhenExecutingThenListIsNotEmpty) {
class RecursiveLockList : public IDList<DummyDNode, true, true, true> {
public:
void execute() {
processLocked<RecursiveLockList, &RecursiveLockList::executeImpl>(nullptr, nullptr);
}
protected:
DummyDNode *executeImpl(DummyDNode *, void *data) {
this->pushFrontOne(*(new DummyDNode())); // recursive lock
return nullptr;
}
};
RecursiveLockList list;
ASSERT_TRUE(list.peekIsEmpty());
list.execute();
ASSERT_FALSE(list.peekIsEmpty());
}
// Checks if a pointer is contained (in terms of continous memory) within given object
template <typename ContainerType>
bool contains(const ContainerType *container, const void *ptr) {
uintptr_t base = (intptr_t)container;
uintptr_t tested = (intptr_t)ptr;
size_t elementSize = sizeof(decltype(*container->begin()));
return (tested >= base) && (tested + elementSize <= base + sizeof(ContainerType));
}
TEST(StackVec, WhenStackVecIsCreatedThenItIsCorrectlyPopulated) {
using Type = int;
static const uint32_t srcSize = 10;
static const uint32_t overReserve = 3;
static const uint32_t underReserve = 3;
std::vector<Type> src(srcSize);
for (uint32_t i = 0; i < srcSize; ++i) {
src[i] = i;
}
ASSERT_GE(sizeof(StackVec<Type, srcSize>), sizeof(Type) * srcSize);
StackVec<Type, srcSize + overReserve> bigger(src.begin(), src.end());
StackVec<Type, srcSize> exact(src.begin(), src.end());
StackVec<Type, srcSize - underReserve> smaller(src.begin(), src.end());
ASSERT_EQ(srcSize, bigger.size());
ASSERT_EQ(srcSize, exact.size());
ASSERT_EQ(srcSize, smaller.size());
ASSERT_EQ(srcSize + overReserve, bigger.capacity());
ASSERT_EQ(srcSize, exact.capacity());
ASSERT_TRUE(smaller.capacity() >= srcSize);
// check if data was copied successfully
ASSERT_EQ(0, memcmp(&*src.begin(), &*bigger.begin(), sizeof(Type) * srcSize));
ASSERT_EQ(0, memcmp(&*src.begin(), &*exact.begin(), sizeof(Type) * srcSize));
ASSERT_EQ(0, memcmp(&*src.begin(), &*smaller.begin(), sizeof(Type) * srcSize));
// make sure data is allocated within the object (e.g. on stack) if the container is big enough
ASSERT_TRUE(contains(&bigger, &*bigger.begin()));
ASSERT_TRUE(contains(&exact, &*exact.begin()));
ASSERT_FALSE(contains(&smaller, &*smaller.begin()));
StackVec<Type, 4> withInitList{1, 2, 3, 5};
EXPECT_EQ(1, withInitList[0]);
EXPECT_EQ(2, withInitList[1]);
EXPECT_EQ(3, withInitList[2]);
EXPECT_EQ(5, withInitList[3]);
}
TEST(StackVec, GivenInitialSizeWhenCreatingThenResizeOccurs) {
StackVec<int, 5> vec1(10);
EXPECT_EQ(10U, vec1.size());
}
TEST(StackVec, WhenCopyingByConstructorThenCopyIsCorrect) {
using Type = int;
constexpr size_t sizeBase = 7;
constexpr size_t sizeSmaller = 5;
constexpr size_t sizeBigger = 9;
size_t sizeConfigs[] = {0, sizeSmaller, sizeBase, sizeBigger};
auto it = 0;
for (auto sc : sizeConfigs) {
++it;
StackVec<Type, sizeBase> src;
Type refData[sizeBigger];
for (uint32_t i = 0; i < sc; ++i) {
refData[i] = i * it;
src.push_back(refData[i]);
}
StackVec<Type, sizeBase> dst(src);
ASSERT_EQ(sc, src.size());
ASSERT_EQ(sc, dst.size());
EXPECT_EQ(0, memcmp(refData, &*src.begin(), sc));
EXPECT_EQ(0, memcmp(refData, &*dst.begin(), sc));
}
}
TEST(StackVec, WhenCopyingByAssignmentThenCopyIsCorrect) {
using Type = int;
constexpr size_t sizeBase = 7;
constexpr size_t sizeSmaller = 5;
constexpr size_t sizeBigger = 9;
size_t sizeConfigs[] = {0, sizeSmaller, sizeBase, sizeBigger};
auto it = 0;
for (auto dstSc : sizeConfigs) {
for (auto srcSc : sizeConfigs) {
++it;
StackVec<Type, sizeBase> src;
Type refData[sizeBigger];
for (uint32_t i = 0; i < srcSc; ++i) {
refData[i] = i * it;
src.push_back(refData[i]);
}
StackVec<Type, sizeBase> dst;
for (uint32_t i = 0; i < dstSc; ++i) {
dst.push_back(~i);
}
ASSERT_EQ(srcSc, src.size());
ASSERT_EQ(dstSc, dst.size());
dst = src;
ASSERT_EQ(srcSc, src.size());
ASSERT_EQ(srcSc, dst.size());
EXPECT_EQ(0, memcmp(refData, &*src.begin(), srcSc));
EXPECT_EQ(0, memcmp(refData, &*dst.begin(), srcSc));
}
}
}
TEST(StackVec, WhenMovingThenObjectIsCorrect) {
using Type = int;
constexpr size_t sizeBase = 7;
constexpr size_t sizeSmaller = 5;
constexpr size_t sizeBigger = 9;
size_t sizeConfigs[] = {0, sizeSmaller, sizeBase, sizeBigger};
auto it = 0;
for (auto sc : sizeConfigs) {
++it;
StackVec<Type, sizeBase> src;
Type refData[sizeBigger];
for (uint32_t i = 0; i < sc; ++i) {
refData[i] = i * it;
src.push_back(refData[i]);
}
StackVec<Type, sizeBase> dst(std::move(src));
ASSERT_EQ(sc, dst.size());
EXPECT_EQ(0, memcmp(&*refData, &*dst.begin(), sc));
}
}
TEST(StackVec, WhenMovingByAssignmentThenObjectIsCorrect) {
using Type = int;
constexpr size_t sizeBase = 7;
constexpr size_t sizeSmaller = 5;
constexpr size_t sizeBigger = 9;
size_t sizeConfigs[] = {0, sizeSmaller, sizeBase, sizeBigger};
auto it = 0;
for (auto dstSc : sizeConfigs) {
for (auto srcSc : sizeConfigs) {
++it;
StackVec<Type, sizeBase> src;
Type refData[sizeBigger];
for (uint32_t i = 0; i < srcSc; ++i) {
refData[i] = i * it;
src.push_back(refData[i]);
}
StackVec<Type, sizeBase> dst;
for (uint32_t i = 0; i < dstSc; ++i) {
dst.push_back(~i);
}
ASSERT_EQ(srcSc, src.size());
ASSERT_EQ(dstSc, dst.size());
dst = std::move(src);
ASSERT_EQ(srcSc, dst.size());
EXPECT_EQ(0, memcmp(refData, &*dst.begin(), srcSc));
}
}
}
TEST(StackVec, WhenStackVecIsCreatedThenItIsCorrectlyAligned) {
StackVec<uint16_t, 4> s16;
StackVec<uint32_t, 4> s32;
StackVec<uint64_t, 4> s64;
static_assert(sizeof(s16) <= 24u, "");
static_assert(sizeof(s32) <= 32u, "");
static_assert(sizeof(s64) <= 48u, "");
s16.push_back(0);
s32.push_back(0);
s64.push_back(0);
EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(s16.begin()) % 2);
EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(s32.begin()) % 4);
EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(s64.begin()) % 8);
}
TEST(StackVec, WhenPushingBackThenContentsAreCorrect) {
using Type = uint32_t;
StackVec<Type, 5> v;
ASSERT_EQ(0U, v.size());
for (uint32_t i = 0; i < 5; ++i) {
v.push_back(i);
}
ASSERT_EQ(5U, v.size());
ASSERT_TRUE(contains(&v, &*v.begin()));
for (uint32_t i = 0; i < v.size(); ++i) {
ASSERT_EQ(i, v[i]);
}
v.push_back(5);
ASSERT_EQ(6U, v.size());
ASSERT_FALSE(contains(&v, &*v.begin()));
for (uint32_t i = 0; i < v.size(); ++i) {
ASSERT_EQ(i, v[i]);
}
for (uint32_t i = 0; i < 5; ++i) {
v.push_back(v[v.size() - 1] + 1);
}
for (uint32_t i = 0; i < v.size(); ++i) {
ASSERT_EQ(i, v[i]);
}
ASSERT_FALSE(contains(&v, &*v.begin()));
}
TEST(StackVec, whenSwapThenContentsAreExchanged) {
using Type = uint32_t;
StackVec<Type, 5> vector1;
StackVec<Type, 5> vector2;
for (uint32_t i = 0; i < 3; ++i) {
vector1.push_back(i);
}
for (uint32_t i = 0; i < 5; ++i) {
vector2.push_back(i);
}
vector1.swap(vector2);
EXPECT_EQ(5u, vector1.size());
EXPECT_EQ(3u, vector2.size());
EXPECT_EQ(4u, vector1.at(4u));
vector1.swap(vector2);
EXPECT_EQ(3u, vector1.size());
EXPECT_EQ(5u, vector2.size());
}
TEST(StackVec, whenDynamicStorageSwapThenContentsAreExchanged) {
using Type = uint32_t;
StackVec<Type, 1> vector1;
StackVec<Type, 1> vector2;
for (uint32_t i = 0; i < 3; ++i) {
vector1.push_back(i);
}
for (uint32_t i = 0; i < 5; ++i) {
vector2.push_back(i);
}
vector1.swap(vector2);
EXPECT_EQ(5u, vector1.size());
EXPECT_EQ(3u, vector2.size());
EXPECT_EQ(4u, vector1.at(4u));
vector1.swap(vector2);
EXPECT_EQ(3u, vector1.size());
EXPECT_EQ(5u, vector2.size());
}
TEST(StackVec, whenDynamicToNonDynamicStorageSwapThenContentsAreExchanged) {
using Type = uint32_t;
StackVec<Type, 1> vector1;
StackVec<Type, 5> vector2;
for (uint32_t i = 0; i < 3; ++i) {
vector1.push_back(i);
}
for (uint32_t i = 0; i < 5; ++i) {
vector2.push_back(i);
}
vector1.swap(vector2);
EXPECT_EQ(5u, vector1.size());
EXPECT_EQ(3u, vector2.size());
EXPECT_EQ(4u, vector1.at(4u));
vector1.swap(vector2);
EXPECT_EQ(3u, vector1.size());
EXPECT_EQ(5u, vector2.size());
}
TEST(StackVecPopBack, GivenNonEmptyStackVecThenRemoveSingleElementFromTheEnd) {
using Type = uint32_t;
StackVec<Type, 2> v;
ASSERT_EQ(2U, v.capacity());
v.push_back(3);
EXPECT_EQ(1U, v.size());
EXPECT_EQ(3U, *v.rbegin());
v.push_back(5);
EXPECT_EQ(2U, v.size());
EXPECT_EQ(5U, *v.rbegin());
v.pop_back();
EXPECT_EQ(1U, v.size());
EXPECT_EQ(3U, *v.rbegin());
v.pop_back();
EXPECT_EQ(0U, v.size());
v.push_back(3);
EXPECT_EQ(1U, v.size());
EXPECT_EQ(3U, *v.rbegin());
v.push_back(5);
EXPECT_EQ(2U, v.size());
EXPECT_EQ(5U, *v.rbegin());
v.push_back(7);
EXPECT_EQ(3U, v.size());
EXPECT_EQ(7U, *v.rbegin());
v.push_back(11);
EXPECT_EQ(4U, v.size());
EXPECT_EQ(11U, *v.rbegin());
v.pop_back();
EXPECT_EQ(3U, v.size());
EXPECT_EQ(7U, *v.rbegin());
v.pop_back();
EXPECT_EQ(2U, v.size());
EXPECT_EQ(5U, *v.rbegin());
v.pop_back();
EXPECT_EQ(1U, v.size());
EXPECT_EQ(3U, *v.rbegin());
v.pop_back();
EXPECT_EQ(0U, v.size());
}
TEST(StackVec, WhenReservingThenCapacityIncreasesAndSizeDoesNot) {
using Type = uint32_t;
StackVec<Type, 5> v;
ASSERT_EQ(5U, v.capacity());
v.push_back(3);
v.push_back(7);
ASSERT_EQ(2U, v.size());
v.reserve(10);
ASSERT_LE(10U, v.capacity());
ASSERT_EQ(2U, v.size());
ASSERT_EQ(3U, v[0]);
ASSERT_EQ(7U, v[1]);
ASSERT_FALSE(contains(&v, &*v.begin()));
v.reserve(1024);
ASSERT_LE(1024U, v.capacity());
}
TEST(StackVec, WhenResizingThenElementsAreCorrectlyManaged) {
struct Element {
Element()
: v(7) {
data = new int[100];
}
Element(int v)
: v(v) {
data = new int[100];
}
~Element() {
if (data != nullptr) {
delete[] data;
data = nullptr;
}
v = -77;
}
Element(const Element &rhs) {
this->v = rhs.v;
this->data = new int[100];
}
Element(Element &&rhs) {
this->v = rhs.v;
this->data = rhs.data;
rhs.data = nullptr;
}
Element &operator=(const Element &rhs) {
this->v = rhs.v;
delete[] this->data;
this->data = new int[100];
return *this;
}
Element &operator=(Element &&rhs) {
this->v = rhs.v;
delete[] this->data;
this->data = rhs.data;
rhs.data = nullptr;
return *this;
}
int v = 9;
int *data = nullptr;
};
StackVec<Element, 5> vec;
vec.resize(1);
ASSERT_EQ(1U, vec.size());
EXPECT_EQ(7, vec[0].v);
EXPECT_NE(nullptr, vec[0].data);
EXPECT_TRUE(contains(&vec, &*vec.begin()));
vec.resize(3, Element(11));
ASSERT_EQ(3U, vec.size());
EXPECT_EQ(7, vec[0].v);
EXPECT_EQ(11, vec[1].v);
EXPECT_EQ(11, vec[2].v);
EXPECT_NE(nullptr, vec[0].data);
EXPECT_NE(nullptr, vec[1].data);
EXPECT_NE(nullptr, vec[2].data);
EXPECT_TRUE(contains(&vec, &*vec.begin()));
vec.resize(2);
ASSERT_EQ(2U, vec.size());
EXPECT_EQ(7, vec[0].v);
EXPECT_EQ(11, vec[1].v);
EXPECT_NE(nullptr, vec[0].data);
EXPECT_NE(nullptr, vec[1].data);
EXPECT_TRUE(contains(&vec, &*vec.begin()));
vec.resize(7);
ASSERT_EQ(7U, vec.size());
EXPECT_EQ(7, vec[0].v);
EXPECT_EQ(11, vec[1].v);
EXPECT_EQ(7, vec[2].v);
EXPECT_EQ(7, vec[3].v);
EXPECT_EQ(7, vec[4].v);
EXPECT_EQ(7, vec[5].v);
EXPECT_EQ(7, vec[6].v);
EXPECT_NE(nullptr, vec[0].data);
EXPECT_NE(nullptr, vec[1].data);
EXPECT_NE(nullptr, vec[2].data);
EXPECT_NE(nullptr, vec[3].data);
EXPECT_NE(nullptr, vec[4].data);
EXPECT_NE(nullptr, vec[5].data);
EXPECT_NE(nullptr, vec[6].data);
EXPECT_FALSE(contains(&vec, &*vec.begin()));
vec.resize(1);
ASSERT_EQ(1U, vec.size());
EXPECT_EQ(7, vec[0].v);
EXPECT_NE(nullptr, vec[0].data);
EXPECT_FALSE(contains(&vec, &*vec.begin()));
vec.resize(3, Element(55));
ASSERT_EQ(3U, vec.size());
EXPECT_EQ(7, vec[0].v);
EXPECT_EQ(55, vec[1].v);
EXPECT_EQ(55, vec[2].v);
EXPECT_NE(nullptr, vec[0].data);
EXPECT_NE(nullptr, vec[1].data);
EXPECT_NE(nullptr, vec[2].data);
EXPECT_FALSE(contains(&vec, &*vec.begin()));
}
TEST(StackVec, WhenIteratingThenCorrectElementsAreReturned) {
using Type = int;
StackVec<Type, 5> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
ASSERT_TRUE(contains(&v, &*v.begin()));
Type sum = 0;
for (auto e : v) {
sum += e;
}
ASSERT_EQ(15, sum);
v.push_back(6);
v.push_back(7);
v.push_back(8);
ASSERT_FALSE(contains(&v, &*v.begin()));
sum = 0;
for (auto e : v) {
sum += e;
}
ASSERT_EQ(36, sum);
}
TEST(StackVec, WhenClearingThenAllElementsAreRemoved) {
uint32_t destructorCounter = 0;
DummyFNode nd1(&destructorCounter);
DummyFNode nd2(&destructorCounter);
DummyFNode nd3(&destructorCounter);
StackVec<DummyFNode, 3> v;
EXPECT_TRUE(v.empty());
v.push_back(nd1);
v.push_back(nd2);
v.push_back(nd3);
ASSERT_EQ(0U, destructorCounter);
ASSERT_EQ(3U, v.size());
EXPECT_FALSE(v.empty());
v.clear();
ASSERT_EQ(3U, destructorCounter);
ASSERT_EQ(0U, v.size());
EXPECT_TRUE(v.empty());
StackVec<DummyFNode, 1> v2;
v2.push_back(nd1);
v2.push_back(nd2);
v2.push_back(nd3);
destructorCounter = 0;
ASSERT_EQ(3U, v2.size());
v2.clear();
ASSERT_EQ(3U, destructorCounter);
ASSERT_EQ(0U, v2.size());
}
TEST(StackVec, WhenGettingReverseThenCorrectElementIsReturned) {
using VecType = StackVec<int, 1>;
VecType v;
v.push_back(5);
ASSERT_EQ(v.begin(), v.rend().base());
ASSERT_EQ(v.end(), v.rbegin().base());
ASSERT_EQ(v.begin(), v.crend().base());
ASSERT_EQ(v.end(), v.crbegin().base());
}
TEST(StackVec, WhenUsingConstMemberFunctionsThenResultsAreCorrect) {
using VecType = StackVec<int, 3>;
VecType v;
ASSERT_EQ(v.begin(), ((const VecType *)&v)->begin());
ASSERT_EQ(v.end(), ((const VecType *)&v)->end());
ASSERT_EQ(v.begin(), v.end());
v.push_back(5);
ASSERT_EQ(v.begin(), ((const VecType *)&v)->begin());
ASSERT_EQ(v.end(), ((const VecType *)&v)->end());
ASSERT_NE(v.begin(), v.end());
ASSERT_EQ(&v[0], &((const VecType *)&v)->operator[](0));
v.push_back(6);
v.push_back(1);
v.push_back(3);
ASSERT_EQ(v.begin(), ((const VecType *)&v)->begin());
ASSERT_EQ(v.end(), ((const VecType *)&v)->end());
ASSERT_NE(v.begin(), v.end());
ASSERT_EQ(&v[0], &((const VecType *)&v)->operator[](0));
}
TEST(StackVec, GivenComplexElementsWhenCreatingThenCreationSucceeds) {
std::vector<int> src(100, 5);
static const int elementsCount = 10;
auto v = std::unique_ptr<StackVec<std::vector<int>, elementsCount>>(new StackVec<std::vector<int>, elementsCount>());
for (int i = 0; i < elementsCount * 2; ++i) {
v->push_back(src);
}
v.reset();
}
TEST(StackVec, WhenGettingIteratorThenIteratorTypeIsCorrect) {
struct S {
};
using StackVecT = StackVec<S, 5>;
using iterator = StackVecT::iterator;
using const_iterator = StackVecT::const_iterator;
StackVecT vec;
const StackVecT &constVec = vec;
static_assert(std::is_same<iterator, decltype(vec.begin())>::value, "iterator types do not match");
static_assert(std::is_same<iterator, decltype(vec.end())>::value, "iterator types do not match");
static_assert(std::is_same<const_iterator, decltype(constVec.begin())>::value, "iterator types do not match");
static_assert(std::is_same<const_iterator, decltype(constVec.end())>::value, "iterator types do not match");
}
TEST(StackVec, GivenStackVecsOfDifferentSizesWhenComparingThenEqualReturnsFalse) {
StackVec<int, 5> longer;
longer.resize(4, 0);
StackVec<int, 10> shorter;
shorter.resize(2, 0);
EXPECT_FALSE(longer == shorter);
EXPECT_FALSE(shorter == longer);
EXPECT_TRUE(longer != shorter);
EXPECT_TRUE(shorter != longer);
}
TEST(StackVec, GivenStackVecsContainingDifferentDataWhenComparingThenEqualReturnsFalse) {
char dataA[] = {0, 1, 3, 4, 5};
char dataB[] = {0, 1, 3, 5, 4};
StackVec<char, 10> vecA{dataA, dataA + sizeof(dataA)};
StackVec<char, 15> vecB{dataB, dataB + sizeof(dataB)};
EXPECT_FALSE(vecA == vecB);
EXPECT_TRUE(vecA != vecB);
}
TEST(StackVec, GivenStackVecsAreEmptyWhenComparingThenEqualReturnsTrue) {
StackVec<char, 10> vecA;
StackVec<char, 15> vecB;
EXPECT_TRUE(vecA == vecB);
EXPECT_FALSE(vecA != vecB);
}
TEST(StackVec, GivenStackVecsContainingSameDataWhenComparingThenEqualReturnsTrue) {
char dataA[] = {0, 1, 3, 4, 5};
char dataB[] = {0, 1, 3, 4, 5};
StackVec<char, 10> vecA{dataA, dataA + sizeof(dataA)};
StackVec<char, 15> vecB{dataB, dataB + sizeof(dataB)};
EXPECT_TRUE(vecA == vecB);
EXPECT_FALSE(vecA != vecB);
}
TEST(StackVec, WhenCallingDataThenVectorDataIsReturned) {
char dataA[] = {0, 1, 3, 4, 5};
char dataB[] = {5, 4, 3, 2, 1};
StackVec<char, 1> stackVecA{dataA, dataA + sizeof(dataA)};
StackVec<char, 5> stackVecB{dataB, dataB + sizeof(dataB)};
EXPECT_TRUE(stackVecA.usesDynamicMem());
EXPECT_FALSE(stackVecB.usesDynamicMem());
auto stackVecAData = reinterpret_cast<char *>(stackVecA.data());
auto stackVecBData = reinterpret_cast<char *>(stackVecB.data());
for (size_t i = 0; i < 5; i++) {
EXPECT_EQ(dataA[i], stackVecAData[i]);
EXPECT_EQ(dataB[i], stackVecBData[i]);
}
}
TEST(StackVec, GivenStackVecWithDynamicMemWhenSelfAssignedThenMemoryIsReused) {
char dataA[] = {0, 1, 3, 4, 5};
StackVec<char, 1> stackVecA{dataA, dataA + sizeof(dataA)};
EXPECT_TRUE(stackVecA.usesDynamicMem());
{
auto currentMemory = stackVecA.data();
auto currentSize = stackVecA.size();
auto &stackVecB = stackVecA;
stackVecA = stackVecB;
EXPECT_EQ(currentMemory, stackVecA.data());
EXPECT_EQ(currentSize, stackVecA.size());
stackVecA.push_back(7);
currentMemory = stackVecA.data();
EXPECT_EQ(7, currentMemory[5]);
for (size_t i = 0; i < 5; i++) {
EXPECT_EQ(dataA[i], currentMemory[i]);
}
}
{
auto currentMemory = stackVecA.data();
auto currentSize = stackVecA.size();
auto &stackVecB = stackVecA;
stackVecA = std::move(stackVecB);
EXPECT_EQ(currentMemory, stackVecA.data());
EXPECT_EQ(currentSize, stackVecA.size());
stackVecA.push_back(7);
currentMemory = stackVecA.data();
EXPECT_EQ(7, currentMemory[5]);
for (size_t i = 0; i < 5; i++) {
EXPECT_EQ(dataA[i], currentMemory[i]);
}
}
}
TEST(StackVec, GivenVectorWithDuplicatesWhenRemovingDuplicatesThenVectorIsSortedAndDuplicatesRemoved) {
StackVec<uint32_t, 8> stackVec = {6, 5, 4, 3, 2, 1, 5, 4, 5, 4, 2, 4, 3, 1, 6, 1};
ASSERT_EQ(stackVec.size(), 16u);
stackVec.remove_duplicates();
EXPECT_EQ(stackVec.size(), 6u);
const StackVec<uint32_t, 8> expectedStackVec = {1, 2, 3, 4, 5, 6};
EXPECT_EQ(stackVec, expectedStackVec);
}
TEST(StackVec, GivenVectorWithoutDuplicatesWhenRemovingDuplicatesThenVectorIsSorted) {
StackVec<uint32_t, 8> stackVec = {16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
ASSERT_EQ(stackVec.size(), 16u);
stackVec.remove_duplicates();
EXPECT_EQ(stackVec.size(), 16u);
const StackVec<uint32_t, 8> expectedStackVec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
EXPECT_EQ(stackVec, expectedStackVec);
}
TEST(StackVec, GivenSortedVectorWithoutDuplicatesWhenRemovingDuplicatesThenVectorIsUnchanged) {
StackVec<uint32_t, 8> stackVec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
ASSERT_EQ(stackVec.size(), 16u);
stackVec.remove_duplicates();
EXPECT_EQ(stackVec.size(), 16u);
const StackVec<uint32_t, 8> expectedStackVec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
EXPECT_EQ(stackVec, expectedStackVec);
}
int sum(ArrayRef<int> a) {
int sum = 0;
for (auto v : a) {
sum += v;
}
return sum;
}
TEST(ArrayRef, WhenArrayRefIsCreatedThenBehaviorIsCorrect) {
StackVec<int, 5> sv;
sv.push_back(0);
sv.push_back(1);
sv.push_back(2);
sv.push_back(3);
sv.push_back(4);
ASSERT_EQ(10, sum(sv));
ASSERT_EQ(10, sum(ArrayRef<int>(sv.begin(), sv.end())));
int carray[] = {5, 6, 7, 8, 9};
ASSERT_EQ(35, sum(carray));
ArrayRef<int> ar2;
EXPECT_TRUE(ar2.empty());
ASSERT_EQ(0U, ar2.size());
ASSERT_EQ(nullptr, ar2.begin());
ASSERT_EQ(nullptr, ar2.end());
ar2 = carray;
EXPECT_FALSE(ar2.empty());
ASSERT_EQ(sizeof(carray) / sizeof(carray[0]), ar2.size());
ASSERT_EQ(35, sum(ar2));
std::vector<int> stdv(ar2.begin(), ar2.end());
ASSERT_EQ(35, sum(stdv));
ArrayRef<int> ar1 = sv;
ar1.swap(ar2);
ASSERT_EQ(10, sum(ar2));
ASSERT_EQ(35, sum(ar1));
ASSERT_EQ(ar1.begin(), ((const ArrayRef<int> *)&ar1)->begin());
ASSERT_EQ(ar1.end(), ((const ArrayRef<int> *)&ar1)->end());
ASSERT_EQ(ar1.size(), stdv.size());
for (uint32_t i = 0; i < ar1.size(); ++i) {
ASSERT_EQ(ar1[i], stdv[i]);
}
ASSERT_EQ(&ar1[3], &((const ArrayRef<int> *)&ar1)->operator[](3));
sv.resize(30);
ASSERT_EQ(sv.capacity(), 30u);
ArrayRef<int> ar3{carray, sizeof(carray) / sizeof(carray[0])};
ASSERT_EQ(35, sum(ar3));
}
TEST(ArrayRef, GivenEmptyContainerThenArrayRefIsEmpty) {
StackVec<int, 5> stackVec;
ArrayRef<int> arrayRef(stackVec);
EXPECT_TRUE(arrayRef.empty());
const StackVec<int, 5> &constStackVec = stackVec;
ArrayRef<const int> constArrayRef(constStackVec);
EXPECT_TRUE(constArrayRef.empty());
}
TEST(ArrayRef, WhenImplicitlyCovertingToConstThenResultIsCorrect) {
int carray[] = {5, 6, 7, 8, 9};
ArrayRef<int> arrayRef(carray);
ArrayRef<const int> constArrayRef = arrayRef;
EXPECT_EQ(arrayRef.begin(), constArrayRef.begin());
EXPECT_EQ(arrayRef.end(), constArrayRef.end());
}
TEST(ArrayRef, WhenGettingIteratorThenIteratorTypeIsCorrect) {
struct S {
};
using ArrayT = ArrayRef<S>;
using iterator = ArrayT::iterator;
using const_iterator = ArrayT::const_iterator;
ArrayT array;
const ArrayT constArray;
static_assert(std::is_same<iterator, decltype(array.begin())>::value, "iterator types do not match");
static_assert(std::is_same<iterator, decltype(array.end())>::value, "iterator types do not match");
static_assert(std::is_same<const_iterator, decltype(constArray.begin())>::value, "iterator types do not match");
static_assert(std::is_same<const_iterator, decltype(constArray.end())>::value, "iterator types do not match");
}
TEST(ArrayRef, GivenArraysOfDifferentSizesWhenComparingThenEqualReturnsFalse) {
char data[] = {0, 1, 3, 4, 5};
ArrayRef<char> longer{data, sizeof(data)};
ArrayRef<char> shorter{data, sizeof(data) - 1};
EXPECT_FALSE(longer == shorter);
EXPECT_FALSE(shorter == longer);
}
TEST(ArrayRef, GivenArraysContainingDifferentDataWhenComparingThenEqualReturnsFalse) {
char dataA[] = {0, 1, 3, 4, 5};
char dataB[] = {0, 1, 3, 5, 4};
ArrayRef<char> arrayA{dataA, sizeof(dataA)};
ArrayRef<char> arrayB{dataB, sizeof(dataB)};
EXPECT_FALSE(arrayA == arrayB);
}
TEST(ArrayRef, GivenEmptyArraysWhenComparingThenEqualReturnsTrue) {
ArrayRef<char> arrayA;
ArrayRef<char> arrayB;
EXPECT_TRUE(arrayA == arrayB);
}
TEST(ArrayRef, GivenArraysContainingSameDataWhenComparingThenEqualReturnsTrue) {
char dataA[] = {0, 1, 3, 4, 5};
char dataB[] = {0, 1, 3, 4, 5};
ArrayRef<char> arrayA{dataA, sizeof(dataA)};
ArrayRef<char> arrayB{dataB, sizeof(dataB)};
EXPECT_TRUE(arrayA == arrayB);
}
TEST(Range, GivenRangeThenValidStandardIteratorsAreAvailable) {
int tab[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
Range<int> range = tab;
const Range<int> &constantRange = range;
Range<int> emptyRange{nullptr, 0};
EXPECT_EQ(0U, emptyRange.size());
EXPECT_TRUE(emptyRange.empty());
EXPECT_EQ(10U, constantRange.size());
EXPECT_FALSE(constantRange.empty());
auto rangeFwdIt = range.begin();
auto rangeFwdEnd = range.end();
auto rangeBackIt = range.rbegin();
auto rangeBackEnd = range.rend();
auto constantRangeFwdIt = constantRange.begin();
auto constantRangeFwdEnd = constantRange.end();
auto constantRangeBackIt = constantRange.rbegin();
auto constantRangeBackEnd = constantRange.rend();
for (int i = 0; i < 10; ++i, ++rangeFwdIt, ++rangeBackIt, ++constantRangeFwdIt, ++constantRangeBackIt) {
EXPECT_EQ(tab[i], *rangeFwdIt) << " it : " << i;
EXPECT_EQ(tab[i], *constantRangeFwdIt) << " it : " << i;
EXPECT_NE(rangeFwdEnd, rangeFwdIt) << " it : " << i;
EXPECT_NE(constantRangeFwdEnd, constantRangeFwdIt) << " it : " << i;
EXPECT_EQ(tab[10 - 1 - i], *rangeBackIt) << " it : " << i;
EXPECT_EQ(tab[10 - 1 - i], *constantRangeBackIt) << " it : " << i;
EXPECT_NE(rangeBackEnd, rangeBackIt) << " it : " << i;
EXPECT_NE(constantRangeBackEnd, constantRangeBackIt) << " it : " << i;
}
EXPECT_EQ(rangeFwdEnd, rangeFwdIt);
EXPECT_EQ(constantRangeFwdEnd, constantRangeFwdIt);
EXPECT_EQ(rangeBackEnd, rangeBackIt);
EXPECT_EQ(constantRangeBackEnd, constantRangeBackIt);
std::vector<int> vec(&tab[0], &tab[10]);
Range<int> rangeFromVec = vec;
EXPECT_EQ(&*vec.begin(), &*rangeFromVec.begin());
EXPECT_EQ(&*vec.rbegin(), &*rangeFromVec.rbegin());
}
TEST(ArrayRef, WhenFromAnyIsCalledThenPointerIsReinterpretedAndSizeIsAdjusted) {
uint32_t x[2] = {};
auto arrayRefU8 = ArrayRef<const uint8_t>::fromAny(x, 2);
EXPECT_EQ(8U, arrayRefU8.size());
EXPECT_EQ(reinterpret_cast<uint8_t *>(x), arrayRefU8.begin());
}
TEST(ArrayRef, WhenToArrayRefIsCalledThenPointerIsReinterpretedAndSizeIsAdjusted) {
uint32_t x[2] = {};
auto arrayRefU32 = ArrayRef<const uint32_t>::fromAny(x, 2);
auto arrayRefU8 = arrayRefU32.toArrayRef<const uint8_t>();
EXPECT_EQ(8U, arrayRefU8.size());
EXPECT_EQ(reinterpret_cast<uint8_t *>(x), arrayRefU8.begin());
}
TEST(ArrayRef, WhenClearedThenEmpty) {
uint32_t x[2] = {};
auto arrayRefU32 = ArrayRef<const uint32_t>::fromAny(x, 2);
arrayRefU32.clear();
EXPECT_TRUE(arrayRefU32.empty());
}