blob: 78443cbc3c2d1b31ef2ac682a13b4c1e2135ad06 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Quick 3D.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest>
#include <QtQuick3DUtils/private/qssginvasivelinkedlist_p.h>
struct NodeItem
{
NodeItem() = default;
NodeItem(int v, NodeItem *p, NodeItem *n) : value(v), prev(p), next(n) {}
int value = -1;
NodeItem *prev = nullptr;
NodeItem *next = nullptr;
};
using SingleLinkedList = QSSGInvasiveSingleLinkedList<NodeItem, &NodeItem::next>;
using LinkedList = QSSGInvasiveLinkedList<NodeItem, &NodeItem::prev, &NodeItem::next>;
class invasivelist : public QObject
{
Q_OBJECT
public:
invasivelist() = default;
~invasivelist() = default;
private slots:
void test_empty();
void test_push_backSll();
void test_push_frontSll();
void test_push_backLl();
void test_push_frontLl();
void test_removeSll();
void test_removeLl();
void test_insert_unsafe();
void test_insert_before();
void test_insert_after();
void test_copy_slList();
void test_copy_lList();
void test_iteratorSll();
void test_iteratorLl();
};
void invasivelist::test_empty()
{
SingleLinkedList sll;
QVERIFY(sll.empty());
LinkedList ll;
QVERIFY(ll.empty());
NodeItem node;
QCOMPARE(node.next, nullptr);
QCOMPARE(node.prev, nullptr);
sll.push_back(node);
QVERIFY(!sll.empty());
sll.remove(node);
// list should be empty and the node "links" reset
QVERIFY(sll.empty());
QCOMPARE(node.next, nullptr);
QCOMPARE(node.prev, nullptr);
ll.push_back(node);
QVERIFY(!ll.empty());
ll.remove(node);
// list should be empty and the node "links" reset
QVERIFY(ll.empty());
QCOMPARE(node.next, nullptr);
QCOMPARE(node.prev, nullptr);
}
void invasivelist::test_push_backSll()
{
const int itemCount = 100;
NodeItem items[itemCount];
SingleLinkedList list;
for (int i = 0; i != itemCount; ++i) {
items[i].value = i;
list.push_back(items[i]);
}
int lastValue = -1;
int count = 0;
for (const auto &item : qAsConst(list)) {
QVERIFY(item.value > lastValue);
lastValue = item.value;
++count;
}
QCOMPARE(lastValue, itemCount - 1);
QCOMPARE(count, itemCount);
}
void invasivelist::test_push_frontSll()
{
const int itemCount = 100;
NodeItem items[itemCount];
SingleLinkedList list;
for (int i = 0; i != itemCount; ++i) {
items[i].value = i;
list.push_front(items[i]);
}
int lastValue = 100;
int count = 0;
for (const auto &item : qAsConst(list)) {
QVERIFY(item.value < lastValue);
lastValue = item.value;
++count;
}
QCOMPARE(lastValue, 0);
QCOMPARE(count, itemCount);
}
void invasivelist::test_push_backLl()
{
const int itemCount = 100;
NodeItem items[itemCount];
LinkedList list;
for (int i = 0; i != itemCount; ++i) {
items[i].value = i;
list.push_back(items[i]);
}
int lastValue = -1;
int count = 0;
for (const auto &item : qAsConst(list)) {
QVERIFY(item.value > lastValue);
lastValue = item.value;
++count;
}
QCOMPARE(lastValue, itemCount - 1);
QCOMPARE(count, itemCount);
}
void invasivelist::test_push_frontLl()
{
const int itemCount = 100;
NodeItem items[itemCount];
LinkedList list;
for (int i = 0; i != itemCount; ++i) {
items[i].value = i;
list.push_front(items[i]);
}
int lastValue = 100;
int count = 0;
for (const auto &item : qAsConst(list)) {
QVERIFY(item.value < lastValue);
lastValue = item.value;
++count;
}
QCOMPARE(lastValue, 0);
QCOMPARE(count, itemCount);
}
void invasivelist::test_removeSll()
{
const int itemCount = 100;
NodeItem items[itemCount];
SingleLinkedList list;
for (int i = 0; i != itemCount; ++i) {
items[i].value = i;
list.push_back(items[i]);
}
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &items[1]);
list.remove(items[0]);
QCOMPARE(items[0].next, nullptr);
QCOMPARE(items[0].prev, nullptr);
list.push_front(items[0]);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &items[1]);
// Remove all
for (int i = 0; i != itemCount; ++i)
list.remove(items[i]);
QVERIFY(list.empty());
for (int i = 0; i != itemCount; ++i) {
QCOMPARE(items[i].next, nullptr);
QCOMPARE(items[i].prev, nullptr);
}
}
void invasivelist::test_removeLl()
{
const int itemCount = 100;
NodeItem items[itemCount];
LinkedList list;
for (int i = 0; i != itemCount; ++i) {
items[i].value = i;
list.push_back(items[i]);
}
// Remove first
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &items[1]);
QCOMPARE(items[1].prev, &items[0]);
QCOMPARE(items[1].next, &items[2]);
list.remove(items[0]);
QCOMPARE(items[0].next, nullptr);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[1].next, &items[2]);
QCOMPARE(items[1].prev, nullptr);
list.push_front(items[0]);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &items[1]);
// Remove !first
QCOMPARE(items[49].next, &items[50]);
QCOMPARE(items[51].prev, &items[50]);
list.remove(items[50]);
QCOMPARE(items[50].next, nullptr);
QCOMPARE(items[50].prev, nullptr);
QCOMPARE(items[49].next, &items[51]);
QCOMPARE(items[51].prev, &items[49]);
list.insert_after(items[49], items[50]);
// Remove all
for (int i = 0; i != itemCount; ++i)
list.remove(items[i]);
QVERIFY(list.empty());
for (int i = 0; i != itemCount; ++i) {
QCOMPARE(items[i].next, nullptr);
QCOMPARE(items[i].prev, nullptr);
}
}
// Shared between the two, so we don't need to duplcate these
void invasivelist::test_insert_unsafe()
{
{
const int itemCount = 2;
NodeItem items[itemCount] { NodeItem { 0, nullptr, nullptr },
NodeItem { 2, nullptr, nullptr } };
LinkedList list;
list.push_back(items[0]);
list.push_back(items[1]);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &items[1]);
QCOMPARE(items[1].prev, &items[0]);
QCOMPARE(items[1].next, nullptr);
NodeItem middle { 1, nullptr, nullptr };
list.insert_unsafe(&items[0], &items[1], middle);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &middle);
QCOMPARE(middle.prev, &items[0]);
QCOMPARE(middle.next, &items[1]);
QCOMPARE(items[1].prev, &middle);
QCOMPARE(items[1].next, nullptr);
}
{
const int itemCount = 3;
NodeItem items[itemCount] { NodeItem { 0, nullptr, nullptr },
NodeItem { -1, nullptr, nullptr },
NodeItem { 2, nullptr, nullptr } };
LinkedList list;
list.push_back(items[0]);
list.push_back(items[1]);
list.push_back(items[2]);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &items[1]);
QCOMPARE(items[1].prev, &items[0]);
QCOMPARE(items[1].next, &items[2]);
QCOMPARE(items[2].prev, &items[1]);
QCOMPARE(items[2].next, nullptr);
NodeItem middle { 1, nullptr, nullptr };
list.insert_unsafe(&items[0], &items[2], middle);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &middle);
QCOMPARE(middle.prev, &items[0]);
QCOMPARE(middle.next, &items[2]);
QCOMPARE(items[2].prev, &middle);
QCOMPARE(items[2].next, nullptr);
// item[1] is now danling, but the tail and head is
// still pointing to the old items
// (this is inteded, so test that it still works)
QCOMPARE(items[1].prev, &items[0]);
QCOMPARE(items[1].next, &items[2]);
}
}
void invasivelist::test_insert_before()
{
const int itemCount = 2;
NodeItem items[itemCount] { NodeItem { 1, nullptr, nullptr },
NodeItem { 4, nullptr, nullptr } };
LinkedList list;
list.push_back(items[0]);
list.push_back(items[1]);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &items[1]);
QCOMPARE(items[1].prev, &items[0]);
QCOMPARE(items[1].next, nullptr);
NodeItem newItems[] { NodeItem { 0, nullptr, nullptr },
NodeItem { 2, nullptr, nullptr },
NodeItem { 3, nullptr, nullptr }};
list.insert_before(items[0], newItems[0]);
QCOMPARE(newItems[0].prev, nullptr);
QCOMPARE(newItems[0].next, &items[0]);
QCOMPARE(items[0].prev, &newItems[0]);
QCOMPARE(items[0].next, &items[1]);
QCOMPARE(items[1].prev, &items[0]);
QCOMPARE(items[1].next, nullptr);
list.insert_before(items[1], newItems[1]);
QCOMPARE(newItems[0].prev, nullptr);
QCOMPARE(newItems[0].next, &items[0]);
QCOMPARE(items[0].prev, &newItems[0]);
QCOMPARE(items[0].next, &newItems[1]);
QCOMPARE(newItems[1].prev, &items[0]);
QCOMPARE(newItems[1].next, &items[1]);
QCOMPARE(items[1].prev, &newItems[1]);
QCOMPARE(items[1].next, nullptr);
list.insert_before(items[1], newItems[2]);
QCOMPARE(newItems[0].prev, nullptr);
QCOMPARE(newItems[0].next, &items[0]);
QCOMPARE(items[0].prev, &newItems[0]);
QCOMPARE(items[0].next, &newItems[1]);
QCOMPARE(newItems[1].prev, &items[0]);
QCOMPARE(newItems[1].next, &newItems[2]);
QCOMPARE(newItems[2].prev, &newItems[1]);
QCOMPARE(newItems[2].next, &items[1]);
QCOMPARE(items[1].prev, &newItems[2]);
QCOMPARE(items[1].next, nullptr);
}
void invasivelist::test_insert_after()
{
const int itemCount = 2;
NodeItem items[itemCount] { NodeItem { 0, nullptr, nullptr },
NodeItem { 3, nullptr, nullptr } };
LinkedList list;
list.push_back(items[0]);
list.push_back(items[1]);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &items[1]);
QCOMPARE(items[1].prev, &items[0]);
QCOMPARE(items[1].next, nullptr);
NodeItem newItems[] { NodeItem { 1, nullptr, nullptr },
NodeItem { 2, nullptr, nullptr },
NodeItem { 4, nullptr, nullptr }};
list.insert_after(items[0], newItems[0]);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &newItems[0]);
QCOMPARE(newItems[0].prev, &items[0]);
QCOMPARE(newItems[0].next, &items[1]);
QCOMPARE(items[1].prev, &newItems[0]);
QCOMPARE(items[1].next, nullptr);
list.insert_after(newItems[0], newItems[1]);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &newItems[0]);
QCOMPARE(newItems[0].prev, &items[0]);
QCOMPARE(newItems[0].next, &newItems[1]);
QCOMPARE(newItems[1].prev, &newItems[0]);
QCOMPARE(newItems[1].next, &items[1]);
QCOMPARE(items[1].prev, &newItems[1]);
QCOMPARE(items[1].next, nullptr);
list.insert_after(items[1], newItems[2]);
QCOMPARE(items[0].prev, nullptr);
QCOMPARE(items[0].next, &newItems[0]);
QCOMPARE(newItems[0].prev, &items[0]);
QCOMPARE(newItems[0].next, &newItems[1]);
QCOMPARE(newItems[1].prev, &newItems[0]);
QCOMPARE(newItems[1].next, &items[1]);
QCOMPARE(items[1].prev, &newItems[1]);
QCOMPARE(items[1].next, &newItems[2]);
QCOMPARE(newItems[2].prev, &items[1]);
QCOMPARE(newItems[2].next, nullptr);
}
void invasivelist::test_copy_slList()
{
const int itemCount = 2;
NodeItem items[itemCount] { NodeItem { 0, nullptr, nullptr },
NodeItem { 1, nullptr, nullptr } };
SingleLinkedList l1, l2;
QVERIFY(l1.empty());
QVERIFY(l2.empty());
l1.push_back(items[0]);
l1.push_back(items[1]);
QVERIFY(!l1.empty());
QVERIFY(l2.empty());
l2 = l1;
QVERIFY(!l1.empty());
QVERIFY(!l2.empty());
for (auto it1 = l1.begin(), it2 = l2.begin(); it1 != l1.end(); ++it1, ++it2)
QCOMPARE(it2, it1);
}
void invasivelist::test_copy_lList()
{
const int itemCount = 2;
NodeItem items[itemCount] { NodeItem { 0, nullptr, nullptr },
NodeItem { 1, nullptr, nullptr } };
LinkedList l1, l2;
QVERIFY(l1.empty());
QVERIFY(l2.empty());
l1.push_back(items[0]);
l1.push_back(items[1]);
QVERIFY(!l1.empty());
QVERIFY(l2.empty());
l2 = l1;
QVERIFY(!l1.empty());
QVERIFY(!l2.empty());
for (auto it1 = l1.begin(), it2 = l2.begin(); it1 != l1.end(); ++it1, ++it2)
QCOMPARE(it2, it1);
}
void invasivelist::test_iteratorSll()
{
const int itemCount = 100;
NodeItem items[itemCount];
SingleLinkedList list;
for (int i = 0; i != itemCount; ++i) {
items[i].value = i;
list.push_back(items[i]);
}
{
auto it = list.begin();
const auto end = list.end();
int lastValue = -1;
for (; it != end; ++it) {
QVERIFY(it->value > lastValue);
lastValue = it->value;
}
QCOMPARE(lastValue, itemCount - 1);
}
}
void invasivelist::test_iteratorLl()
{
const int itemCount = 100;
NodeItem items[itemCount];
LinkedList list;
for (int i = 0; i != itemCount; ++i) {
items[i].value = i;
list.push_back(items[i]);
}
{
auto it = list.begin();
const auto end = list.end();
int lastValue = -1;
for (; it != end; ++it) {
QVERIFY(it->value > lastValue);
lastValue = it->value;
}
QCOMPARE(lastValue, itemCount - 1);
}
{
auto rit = list.rbegin();
const auto rend = list.rend();
int lastValue = itemCount;
for (; rit != rend; ++rit) {
QVERIFY(rit->value < lastValue);
lastValue = rit->value;
}
QCOMPARE(lastValue, 0);
}
}
QTEST_APPLESS_MAIN(invasivelist)
#include "tst_invasivelist.moc"