/* * Copyright (C) 2018-2020 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 "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include #include #include using namespace NEO; struct DummyFNode : IFNode { DummyFNode(uint32_t *destructorsCounter = nullptr) : destructorsCounter(destructorsCounter) { } ~DummyFNode() { if (destructorsCounter != nullptr) { ++(*destructorsCounter); } } void setPrev(DummyFNode *) { } uint32_t *destructorsCounter; }; struct DummyDNode : IDNode { DummyDNode(uint32_t *destructorsCounter = nullptr) : destructorsCounter(destructorsCounter) { } ~DummyDNode() override { if (destructorsCounter != nullptr) { ++(*destructorsCounter); } } void setPrev(DummyDNode *nd) { prev = nd; } uint32_t *destructorsCounter; }; template 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 void iFListTestPushFrontOne() { IFList 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(); } TEST(IFList, pushFrontOneNonThreadSafe) { iFListTestPushFrontOne(); } TEST(IFList, ownNodesDeletion) { DummyFNode *nodes[7]; uint32_t destructorCounter = 0; makeList(nodes, &destructorCounter); auto list = std::unique_ptr>(new IFList(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 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 void iFListTestDetachNodes() { IFList 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(); } TEST(IFList, detachNodesNonThreadSafe) { iFListTestDetachNodes(); } TEST(IFList, compareExchangeHead) { struct DummyList : IFList { 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 void iFRefListTestPushFrontOne() { auto list = std::unique_ptr>(new IFRefList()); 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(); } TEST(IFRefList, pushFrontOneNonThreadSafe) { iFRefListTestPushFrontOne(); } 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 void iDListTestPushOne() { IDList 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(); } TEST(IDList, pushOneNonThreadSafe) { iDListTestPushOne(); } TEST(IDList, ownNodesDeletion) { DummyDNode *nodes[7]; uint32_t destructorCounter = 0; makeList(nodes, &destructorCounter); auto list = std::unique_ptr>(new IDList(nodes[0])); ASSERT_FALSE(list->peekIsEmpty()); ASSERT_EQ(nodes[0], list->peekHead()); list.reset(); ASSERT_EQ(sizeof(nodes) / sizeof(nodes[0]), destructorCounter); } template void iDListSpliceAndDeleteAll() { DummyDNode *nodes[7]; DummyDNode *nodes2[sizeof(nodes) / sizeof(nodes[0])]; uint32_t destructorCounter = 0; makeList(nodes, &destructorCounter); makeList(nodes2, &destructorCounter); IDList 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(); } TEST(IDList, spliceAndDeleteAllNonThreadSafe) { iDListSpliceAndDeleteAll(); } template void iDListTestDetachNodes() { IDList 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(); } TEST(IDList, detachNodesNonThreadSafe) { iDListTestDetachNodes(); } template void iDListTestRemoveOne() { IDList 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(); } TEST(IDList, removeOneNonThreadSafe) { iDListTestRemoveOne(); } template void iDListTestRemoveFrontOne() { IDList 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(); } TEST(IDList, removeFrontOneNonThreadSafe) { iDListTestRemoveFrontOne(); } template void iDListTestDetachSequence() { DummyDNode *nodes[10]; makeList(nodes); IDList 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); // NOLINT(clang-analyzer-core.NonNullParamChecker) 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(); } TEST(IDList, detachSequenceNonThreadSafe) { iDListTestDetachSequence(); } template void iDListTestPeekContains() { IDList 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(); } TEST(IDList, peekContainsNonThreadSafe) { iDListTestPeekContains(); } template void testIDListSpinlock() { struct ListMock : IDList { std::atomic_flag &getLockedRef() { return this->locked; } std::atomic &getLockOwnerRef() { return this->lockOwner; } using ListenerT = void (*)(IDList &list); ListenerT &getSpinLockedListenerRef() { return this->spinLockedListener; } int32_t lockLoopCount = 0; static void listener(IDList &list) { ListMock &l = reinterpret_cast(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(); testIDListSpinlock(); } template void testIDListUnlockOnException() { using ExType = std::runtime_error; struct ListMock : IDList { void throwExFromLock() { this->template processLocked(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(); testIDListUnlockOnException(); } template void iDRefListTestPushFrontOne() { auto list = std::unique_ptr>(new IDRefList()); 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(); } TEST(IDRefList, pushFrontOneNonThreadSafe) { iDRefListTestPushFrontOne(); } TEST(IDRefList, recursiveLock) { class RecursiveLockList : public IDList { public: void execute() { processLocked(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 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 src(srcSize); for (uint32_t i = 0; i < srcSize; ++i) { src[i] = i; } ASSERT_GE(sizeof(StackVec), sizeof(Type) * srcSize); StackVec bigger(src.begin(), src.end()); StackVec exact(src.begin(), src.end()); StackVec 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 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, ConstructorWithInitialSizeGetsResizedAutomaticallyDuringConstruction) { StackVec 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 src; Type refData[sizeBigger]; for (uint32_t i = 0; i < sc; ++i) { refData[i] = i * it; src.push_back(refData[i]); } StackVec 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 src; Type refData[sizeBigger]; for (uint32_t i = 0; i < srcSc; ++i) { refData[i] = i * it; src.push_back(refData[i]); } StackVec 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 src; Type refData[sizeBigger]; for (uint32_t i = 0; i < sc; ++i) { refData[i] = i * it; src.push_back(refData[i]); } StackVec 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 src; Type refData[sizeBigger]; for (uint32_t i = 0; i < srcSc; ++i) { refData[i] = i * it; src.push_back(refData[i]); } StackVec 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) { StackVec s16; StackVec s32; StackVec 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(s16.begin()) % 2); EXPECT_EQ(0U, reinterpret_cast(s32.begin()) % 4); EXPECT_EQ(0U, reinterpret_cast(s64.begin()) % 8); } TEST(StackVec, PushBack) { using Type = uint32_t; StackVec 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 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 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 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 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 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, ReverseBeginningFunctions) { using VecType = StackVec; 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, ConstMemberFunctions) { using VecType = StackVec; 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 src(100, 5); static const int elementsCount = 10; auto v = std::unique_ptr, elementsCount>>(new StackVec, elementsCount>()); for (int i = 0; i < elementsCount * 2; ++i) { v->push_back(src); } v.reset(); } TEST(StackVec, DefinesIteratorTypes) { struct S { }; using StackVecT = StackVec; using iterator = StackVecT::iterator; using const_iterator = StackVecT::const_iterator; StackVecT vec; const StackVecT &constVec = vec; static_assert(std::is_same::value, "iterator types do not match"); static_assert(std::is_same::value, "iterator types do not match"); static_assert(std::is_same::value, "iterator types do not match"); static_assert(std::is_same::value, "iterator types do not match"); } TEST(StackVec, EqualsOperatorReturnsFalseIfStackVecsHaveDifferentSizes) { StackVec longer; longer.resize(4, 0); StackVec shorter; shorter.resize(2, 0); EXPECT_FALSE(longer == shorter); EXPECT_FALSE(shorter == longer); EXPECT_TRUE(longer != shorter); EXPECT_TRUE(shorter != longer); } TEST(StackVec, EqualsOperatorReturnsFalseIfDataNotEqual) { char dataA[] = {0, 1, 3, 4, 5}; char dataB[] = {0, 1, 3, 5, 4}; StackVec vecA{dataA, dataA + sizeof(dataA)}; StackVec vecB{dataB, dataB + sizeof(dataB)}; EXPECT_FALSE(vecA == vecB); EXPECT_TRUE(vecA != vecB); } TEST(StackVec, EqualsOperatorReturnsTrueIfBothContainersAreEmpty) { StackVec vecA; StackVec vecB; EXPECT_TRUE(vecA == vecB); EXPECT_FALSE(vecA != vecB); } TEST(StackVec, EqualsOperatorReturnsTrueIfDataIsEqual) { char dataA[] = {0, 1, 3, 4, 5}; char dataB[] = {0, 1, 3, 4, 5}; StackVec vecA{dataA, dataA + sizeof(dataA)}; StackVec vecB{dataB, dataB + sizeof(dataB)}; EXPECT_TRUE(vecA == vecB); EXPECT_FALSE(vecA != vecB); } int sum(ArrayRef a) { int sum = 0; for (auto v : a) { sum += v; } return sum; } TEST(ArrayRef, WrapContainers) { StackVec 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(sv.begin(), sv.end()))); int carray[] = {5, 6, 7, 8, 9}; ASSERT_EQ(35, sum(carray)); ArrayRef 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 stdv(ar2.begin(), ar2.end()); ASSERT_EQ(35, sum(stdv)); ArrayRef ar1 = sv; ar1.swap(ar2); ASSERT_EQ(10, sum(ar2)); ASSERT_EQ(35, sum(ar1)); ASSERT_EQ(ar1.begin(), ((const ArrayRef *)&ar1)->begin()); ASSERT_EQ(ar1.end(), ((const ArrayRef *)&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 *)&ar1)->operator[](3)); sv.resize(30); ASSERT_EQ(sv.capacity(), 30u); ArrayRef ar3{carray, sizeof(carray) / sizeof(carray[0])}; ASSERT_EQ(35, sum(ar3)); } TEST(ArrayRef, GivenEmptyContainerThenArrayRefIsEmpty) { StackVec stackVec; ArrayRef arrayRef(stackVec); EXPECT_TRUE(arrayRef.empty()); const StackVec &constStackVec = stackVec; ArrayRef constArrayRef(constStackVec); EXPECT_TRUE(constArrayRef.empty()); } TEST(ArrayRef, ImplicitCoversionToArrayrefOfConst) { int carray[] = {5, 6, 7, 8, 9}; ArrayRef arrayRef(carray); ArrayRef constArrayRef = arrayRef; EXPECT_EQ(arrayRef.begin(), constArrayRef.begin()); EXPECT_EQ(arrayRef.end(), constArrayRef.end()); } TEST(ArrayRef, DefinesIteratorTypes) { struct S { }; using ArrayT = ArrayRef; using iterator = ArrayT::iterator; using const_iterator = ArrayT::const_iterator; ArrayT array; const ArrayT constArray; static_assert(std::is_same::value, "iterator types do not match"); static_assert(std::is_same::value, "iterator types do not match"); static_assert(std::is_same::value, "iterator types do not match"); static_assert(std::is_same::value, "iterator types do not match"); } TEST(ArrayRef, EqualsOperatorReturnsFalseIfArraysReferenceContaintersOfDifferentLenghts) { char data[] = {0, 1, 3, 4, 5}; ArrayRef longer{data, sizeof(data)}; ArrayRef 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 arrayA{dataA, sizeof(dataA)}; ArrayRef arrayB{dataB, sizeof(dataB)}; EXPECT_FALSE(arrayA == arrayB); } TEST(ArrayRef, EqualsOperatorReturnsTrueIfBothContainersAreEmpty) { ArrayRef arrayA; ArrayRef arrayB; EXPECT_TRUE(arrayA == arrayB); } TEST(ArrayRef, EqualsOperatorReturnsTrueIfDataIsEqual) { char dataA[] = {0, 1, 3, 4, 5}; char dataB[] = {0, 1, 3, 4, 5}; ArrayRef arrayA{dataA, sizeof(dataA)}; ArrayRef 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 range = tab; const Range &constantRange = range; Range 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 vec(&tab[0], &tab[10]); Range rangeFromVec = vec; EXPECT_EQ(&*vec.begin(), &*rangeFromVec.begin()); EXPECT_EQ(&*vec.rbegin(), &*rangeFromVec.rbegin()); } TEST(ArrayRef, WhenFromAnyIsCalledThenPointerIsReinterpretedAndSizeIsAdjusted) { uint32_t x[2] = {}; auto arrayRefU8 = ArrayRef::fromAny(x, 2); EXPECT_EQ(8U, arrayRefU8.size()); EXPECT_EQ(reinterpret_cast(x), arrayRefU8.begin()); } TEST(ArrayRef, WhenToArrayRefIsCalledThenPointerIsReinterpretedAndSizeIsAdjusted) { uint32_t x[2] = {}; auto arrayRefU32 = ArrayRef::fromAny(x, 2); auto arrayRefU8 = arrayRefU32.toArrayRef(); EXPECT_EQ(8U, arrayRefU8.size()); EXPECT_EQ(reinterpret_cast(x), arrayRefU8.begin()); } TEST(ArrayRef, WhenClearedThenEmpty) { uint32_t x[2] = {}; auto arrayRefU32 = ArrayRef::fromAny(x, 2); arrayRefU32.clear(); EXPECT_TRUE(arrayRefU32.empty()); }