1645 lines
48 KiB
C++
1645 lines
48 KiB
C++
/*
|
|
* Copyright (c) 2018, Intel Corporation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "runtime/utilities/arrayref.h"
|
|
#include "runtime/utilities/idlist.h"
|
|
#include "runtime/utilities/iflist.h"
|
|
#include "runtime/utilities/stackvec.h"
|
|
#include "unit_tests/utilities/containers_tests_helpers.h"
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <cinttypes>
|
|
#include <memory>
|
|
#include <numeric>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
using namespace OCLRT;
|
|
|
|
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() {
|
|
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, insertOne) {
|
|
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, insertAllNext) {
|
|
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, deleteThisAndAllNext) {
|
|
DummyFNode *list[7];
|
|
uint32_t destructorCounter = 0;
|
|
makeList(list, &destructorCounter);
|
|
list[0]->deleteThisAndAllNext();
|
|
ASSERT_EQ(sizeof(list) / sizeof(list[0]), destructorCounter);
|
|
}
|
|
|
|
TEST(IFNode, getTail) {
|
|
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, slice) {
|
|
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, countSuccessors) {
|
|
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, verifySequence) {
|
|
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, pushFrontOneThreadSafe) {
|
|
iFListTestPushFrontOne<true>();
|
|
}
|
|
|
|
TEST(IFList, pushFrontOneNonThreadSafe) {
|
|
iFListTestPushFrontOne<false>();
|
|
}
|
|
|
|
TEST(IFList, ownNodesDeletion) {
|
|
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, spliceAndDeleteAll) {
|
|
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, detachNodesThreadSafe) {
|
|
iFListTestDetachNodes<true>();
|
|
}
|
|
|
|
TEST(IFList, detachNodesNonThreadSafe) {
|
|
iFListTestDetachNodes<false>();
|
|
}
|
|
|
|
TEST(IFList, compareExchangeHead) {
|
|
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, pushFrontOneThreadSafe) {
|
|
iFRefListTestPushFrontOne<true>();
|
|
}
|
|
|
|
TEST(IFRefList, pushFrontOneNonThreadSafe) {
|
|
iFRefListTestPushFrontOne<false>();
|
|
}
|
|
|
|
TEST(IDNode, insertOne) {
|
|
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, insertAllNext) {
|
|
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, deleteThisAndAllNext) {
|
|
DummyFNode *list[7];
|
|
uint32_t destructorCounter = 0;
|
|
makeList(list, &destructorCounter);
|
|
list[0]->deleteThisAndAllNext();
|
|
ASSERT_EQ(sizeof(list) / sizeof(list[0]), destructorCounter);
|
|
}
|
|
|
|
TEST(IDNode, deleteThisAndAllPrev) {
|
|
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, deleteThisAndAllConnected) {
|
|
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, getHeadAndTail) {
|
|
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, slice) {
|
|
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, countConnected) {
|
|
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, isPredecessorSuccessorOrConnected) {
|
|
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, verifySequence) {
|
|
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, pushOneThreadSafe) {
|
|
iDListTestPushOne<true>();
|
|
}
|
|
|
|
TEST(IDList, pushOneNonThreadSafe) {
|
|
iDListTestPushOne<false>();
|
|
}
|
|
|
|
TEST(IDList, ownNodesDeletion) {
|
|
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, spliceAndDeleteAllThreadSafe) {
|
|
iDListSpliceAndDeleteAll<true>();
|
|
}
|
|
|
|
TEST(IDList, spliceAndDeleteAllNonThreadSafe) {
|
|
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, detachNodesThreadSafe) {
|
|
iDListTestDetachNodes<true>();
|
|
}
|
|
|
|
TEST(IDList, detachNodesNonThreadSafe) {
|
|
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, removeOneThreadSafe) {
|
|
iDListTestRemoveOne<true>();
|
|
}
|
|
|
|
TEST(IDList, removeOneNonThreadSafe) {
|
|
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, removeFrontOneThreadSafe) {
|
|
iDListTestRemoveFrontOne<true>();
|
|
}
|
|
|
|
TEST(IDList, removeFrontOneNonThreadSafe) {
|
|
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_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, detachSequenceThreadSafe) {
|
|
iDListTestDetachSequence<true>();
|
|
}
|
|
|
|
TEST(IDList, detachSequenceNonThreadSafe) {
|
|
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, peekContainsThreadSafe) {
|
|
iDListTestPeekContains<true>();
|
|
}
|
|
|
|
TEST(IDList, peekContainsNonThreadSafe) {
|
|
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;
|
|
bool caughtEx = false;
|
|
try {
|
|
l.throwExFromLock();
|
|
} catch (const ExType &) {
|
|
caughtEx = true;
|
|
}
|
|
|
|
EXPECT_TRUE(caughtEx);
|
|
EXPECT_FALSE(l.getLockedRef().test_and_set(std::memory_order_seq_cst));
|
|
}
|
|
|
|
TEST(IDList, InsideLockWhenExceptionIsThrownThenUnlocksBeforeRethrowingException) {
|
|
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, pushFrontOneThreadSafe) {
|
|
iDRefListTestPushFrontOne<true>();
|
|
}
|
|
|
|
TEST(IDRefList, pushFrontOneNonThreadSafe) {
|
|
iDRefListTestPushFrontOne<false>();
|
|
}
|
|
|
|
TEST(IDRefList, recursiveLock) {
|
|
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, Constructor) {
|
|
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()));
|
|
}
|
|
|
|
TEST(StackVec, ConstructorWithInitialSizeGetsResizedAutomaticallyDuringConstruction) {
|
|
StackVec<int, 5> vec1(10);
|
|
EXPECT_EQ(10U, vec1.size());
|
|
}
|
|
|
|
TEST(StackVec, CopyConstructor) {
|
|
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, CopyAsignment) {
|
|
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, MoveConstructor) {
|
|
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, MoveAsignment) {
|
|
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, Alignment) {
|
|
struct alignas(16) S16 {
|
|
int a;
|
|
};
|
|
struct alignas(32) S32 {
|
|
int a;
|
|
};
|
|
struct alignas(64) S64 {
|
|
int a;
|
|
};
|
|
|
|
StackVec<S32, 4> s32;
|
|
StackVec<S16, 4> s16;
|
|
StackVec<S64, 4> s64;
|
|
s16.push_back(S16{});
|
|
s32.push_back(S32{});
|
|
s64.push_back(S64{});
|
|
|
|
EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(&*s16.begin()) % 16);
|
|
EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(&*s32.begin()) % 32);
|
|
EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(&*s64.begin()) % 64);
|
|
}
|
|
|
|
TEST(StackVec, PushBack) {
|
|
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, Reserve) {
|
|
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, Resize) {
|
|
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, Iterators) {
|
|
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, Clear) {
|
|
uint32_t destructorCounter = 0;
|
|
DummyFNode nd1(&destructorCounter);
|
|
DummyFNode nd2(&destructorCounter);
|
|
DummyFNode nd3(&destructorCounter);
|
|
StackVec<DummyFNode, 3> v;
|
|
v.push_back(nd1);
|
|
v.push_back(nd2);
|
|
v.push_back(nd3);
|
|
ASSERT_EQ(0U, destructorCounter);
|
|
ASSERT_EQ(3U, v.size());
|
|
v.clear();
|
|
ASSERT_EQ(3U, destructorCounter);
|
|
ASSERT_EQ(0U, v.size());
|
|
|
|
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, ConstMemberFunctions) {
|
|
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, ComplexElements) {
|
|
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, DefinesIteratorTypes) {
|
|
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, EqualsOperatorReturnsFalseIfStackVecsHaveDifferentSizes) {
|
|
StackVec<int, 5> longer;
|
|
longer.resize(4, 0);
|
|
|
|
StackVec<int, 10> shorter;
|
|
shorter.resize(2, 0);
|
|
|
|
EXPECT_FALSE(longer == shorter);
|
|
EXPECT_FALSE(shorter == longer);
|
|
}
|
|
|
|
TEST(StackVec, EqualsOperatorReturnsFalseIfDataNotEqual) {
|
|
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);
|
|
}
|
|
|
|
TEST(StackVec, EqualsOperatorReturnsTrueIfBothContainersAreEmpty) {
|
|
StackVec<char, 10> vecA;
|
|
StackVec<char, 15> vecB;
|
|
|
|
EXPECT_TRUE(vecA == vecB);
|
|
}
|
|
|
|
TEST(StackVec, EqualsOperatorReturnsTrueIfDataIsEqual) {
|
|
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);
|
|
}
|
|
|
|
int sum(ArrayRef<int> a) {
|
|
int sum = 0;
|
|
for (auto v : a) {
|
|
sum += v;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
TEST(ArrayRef, WrapContainers) {
|
|
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;
|
|
ASSERT_EQ(0U, ar2.size());
|
|
ASSERT_EQ(nullptr, ar2.begin());
|
|
ASSERT_EQ(nullptr, ar2.end());
|
|
|
|
ar2 = carray;
|
|
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, ImplicitCoversionToArrayrefOfConst) {
|
|
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, DefinesIteratorTypes) {
|
|
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, EqualsOperatorReturnsFalseIfArraysReferenceContaintersOfDifferentLenghts) {
|
|
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, EqualsOperatorReturnsFalseIfDataNotEqual) {
|
|
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, EqualsOperatorReturnsTrueIfBothContainersAreEmpty) {
|
|
ArrayRef<char> arrayA;
|
|
ArrayRef<char> arrayB;
|
|
|
|
EXPECT_TRUE(arrayA == arrayB);
|
|
}
|
|
|
|
TEST(ArrayRef, EqualsOperatorReturnsTrueIfDataIsEqual) {
|
|
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);
|
|
}
|