diff options
Diffstat (limited to 'tests/auto/corelib/tools')
157 files changed, 18119 insertions, 8735 deletions
diff --git a/tests/auto/corelib/tools/CMakeLists.txt b/tests/auto/corelib/tools/CMakeLists.txt new file mode 100644 index 0000000000..5cca2e2df6 --- /dev/null +++ b/tests/auto/corelib/tools/CMakeLists.txt @@ -0,0 +1,58 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +add_subdirectory(qatomicscopedvaluerollback) +if(NOT INTEGRITY) + add_subdirectory(collections) +endif() +add_subdirectory(containerapisymmetry) +add_subdirectory(qalgorithms) +add_subdirectory(qarraydata) +add_subdirectory(qbitarray) +add_subdirectory(qcache) +add_subdirectory(qcommandlineparser) +add_subdirectory(qcontiguouscache) +add_subdirectory(qcryptographichash) +add_subdirectory(qduplicatetracker) +add_subdirectory(qeasingcurve) +add_subdirectory(qexplicitlyshareddatapointer) +add_subdirectory(qflatmap) +if(QT_FEATURE_private_tests) + add_subdirectory(qfreelist) +endif() +add_subdirectory(qhash) +add_subdirectory(qhashfunctions) +add_subdirectory(qhashseed) +add_subdirectory(qline) +add_subdirectory(qlist) +add_subdirectory(qmakearray) +add_subdirectory(qmap) +add_subdirectory(qmargins) +add_subdirectory(qmessageauthenticationcode) +if(NOT INTEGRITY) + add_subdirectory(qoffsetstringarray) +endif() +add_subdirectory(qpair) +add_subdirectory(qpoint) +add_subdirectory(qpointf) +add_subdirectory(qqueue) +add_subdirectory(qrect) +add_subdirectory(qringbuffer) +add_subdirectory(qscopedpointer) +add_subdirectory(qscopedvaluerollback) +add_subdirectory(qscopeguard) +add_subdirectory(qtaggedpointer) +add_subdirectory(qtyperevision) +add_subdirectory(qset) +add_subdirectory(qsharedpointer) +add_subdirectory(qsize) +add_subdirectory(qsizef) +add_subdirectory(qspan) +add_subdirectory(qstl) +add_subdirectory(quniquehandle) +add_subdirectory(qvarlengtharray) +add_subdirectory(qversionnumber) +add_subdirectory(qtimeline) +if(APPLE) + add_subdirectory(qmacautoreleasepool) +endif() diff --git a/tests/auto/corelib/tools/collections/CMakeLists.txt b/tests/auto/corelib/tools/collections/CMakeLists.txt new file mode 100644 index 0000000000..687d88b2e4 --- /dev/null +++ b/tests/auto/corelib/tools/collections/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_collections Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_collections LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_collections + SOURCES + tst_collections.cpp +) + +qt_internal_undefine_global_definition(tst_collections QT_NO_JAVA_STYLE_ITERATORS) diff --git a/tests/auto/corelib/tools/collections/collections.pro b/tests/auto/corelib/tools/collections/collections.pro deleted file mode 100644 index d5e4e226b5..0000000000 --- a/tests/auto/corelib/tools/collections/collections.pro +++ /dev/null @@ -1,8 +0,0 @@ -CONFIG += testcase -TARGET = tst_collections -SOURCES += tst_collections.cpp -QT = core testlib - -# This test does not work with strict iterators -DEFINES -= QT_NO_LINKED_LIST -DEFINES -= QT_NO_JAVA_STYLE_ITERATORS diff --git a/tests/auto/corelib/tools/collections/tst_collections.cpp b/tests/auto/corelib/tools/collections/tst_collections.cpp index 023b22b4ba..2fab6cae3a 100644 --- a/tests/auto/corelib/tools/collections/tst_collections.cpp +++ b/tests/auto/corelib/tools/collections/tst_collections.cpp @@ -1,38 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#undef QT_NO_FOREACH // this file tests Q_FOREACH over containers (centralize in a tst_qforeach?) // test the container forwards #include <QtContainerFwd> static QCache<int, int> *cacheX; static QHash<int, int> *hashX; -static QLinkedList<int> *linkedListX; static QList<int> *listX; static QMap<int, int> *mapX; static QMultiHash<int, int> *multiHashX; @@ -43,13 +18,12 @@ static QSet<int> *setX; static QStack<int> *stackX; static QVarLengthArray<int> *varLengthArrayX; static QVarLengthArray<int, 512> *varLengthArrayY; -static QVector<int> *vectorX; +static QList<int> *vectorX; void foo() { cacheX = 0; hashX = 0; - linkedListX = 0; listX = 0; mapX = 0; multiHashX = 0; @@ -63,7 +37,11 @@ void foo() vectorX = 0; } -#include <QtTest/QtTest> +#include <QTest> +#include <QVector> +#include <QScopedPointer> +#include <QThread> +#include <QSemaphore> #include <algorithm> @@ -71,7 +49,6 @@ void foo() #include "qbytearray.h" #include "qcache.h" #include "qhash.h" -#include "qlinkedlist.h" #include "qlist.h" #include "qmap.h" #include "qpair.h" @@ -81,7 +58,6 @@ void foo() #include "qstring.h" #include "qstringlist.h" #include "qvarlengtharray.h" -#include "qvector.h" #include "qqueue.h" class tst_Collections : public QObject @@ -92,7 +68,6 @@ private slots: void typeinfo(); void qstring(); void list(); - void linkedList(); void vector(); void byteArray(); void stack(); @@ -105,7 +80,6 @@ private slots: #endif void pair(); void sharableQList(); - void sharableQLinkedList(); void sharableQVector(); void sharableQMap(); void sharableQHash(); @@ -117,8 +91,6 @@ private slots: void vector_stl(); void list_stl_data(); void list_stl(); - void linkedlist_stl_data(); - void linkedlist_stl(); void q_init(); void pointersize(); void containerInstantiation(); @@ -130,12 +102,27 @@ private slots: void foreach_2(); void insert_remove_loop(); + + void detachAssociativeContainerQMap() { detachAssociativeContainerImpl<QMap>(); } + void detachAssociativeContainerQMultiMap() { detachAssociativeContainerImpl<QMultiMap>(); } + void detachAssociativeContainerQHash() { detachAssociativeContainerImpl<QHash>(); } + void detachAssociativeContainerQMultiHash() { detachAssociativeContainerImpl<QMultiHash>(); } + +private: + template <template<typename, typename> typename Container> + void detachAssociativeContainerImpl(); }; struct LargeStatic { static int count; LargeStatic():c(count) { ++count; } LargeStatic(const LargeStatic& o):c(o.c) { ++count; } + LargeStatic &operator=(const LargeStatic &o) + { + c = o.c; + ++count; + return *this; + }; ~LargeStatic() { --count; } int c; int data[8]; @@ -153,7 +140,7 @@ struct Movable { int Movable::count = 0; QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(Movable, Q_RELOCATABLE_TYPE); QT_END_NAMESPACE @@ -161,10 +148,155 @@ struct Pod { int i1, i2; }; +// Compile-time checks for recursive containers +struct Dummy +{ + bool operator==(const Dummy &) const { return false; } + bool operator<(const Dummy &) const { return false; } +}; + +struct RecursiveList : public QList<RecursiveList> {}; +struct RecursiveSet : public QSet<RecursiveSet> {}; +struct RecursiveMapV : public QMap<Dummy, RecursiveMapV> {}; +struct RecursiveMapK : public QMap<RecursiveMapK, Dummy> {}; +struct RecursiveMultiMapV : public QMultiMap<Dummy, RecursiveMultiMapV> {}; +struct RecursiveMultiMapK : public QMultiMap<RecursiveMultiMapK, Dummy> {}; +struct RecursiveHashV : public QHash<Dummy, RecursiveHashV> {}; +struct RecursiveHashK : public QHash<RecursiveHashK, Dummy> {}; +struct RecursiveMultiHashV : public QMultiHash<Dummy, RecursiveMultiHashV> {}; +struct RecursiveMultiHashK : public QMultiHash<RecursiveMultiHashK, Dummy> {}; + +struct Empty {}; +struct NoCmpParamRecursiveMapV : public QMap<Empty, NoCmpParamRecursiveMapV> {}; +struct NoCmpParamRecursiveMapK : public QMap<NoCmpParamRecursiveMapK, Empty> {}; +struct NoCmpParamRecursiveMultiMapV : public QMultiMap<Empty, NoCmpParamRecursiveMultiMapV> {}; +struct NoCmpParamRecursiveMultiMapK : public QMultiMap<NoCmpParamRecursiveMultiMapK, Empty> {}; +struct NoCmpParamRecursiveHashV : public QHash<Empty, NoCmpParamRecursiveHashV> {}; +struct NoCmpParamRecursiveHashK : public QHash<NoCmpParamRecursiveHashK, Empty> {}; +struct NoCmpParamRecursiveMultiHashV : public QMultiHash<Empty, NoCmpParamRecursiveMultiHashV> {}; +struct NoCmpParamRecursiveMultiHashK : public QMultiHash<NoCmpParamRecursiveMultiHashK, Empty> {}; + +struct NoCmpRecursiveList : public QList<NoCmpRecursiveList> +{ + bool operator==(const RecursiveList &) const = delete; + bool operator<(const RecursiveList &) const = delete; +}; +struct NoCmpRecursiveSet : public QSet<NoCmpRecursiveSet> +{ + bool operator==(const NoCmpRecursiveSet &) const = delete; +}; +struct NoCmpRecursiveMapV : public QMap<Dummy, NoCmpRecursiveMapV> +{ + bool operator==(const NoCmpRecursiveMapV &) const = delete; +}; +struct NoCmpRecursiveMapK : public QMap<NoCmpRecursiveMapK, Dummy> +{ + bool operator==(const NoCmpRecursiveMapK &) const = delete; +}; +struct NoCmpRecursiveMultiMapV : public QMultiMap<Dummy, NoCmpRecursiveMultiMapV> +{ + bool operator==(const NoCmpRecursiveMultiMapV &) const = delete; +}; +struct NoCmpRecursiveMultiMapK : public QMultiMap<NoCmpRecursiveMultiMapK, Dummy> +{ + bool operator==(const NoCmpRecursiveMultiMapK &) const = delete; +}; +struct NoCmpRecursiveHashV : public QHash<Dummy, NoCmpRecursiveHashV> +{ + bool operator==(const NoCmpRecursiveHashV &) const = delete; +}; +struct NoCmpRecursiveHashK : public QHash<NoCmpRecursiveHashK, Dummy> +{ + bool operator==(const NoCmpRecursiveHashK &) const = delete; +}; +struct NoCmpRecursiveMultiHashV : public QMultiHash<Dummy, NoCmpRecursiveMultiHashV> +{ + bool operator==(const NoCmpRecursiveMultiHashV &) const = delete; +}; +struct NoCmpRecursiveMultiHashK : public QMultiHash<NoCmpRecursiveMultiHashK, Dummy> +{ + bool operator==(const NoCmpRecursiveMultiHashK &) const = delete; +}; + +uint qHash(const Dummy &) { return 0; } +uint qHash(const RecursiveSet &) { return 0; } +uint qHash(const RecursiveHashK &) { return 0; } +uint qHash(const RecursiveHashV &) { return 0; } +uint qHash(const RecursiveMultiHashK &) { return 0; } +uint qHash(const RecursiveMultiHashV &) { return 0; } + +Q_DECLARE_METATYPE(RecursiveList); +Q_DECLARE_METATYPE(RecursiveSet); +Q_DECLARE_METATYPE(RecursiveMapV); +Q_DECLARE_METATYPE(RecursiveMapK); +Q_DECLARE_METATYPE(RecursiveMultiMapV); +Q_DECLARE_METATYPE(RecursiveMultiMapK); +Q_DECLARE_METATYPE(RecursiveHashV); +Q_DECLARE_METATYPE(RecursiveHashK); +Q_DECLARE_METATYPE(RecursiveMultiHashV); +Q_DECLARE_METATYPE(RecursiveMultiHashK); + +Q_DECLARE_METATYPE(NoCmpParamRecursiveMapV); +Q_DECLARE_METATYPE(NoCmpParamRecursiveMapK); +Q_DECLARE_METATYPE(NoCmpParamRecursiveMultiMapV); +Q_DECLARE_METATYPE(NoCmpParamRecursiveMultiMapK); +Q_DECLARE_METATYPE(NoCmpParamRecursiveHashK); +Q_DECLARE_METATYPE(NoCmpParamRecursiveHashV); +Q_DECLARE_METATYPE(NoCmpParamRecursiveMultiHashK); +Q_DECLARE_METATYPE(NoCmpParamRecursiveMultiHashV); + +Q_DECLARE_METATYPE(NoCmpRecursiveList); +Q_DECLARE_METATYPE(NoCmpRecursiveMapV); +Q_DECLARE_METATYPE(NoCmpRecursiveMapK); +Q_DECLARE_METATYPE(NoCmpRecursiveMultiMapV); +Q_DECLARE_METATYPE(NoCmpRecursiveMultiMapK); +Q_DECLARE_METATYPE(NoCmpRecursiveHashV); +Q_DECLARE_METATYPE(NoCmpRecursiveHashK); +Q_DECLARE_METATYPE(NoCmpRecursiveMultiHashV); +Q_DECLARE_METATYPE(NoCmpRecursiveMultiHashK); + +static_assert(QTypeTraits::has_operator_equal_v<RecursiveList>); +static_assert(QTypeTraits::has_operator_less_than_v<RecursiveList>); +static_assert(QTypeTraits::has_operator_equal_v<RecursiveSet>); +static_assert(QTypeTraits::has_operator_equal_v<RecursiveMapV>); +static_assert(QTypeTraits::has_operator_equal_v<RecursiveMapK>); +static_assert(QTypeTraits::has_operator_equal_v<RecursiveMultiMapV>); +static_assert(QTypeTraits::has_operator_equal_v<RecursiveMultiMapK>); +static_assert(QTypeTraits::has_operator_equal_v<RecursiveHashV>); +static_assert(QTypeTraits::has_operator_equal_v<RecursiveHashK>); +static_assert(QTypeTraits::has_operator_equal_v<RecursiveMultiHashV>); +static_assert(QTypeTraits::has_operator_equal_v<RecursiveMultiHashK>); + +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpParamRecursiveMapV>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpParamRecursiveMapK>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpParamRecursiveMultiMapV>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpParamRecursiveMultiMapK>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpParamRecursiveHashV>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpParamRecursiveHashK>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpParamRecursiveMultiHashV>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpParamRecursiveMultiHashK>); + +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpRecursiveList>); +static_assert(!QTypeTraits::has_operator_less_than_v<NoCmpRecursiveList>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpRecursiveSet>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpRecursiveMapV>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpRecursiveMapK>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpRecursiveMultiMapV>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpRecursiveMultiMapK>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpRecursiveHashV>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpRecursiveHashK>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpRecursiveMultiHashV>); +static_assert(!QTypeTraits::has_operator_equal_v<NoCmpRecursiveMultiHashK>); + +template <typename T> +constexpr inline bool has_prepend_v = true; +template <typename T, qsizetype N> +constexpr inline bool has_prepend_v<QVarLengthArray<T,N>> = false; // deprecated in Qt 6.3 + void tst_Collections::typeinfo() { - QVERIFY(QTypeInfo<int*>::isPointer); - QVERIFY(!QTypeInfo<int>::isPointer); + QVERIFY(std::is_pointer_v<int*>); + QVERIFY(!std::is_pointer_v<int>); QVERIFY(QTypeInfo<QString>::isComplex); QVERIFY(!QTypeInfo<int>::isComplex); } @@ -398,7 +530,7 @@ void tst_Collections::list() list << "one" << "two" << "one" << "two"; QVERIFY(!list.removeOne("three")); QVERIFY(list.removeOne("two")); - QCOMPARE(list, QList<QString>() << "one" << "one" << "two");; + QCOMPARE(list, QList<QString>() << "one" << "one" << "two"); QVERIFY(list.removeOne("two")); QCOMPARE(list, QList<QString>() << "one" << "one"); QVERIFY(!list.removeOne("two")); @@ -457,7 +589,10 @@ void tst_Collections::list() { QList<int> list; list.append(1); +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded") list = list; +QT_WARNING_POP QVERIFY(list.size() == 1); } } @@ -471,7 +606,7 @@ void tst_Collections::list() } { - QVector<QString> vector(5); + QList<QString> vector(5); vector[0] = "99"; vector[4] ="100"; QList<QString> list = vector.toList(); @@ -550,7 +685,7 @@ void tst_Collections::list() list.insert(0, "atzero"); QCOMPARE(list.at(0), QString("atzero")); - int listCount = list.count(); + int listCount = list.size(); list.insert(listCount, "atcount"); QCOMPARE(list.at(listCount), QString("atcount")); } @@ -725,231 +860,9 @@ void tst_Collections::list() } } -void tst_Collections::linkedList() -{ - { - QLinkedList<int> list; - QVERIFY(list.isEmpty()); - list.append(1); - list.push_back(2); - list += (3); - list << 4 << 5 << 6; - QVERIFY(!list.isEmpty()); - QVERIFY(list.size() == 6); - { - int sum = 0; - QLinkedListIterator<int> i = list; - while (i.hasNext()) { - sum += i.next(); - } - QVERIFY(sum == 21); - } - { - int sum = 0; - QLinkedList<int>::const_iterator i = list.begin(); - while (i != list.end()) - sum += *i++; - QVERIFY(sum == 21); - } - { - QMutableLinkedListIterator<int> i = list; - while (i.hasNext()) - i.setValue(2*i.next()); - } - { - int sum = 0; - QLinkedListIterator<int> i = list; - i.toBack(); - while (i.hasPrevious()) - sum += i.previous(); - QVERIFY(sum == 2*21); - } - { - QMutableLinkedListIterator<int> i = list; - i.toBack(); - while (i.hasPrevious()) - i.setValue(2*i.previous()); - } - { - int sum = 0; - QLinkedListIterator<int> i = list; - i.toBack(); - while (i.hasPrevious()) - sum += i.previous(); - QVERIFY(sum == 2*2*21); - } - { - QMutableLinkedListIterator<int> i = list; - while (i.hasNext()) { - int a = i.next(); - i.insert(a); - } - } - { - int sum = 0; - QLinkedList<int>::iterator i = list.begin(); - while (i != list.end()) - sum += *i++; - QVERIFY(sum == 2*2*2*21); - } - { - int duplicates = 0; - QLinkedListIterator<int> i = list; - while (i.hasNext()) { - int a = i.next(); - if (i.hasNext() && a == i.peekNext()) - duplicates++; - } - QVERIFY(duplicates == 6); - } - { - int duplicates = 0; - QLinkedListIterator<int> i = list; - i.toBack(); - while (i.hasPrevious()) { - int a = i.previous(); - if (i.hasPrevious() && a == i.peekPrevious()) - duplicates++; - } - QVERIFY(duplicates == 6); - } - { - QMutableLinkedListIterator<int> i = list; - while (i.hasNext()) { - int a = i.next(); - if (i.hasNext() && - i.peekNext() == a) - i.remove(); - } - } - { - int duplicates = 0; - QMutableLinkedListIterator<int> i = list; - i.toBack(); - while (i.hasPrevious()) { - int a = i.previous(); - if (i.hasPrevious() && a == i.peekPrevious()) - duplicates++; - } - QVERIFY(duplicates == 0); - } - { - QVERIFY(list.size() == 6); - QMutableLinkedListIterator<int> i = list; - while (i.hasNext()) { - int a = i.peekNext(); - i.insert(42); - QVERIFY(i.peekPrevious() == 42 && i.peekNext() == a); - i.next(); - } - QVERIFY(list.size() == 12); - i.toFront(); - while (i.findNext(42)) - i.remove(); - } - { - QLinkedList<int> l; - l << 4 << 8 << 12 << 16 << 20 << 24; - QVERIFY(l == list); - QLinkedList<int> copy = list; - list += list; - QVERIFY(l != list && l.size() == list.size()/2 && l == copy); - l += copy; - QVERIFY(l == list); - list = copy; - } - { - QLinkedList<int> copy = list; - list.prepend(999); - list.append(999); - QVERIFY(list.contains(999)); - QVERIFY(list.count(999) == 2); - list.removeAll(999); - QVERIFY(list == copy); - } - { - QLinkedList<QString> list; - list << "one" << "two" << "three" << "four" << "five" << "six"; - while (!list.isEmpty()) - list.removeAll(list.first()); - } - { - QLinkedList<QString> list; - list << "one" << "two" << "one" << "two"; - QVERIFY(!list.removeOne("three")); - QVERIFY(list.removeOne("two")); - QCOMPARE(list, QLinkedList<QString>() << "one" << "one" << "two");; - QVERIFY(list.removeOne("two")); - QCOMPARE(list, QLinkedList<QString>() << "one" << "one"); - QVERIFY(!list.removeOne("two")); - QCOMPARE(list, QLinkedList<QString>() << "one" << "one"); - QVERIFY(list.removeOne("one")); - QCOMPARE(list, QLinkedList<QString>() << "one"); - QVERIFY(list.removeOne("one")); - QVERIFY(list.isEmpty()); - QVERIFY(!list.removeOne("one")); - QVERIFY(list.isEmpty()); - } - { - list.clear(); - QVERIFY(list.isEmpty()); - QVERIFY(list.begin() == list.end()); - QLinkedListIterator<int> i(list); - QVERIFY(!i.hasNext() && !i.hasPrevious()); - } - } - - { - QLinkedList<QString> list; - list.append("Hello"); - - QLinkedList<QString>::iterator it = list.begin(); - QVERIFY((*it)[0] == QChar('H')); - QVERIFY(it->constData()[0] == QChar('H')); - it->replace(QChar('H'), QChar('X')); - QCOMPARE(list.first(), QLatin1String("Xello")); - - QLinkedList<QString>::const_iterator cit = list.constBegin(); - QCOMPARE((*cit).toLower(), QLatin1String("xello")); - QCOMPARE(cit->toUpper(), QLatin1String("XELLO")); - - cit = list.cbegin(); - QCOMPARE((*cit).toLower(), QLatin1String("xello")); - QCOMPARE(cit->toUpper(), QLatin1String("XELLO")); - } - - { - QLinkedList<QString> list; - list << "alpha" << "beta"; - list += list; - QVERIFY(list.size() == 4); - QCOMPARE(*list.begin(), QLatin1String("alpha")); - QCOMPARE(*(list.begin() + 1), QLatin1String("beta")); - QCOMPARE(*(list.begin() + 2), QLatin1String("alpha")); - QCOMPARE(*(list.begin() + 3), QLatin1String("beta")); - } - - { - QLinkedList<int> a; - QCOMPARE(a.startsWith(1), false); - QCOMPARE(a.endsWith(1), false); - a.append(1); - QCOMPARE(a.startsWith(1), true); - QCOMPARE(a.startsWith(2), false); - QCOMPARE(a.endsWith(1), true); - QCOMPARE(a.endsWith(2), false); - a.append(2); - QCOMPARE(a.startsWith(1), true); - QCOMPARE(a.startsWith(2), false); - QCOMPARE(a.endsWith(1), false); - QCOMPARE(a.endsWith(2), true); - } -}; - - void tst_Collections::vector() { - QVector<int> v1; + QList<int> v1; v1 << 1 << 2 << 3; QVector<int> v2; v2 << 4 << 5; @@ -1033,7 +946,7 @@ void tst_Collections::vector() QVERIFY(LargeStatic::count == originalLargeStaticCount); { QVector<LargeStatic> vector; - LargeStatic *dummy = 0; + LargeStatic *dummy = nullptr; for (int i = 0; i < 10000; ++i) { delete dummy; dummy = new LargeStatic; @@ -1055,7 +968,7 @@ void tst_Collections::vector() QVERIFY(Movable::count == originalMovableCount); { QVector<Movable> vector; - Movable *dummy = 0; + Movable *dummy = nullptr; for (int i = 0; i < 10000; ++i) { delete dummy; dummy = new Movable; @@ -1198,6 +1111,16 @@ void tst_Collections::byteArray() QVERIFY(hello.indexOf('l',2) == 2); QVERIFY(hello.indexOf('l',3) == 3); + QByteArray empty; + QCOMPARE(empty.indexOf("x"), -1); + QCOMPARE(empty.lastIndexOf("x"), -1); + QCOMPARE(empty.lastIndexOf("x", 0), -1); + QCOMPARE(empty.count("x"), 0); + QCOMPARE(empty.indexOf(""), 0); + QCOMPARE(empty.lastIndexOf(""), 0); + QCOMPARE(empty.lastIndexOf("", -1), -1); + QCOMPARE(empty.count(""), 1); + QByteArray large = "000 100 200 300 400 500 600 700 800 900"; QVERIFY(large.indexOf("700") == 28); @@ -1313,7 +1236,7 @@ void tst_Collections::stack() stack.push(1); stack.push(2); stack.push(3); - QVectorIterator<int> i = stack; + QListIterator<int> i = stack; i.toBack(); int sum = 0; while (i.hasPrevious()) @@ -1342,17 +1265,17 @@ void tst_Collections::hash() Hash hash; QString key = QLatin1String(" "); for (int i = 0; i < 10; ++i) { - key[0] = i + '0'; + key[0] = QChar(i + '0'); for (int j = 0; j < 10; ++j) { - key[1] = j + '0'; + key[1] = QChar(j + '0'); hash.insert(key, "V" + key); } } for (int i = 0; i < 10; ++i) { - key[0] = i + '0'; + key[0] = QChar(i + '0'); for (int j = 0; j < 10; ++j) { - key[1] = j + '0'; + key[1] = QChar(j + '0'); hash.remove(key); } } @@ -1367,6 +1290,8 @@ void tst_Collections::hash() QVERIFY(hash.size() == 2); QVERIFY(!hash.isEmpty()); +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded") { Hash hash2 = hash; hash2 = hash; @@ -1379,6 +1304,7 @@ void tst_Collections::hash() QVERIFY(hash2.isEmpty()); } QVERIFY(hash.size() == 2); +QT_WARNING_POP { Hash hash2 = hash; @@ -1515,19 +1441,13 @@ void tst_Collections::hash() QHash<int, int> hash; for (int i = 0; i < 1000; ++i) hash.insert(i, i); - QVERIFY(hash.capacity() == 1031); - hash.squeeze(); - QVERIFY(hash.capacity() == 521); - - hash.insert(12345, 12345); - QVERIFY(hash.capacity() == 1031); + QVERIFY(hash.capacity() > 1000); for (int j = 0; j < 900; ++j) hash.remove(j); - QVERIFY(hash.capacity() == 257); + QVERIFY(hash.capacity() > 1000); hash.squeeze(); - QVERIFY(hash.capacity() == 67); - hash.reserve(0); + QVERIFY(hash.capacity() < 200); } } @@ -1551,22 +1471,30 @@ void tst_Collections::hash() } { - QHash<int, QString> hash1, hash2; - hash1.insertMulti(1, "Alpha"); - hash1.insertMulti(1, "Gamma"); - hash2.insertMulti(1, "Beta"); - hash2.insertMulti(1, "Gamma"); - hash2.insertMulti(1, "Gamma"); + QMultiHash<int, QString> hash1, hash2; + hash1.insert(1, "Alpha"); + hash1.insert(1, "Gamma"); + hash2.insert(1, "Beta"); + hash2.insert(1, "Gamma"); + hash2.insert(1, "Gamma"); hash1.unite(hash2); QCOMPARE(hash1.size(), 5); - QCOMPARE(hash1.values(), - (QList<QString>() << "Gamma" << "Gamma" << "Beta" << "Gamma" << "Alpha")); + auto values = hash1.values(); + std::sort(values.begin(), values.end()); + QList<QString> expected; + expected << "Gamma" << "Gamma" << "Beta" << "Gamma" << "Alpha"; + std::sort(expected.begin(), expected.end()); + QCOMPARE(values, expected); hash2 = hash1; hash2.unite(hash2); QCOMPARE(hash2.size(), 10); - QCOMPARE(hash2.values(), hash1.values() + hash1.values()); + values = hash2.values(); + std::sort(values.begin(), values.end()); + expected += expected; + std::sort(expected.begin(), expected.end()); + QCOMPARE(values, expected); } } @@ -1588,6 +1516,8 @@ void tst_Collections::map() QVERIFY(map.size() == 2); QVERIFY(!map.isEmpty()); +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded") { Map map2 = map; map2 = map; @@ -1600,6 +1530,7 @@ void tst_Collections::map() QVERIFY(map2.isEmpty()); } QVERIFY(map.size() == 2); +QT_WARNING_POP { Map map2 = map; @@ -1872,12 +1803,12 @@ void tst_Collections::map() } { - QMap<int, QString> map1, map2; - map1.insertMulti(1, "Alpha"); - map1.insertMulti(1, "Gamma"); - map2.insertMulti(1, "Beta"); - map2.insertMulti(1, "Gamma"); - map2.insertMulti(1, "Gamma"); + QMultiMap<int, QString> map1, map2; + map1.insert(1, "Alpha"); + map1.insert(1, "Gamma"); + map2.insert(1, "Beta"); + map2.insert(1, "Gamma"); + map2.insert(1, "Gamma"); map1.unite(map2); QCOMPARE(map1.size(), 5); @@ -1922,11 +1853,21 @@ void tst_Collections::qstring() QVERIFY (hello.contains('e') != false); QVERIFY(hello.indexOf('e') == 1); - QVERIFY(hello.indexOf('e', -10) == 1); + QVERIFY(hello.indexOf('e', -10) == -1); QVERIFY(hello.indexOf('l') == 2); QVERIFY(hello.indexOf('l',2) == 2); QVERIFY(hello.indexOf('l',3) == 3); + QString empty; + QCOMPARE(empty.indexOf("x"), -1); + QCOMPARE(empty.lastIndexOf("x"), -1); + QCOMPARE(empty.lastIndexOf("x", 0), -1); + QCOMPARE(empty.count("x"), 0); + QCOMPARE(empty.indexOf(""), 0); + QCOMPARE(empty.lastIndexOf(""), 0); + QCOMPARE(empty.lastIndexOf("", -1), -1); + QCOMPARE(empty.count(""), 1); + QString large = "000 100 200 300 400 500 600 700 800 900"; QVERIFY(large.indexOf("700") == 28); @@ -1936,6 +1877,20 @@ void tst_Collections::qstring() QVERIFY(large.lastIndexOf("700", 28) == 28); QVERIFY(large.lastIndexOf("700", 27) == -1); + QCOMPARE(large.indexOf(""), 0); + QCOMPARE(large.indexOf(QString()), 0); + QCOMPARE(large.indexOf(QLatin1String()), 0); + QCOMPARE(large.indexOf(QStringView()), 0); + QCOMPARE(large.lastIndexOf(""), large.size()); + QCOMPARE(large.lastIndexOf("", -1), large.size() - 1); + QCOMPARE(large.lastIndexOf(QString()), large.size()); + QCOMPARE(large.lastIndexOf(QLatin1String()), large.size()); + QCOMPARE(large.lastIndexOf(QStringView()), large.size()); + QCOMPARE(large.count(""), large.size() + 1); + QCOMPARE(large.count(QString()), large.size() + 1); + QCOMPARE(large.count(QLatin1String()), large.size() + 1); + QCOMPARE(large.count(QStringView()), large.size() + 1); + QVERIFY(large.contains("200")); QVERIFY(!large.contains("201")); QVERIFY(large.contains('3')); @@ -1986,15 +1941,6 @@ void tst_Collections::qstring() QVERIFY(null.isNull()); QVERIFY(!nonNull.isNull()); -#if QT_DEPRECATED_SINCE(5, 9) - QVERIFY(null == QString::null); - QVERIFY(QString::null == null); - QVERIFY(nonNull != QString::null); - QVERIFY(QString::null != nonNull); - QVERIFY(null == nonNull); - QVERIFY(QString::null == QString::null); -#endif - QString fill = "123"; fill.fill('a'); QCOMPARE(fill, QLatin1String("aaa")); @@ -2073,8 +2019,8 @@ void tst_Collections::qstring() s = "ascii"; s += QChar((uchar) 0xb0); QVERIFY(s.toUtf8() != s.toLatin1()); - QCOMPARE(s[s.length()-1].unicode(), (ushort)0xb0); - QCOMPARE(s.left(s.length()-1), QLatin1String("ascii")); + QCOMPARE(s[s.size()-1].unicode(), char16_t(0xb0)); + QCOMPARE(s.left(s.size()-1), QLatin1String("ascii")); QVERIFY(s == QString::fromUtf8(s.toUtf8().constData())); @@ -2126,7 +2072,7 @@ void tst_Collections::qstring() QString str = "Hello"; - QString cstr = QString::fromRawData(str.unicode(), str.length()); + QString cstr = QString::fromRawData(str.unicode(), str.size()); QCOMPARE(str, QLatin1String("Hello")); QCOMPARE(cstr, QLatin1String("Hello")); cstr.clear(); @@ -2320,12 +2266,6 @@ void populate(QList<int> &container) } template <> -void populate(QLinkedList<int> &container) -{ - container << 1 << 2 << 4 << 8; -} - -template <> void populate(QMap<int, int> &container) { container.insert(1, 1); @@ -2414,11 +2354,6 @@ void tst_Collections::sharableQList() TEST_SEQUENTIAL_CONTAINER(List); } -void tst_Collections::sharableQLinkedList() -{ - TEST_SEQUENTIAL_CONTAINER(LinkedList); -} - void tst_Collections::sharableQVector() { TEST_SEQUENTIAL_CONTAINER(Vector); @@ -2594,7 +2529,7 @@ template <class Container> void testLinkedListLikeStlIterators() { Container fake; - typename Container::value_type t; + typename Container::value_type t = {}; fake << t; typename Container::iterator i1 = fake.begin(), i2 = i1 + 1; @@ -2627,7 +2562,7 @@ void testListLikeStlIterators() testLinkedListLikeStlIterators<Container>(); Container fake; - typename Container::value_type t; + typename Container::value_type t = {}; fake << t; typename Container::iterator i1 = fake.begin(), i2 = i1 + 1; @@ -2710,8 +2645,10 @@ void testMapLikeStlIterators() QString t; fake.insert(k, t); - typename Container::iterator i1 = fake.begin(), i2 = i1 + 1; - typename Container::const_iterator c1 = i1, c2 = c1 + 1; + typename Container::iterator i1 = fake.begin(), i2 = i1; + ++i2; + typename Container::const_iterator c1 = i1, c2 = c1; + ++c2; QVERIFY(i1 == i1); QVERIFY(i1 == c1); @@ -2721,8 +2658,6 @@ void testMapLikeStlIterators() QVERIFY(i2 == c2); QVERIFY(c2 == i2); QVERIFY(c2 == c2); - QVERIFY(1 + i1 == i1 + 1); - QVERIFY(1 + c1 == c1 + 1); QVERIFY(i1 != i2); QVERIFY(i1 != c2); @@ -2736,14 +2671,12 @@ void testMapLikeStlIterators() void tst_Collections::constAndNonConstStlIterators() { - testListLikeStlIterators<QList<int> >(); - testListLikeStlIterators<QStringList >(); - testLinkedListLikeStlIterators<QLinkedList<int> >(); - testListLikeStlIterators<QVector<int> >(); - testMapLikeStlIterators<QMap<QString, QString> >(); - testMapLikeStlIterators<QMultiMap<QString, QString> >(); - testMapLikeStlIterators<QHash<QString, QString> >(); - testMapLikeStlIterators<QMultiHash<QString, QString> >(); + testListLikeStlIterators<QList<int>>(); + testListLikeStlIterators<QStringList>(); + testMapLikeStlIterators<QMap<QString, QString>>(); + testMapLikeStlIterators<QMultiMap<QString, QString>>(); + testMapLikeStlIterators<QHash<QString, QString>>(); + testMapLikeStlIterators<QMultiHash<QString, QString>>(); } void tst_Collections::vector_stl_data() @@ -2760,8 +2693,8 @@ void tst_Collections::vector_stl() { QFETCH(QStringList, elements); - QVector<QString> vector; - for (int i = 0; i < elements.count(); ++i) + QList<QString> vector; + for (int i = 0; i < elements.size(); ++i) vector << elements.at(i); #if QT_VERSION < QT_VERSION_CHECK(6,0,0) @@ -2776,34 +2709,9 @@ void tst_Collections::vector_stl() QCOMPARE(*it, vector[j]); #if QT_VERSION < QT_VERSION_CHECK(6,0,0) - QCOMPARE(QVector<QString>::fromStdVector(stdVector), vector); + QCOMPARE(QList<QString>::fromStdVector(stdVector), vector); #endif - QCOMPARE(QVector<QString>(stdVector.begin(), stdVector.end()), vector); -} - -void tst_Collections::linkedlist_stl_data() -{ - list_stl_data(); -} - -void tst_Collections::linkedlist_stl() -{ - QFETCH(QStringList, elements); - - QLinkedList<QString> list; - for (int i = 0; i < elements.count(); ++i) - list << elements.at(i); - - std::list<QString> stdList = list.toStdList(); - - QCOMPARE(int(stdList.size()), elements.size()); - - std::list<QString>::const_iterator it = stdList.begin(); - QLinkedList<QString>::const_iterator it2 = list.cbegin(); - for (uint j = 0; j < stdList.size(); ++j, ++it, ++it2) - QCOMPARE(*it, *it2); - - QCOMPARE(QLinkedList<QString>::fromStdList(stdList), list); + QCOMPARE(QList<QString>(stdVector.begin(), stdVector.end()), vector); } void tst_Collections::list_stl_data() @@ -2821,7 +2729,7 @@ void tst_Collections::list_stl() QFETCH(QStringList, elements); QList<QString> list; - for (int i = 0; i < elements.count(); ++i) + for (int i = 0; i < elements.size(); ++i) list << elements.at(i); #if QT_VERSION < QT_VERSION_CHECK(6,0,0) @@ -2843,7 +2751,7 @@ void tst_Collections::list_stl() } template <typename T> -T qtInit(T * = 0) +T qtInit(T * = nullptr) { return T(); } @@ -2853,9 +2761,9 @@ void tst_Collections::q_init() QCOMPARE(qtInit<int>(), 0); QCOMPARE(qtInit<double>(), 0.0); QCOMPARE(qtInit<QString>(), QString()); - QCOMPARE(qtInit<int *>(), static_cast<int *>(0)); - QCOMPARE(qtInit<double *>(), static_cast<double *>(0)); - QCOMPARE(qtInit<QString *>(), static_cast<QString *>(0)); + QCOMPARE(qtInit<int *>(), static_cast<int *>(nullptr)); + QCOMPARE(qtInit<double *>(), static_cast<double *>(nullptr)); + QCOMPARE(qtInit<QString *>(), static_cast<QString *>(nullptr)); QCOMPARE(qtInit<Pod>().i1, 0); QCOMPARE(qtInit<Pod>().i2, 0); } @@ -2869,6 +2777,7 @@ class LessThanComparable { public: bool operator<(const LessThanComparable &) const { return true; } + bool operator==(const LessThanComparable &) const { return true; } }; class EqualsComparable @@ -2877,7 +2786,7 @@ public: bool operator==(const EqualsComparable &) const { return true; } }; -uint qHash(const EqualsComparable &) +size_t qHash(const EqualsComparable &) { return 0; } @@ -2909,11 +2818,11 @@ void instantiateContainer() constIt = constContainer.end(); constIt = constContainer.cend(); container.constEnd(); - Q_UNUSED(constIt) + Q_UNUSED(constIt); container.clear(); container.contains(value); - container.count(); + container.size(); container.empty(); container.isEmpty(); container.size(); @@ -2932,11 +2841,7 @@ void instantiateMutableIterationContainer() typename ContainerType::iterator it; it = container.begin(); it = container.end(); - Q_UNUSED(it) - - // QSet lacks count(T). - const ValueType value = ValueType(); - container.count(value); + Q_UNUSED(it); } template <typename ContainerType, typename ValueType> @@ -2944,10 +2849,9 @@ void instantiateSequence() { instantiateMutableIterationContainer<ContainerType, ValueType>(); -// QVector lacks removeAll(T) -// ValueType value = ValueType(); -// ContainerType container; -// container.removeAll(value); + ValueType value = ValueType(); + ContainerType container; + container.removeAll(value); } template <typename ContainerType, typename ValueType> @@ -3002,14 +2906,12 @@ void instantiatePairAssociative() { instantiateMutableIterationContainer<ContainerType, KeyType>(); - typename ContainerType::iterator it; - typename ContainerType::const_iterator constIt; const KeyType key = KeyType(); const ValueType value = ValueType(); ContainerType container; const ContainerType constContainer(container); - it = container.insert(key, value); + auto it = container.insert(key, value); container.erase(it); container.find(key); container.constFind(key); @@ -3020,11 +2922,10 @@ void instantiatePairAssociative() constContainer.keys(); container.remove(key); container.take(key); - container.unite(constContainer); + container.insert(constContainer); container.value(key); container.value(key, value); container.values(); - container.values(key); container[key]; const int foo = constContainer[key]; Q_UNUSED(foo); @@ -3053,15 +2954,6 @@ void tst_Collections::containerInstantiation() typedef QSet<EqualsComparable> Set; instantiateAssociative<Set, EqualsComparable>(); - //Instantiate QLinkedList member functions. - typedef QLinkedList<EqualsComparable> LinkedList; - instantiateSequence<LinkedList, EqualsComparable> (); - { - EqualsComparable value; - LinkedList list; - list.removeAll(value); - } - //Instantiate QList member functions. typedef QList<EqualsComparable> List; instantiateRandomAccess<List, EqualsComparable>(); @@ -3071,10 +2963,6 @@ void tst_Collections::containerInstantiation() list.removeAll(value); } - //Instantiate QVector member functions. - typedef QVector<EqualsComparable> Vector; - instantiateRandomAccess<Vector, EqualsComparable>(); - //Instantiate QQueue member functions. typedef QQueue<EqualsComparable> Queue; instantiateRandomAccess<Queue, EqualsComparable>(); @@ -3112,7 +3000,7 @@ void tst_Collections::qtimerList() template <typename Container> void testContainerTypedefs(Container container) { - Q_UNUSED(container) + Q_UNUSED(container); { QVERIFY_TYPE(typename Container::value_type); } { QVERIFY_TYPE(typename Container::iterator); } { QVERIFY_TYPE(typename Container::const_iterator); } @@ -3126,7 +3014,7 @@ void testContainerTypedefs(Container container) template <typename Container> void testPairAssociativeContainerTypedefs(Container container) { - Q_UNUSED(container) + Q_UNUSED(container); // TODO: Not sure how to define value_type for our associative containers // { QVERIFY_TYPE(typename Container::value_type); } @@ -3148,7 +3036,7 @@ void testPairAssociativeContainerTypedefs(Container container) template <typename Container> void testSetContainerTypedefs(Container container) { - Q_UNUSED(container) + Q_UNUSED(container); { QVERIFY_TYPE(typename Container::iterator); } { QVERIFY_TYPE(typename Container::const_iterator); } { QVERIFY_TYPE(typename Container::reference); } @@ -3165,10 +3053,8 @@ void testSetContainerTypedefs(Container container) */ void tst_Collections::containerTypedefs() { - testContainerTypedefs(QVector<int>()); testContainerTypedefs(QStack<int>()); testContainerTypedefs(QList<int>()); - testContainerTypedefs(QLinkedList<int>()); testContainerTypedefs(QQueue<int>()); testPairAssociativeContainerTypedefs(QMap<int, int>()); @@ -3184,32 +3070,40 @@ class T2; void tst_Collections::forwardDeclared() { - { typedef QHash<Key1, T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } - { typedef QMultiHash<Key1, T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } - { typedef QMap<Key1, T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } - { typedef QMultiMap<Key1, T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } - { typedef QPair<T1, T2> C; C *x = 0; Q_UNUSED(x) } - { typedef QList<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } - { typedef QLinkedList<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } - { typedef QVector<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) Q_UNUSED(i) Q_UNUSED(j) } - { typedef QStack<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) Q_UNUSED(i) Q_UNUSED(j) } - { typedef QQueue<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } - { typedef QSet<T1> C; C *x = 0; /* C::iterator i; */ C::const_iterator j; Q_UNUSED(x) } -} +#define TEST(...) do { \ + using C = __VA_ARGS__; \ + C *x = nullptr; \ + C::iterator i; \ + C::const_iterator j; \ + Q_UNUSED(x); \ + Q_UNUSED(i); \ + Q_UNUSED(j); \ + } while (false) + + TEST(QHash<Key1, T1>); + TEST(QMap<Key1, T1>); + TEST(QMultiMap<Key1, T1>); + TEST(QList<T1>); + TEST(QVector<T1>); + TEST(QStack<T1>); + TEST(QQueue<T1>); + TEST(QSet<T1>); +#undef TEST -class alignas(4) Aligned4 -{ - char i; -public: - Aligned4(int i = 0) : i(i) {} - - enum { PreferredAlignment = 4 }; + { + using C = QPair<T1, T2>; + C *x = nullptr; + Q_UNUSED(x); + } - inline bool operator==(const Aligned4 &other) const { return i == other.i; } - inline bool operator<(const Aligned4 &other) const { return i < other.i; } - friend inline int qHash(const Aligned4 &a) { return qHash(a.i); } -}; -Q_STATIC_ASSERT(alignof(Aligned4) % 4 == 0); + { + using C = QSet<T1>; + C *x = nullptr; + C::const_iterator j; + Q_UNUSED(x); + Q_UNUSED(j); + } +} #if defined(Q_PROCESSOR_ARM) # if defined(Q_COMPILER_ALIGNAS) && defined(__BIGGEST_ALIGNMENT__) @@ -3223,19 +3117,30 @@ Q_STATIC_ASSERT(alignof(Aligned4) % 4 == 0); # define BIGGEST_ALIGNMENT_TO_TEST 128 #endif -class alignas(BIGGEST_ALIGNMENT_TO_TEST) AlignedBiggest +template <size_t Alignment> +class alignas(Alignment) AlignedClass { char i; + public: - AlignedBiggest(int i = 0) : i(i) {} + AlignedClass(int i = 0) : i(i) {} - enum { PreferredAlignment = BIGGEST_ALIGNMENT_TO_TEST }; + enum { PreferredAlignment = Alignment }; - inline bool operator==(const AlignedBiggest &other) const { return i == other.i; } - inline bool operator<(const AlignedBiggest &other) const { return i < other.i; } - friend inline int qHash(const AlignedBiggest &a) { return qHash(a.i); } + inline bool operator==(const AlignedClass &other) const { return i == other.i; } + inline bool operator<(const AlignedClass &other) const { return i < other.i; } + friend inline size_t qHash(const AlignedClass &a) { return qHash(a.i); } }; -Q_STATIC_ASSERT(alignof(AlignedBiggest) % BIGGEST_ALIGNMENT_TO_TEST == 0); + +using Aligned4 = AlignedClass<4>; +static_assert(alignof(Aligned4) % 4 == 0); + +using AlignedStdMax = AlignedClass<alignof(std::max_align_t)>; +static_assert(alignof(AlignedStdMax) % alignof(std::max_align_t) == 0); + +using AlignedBiggest = AlignedClass<BIGGEST_ALIGNMENT_TO_TEST>; +static_assert(BIGGEST_ALIGNMENT_TO_TEST > alignof(std::max_align_t), "Not overly aligned"); +static_assert(alignof(AlignedBiggest) % BIGGEST_ALIGNMENT_TO_TEST == 0); template<typename C> void testVectorAlignment() @@ -3292,17 +3197,28 @@ void testAssociativeContainerAlignment() void tst_Collections::alignment() { - testVectorAlignment<QVector<Aligned4> >(); - testVectorAlignment<QVector<AlignedBiggest> >(); + testVectorAlignment<QList<Aligned4> >(); + testVectorAlignment<QList<AlignedStdMax> >(); + testVectorAlignment<QList<AlignedBiggest> >(); + testContiguousCacheAlignment<QContiguousCache<Aligned4> >(); + testContiguousCacheAlignment<QContiguousCache<AlignedStdMax> >(); testContiguousCacheAlignment<QContiguousCache<AlignedBiggest> >(); + + // there's no guarentee that std::map supports over-aligned types testAssociativeContainerAlignment<QMap<Aligned4, Aligned4> >(); - testAssociativeContainerAlignment<QMap<Aligned4, AlignedBiggest> >(); - testAssociativeContainerAlignment<QMap<AlignedBiggest, Aligned4> >(); - testAssociativeContainerAlignment<QMap<AlignedBiggest, AlignedBiggest> >(); + testAssociativeContainerAlignment<QMap<Aligned4, AlignedStdMax> >(); + testAssociativeContainerAlignment<QMap<AlignedStdMax, Aligned4> >(); + testAssociativeContainerAlignment<QMap<AlignedStdMax, AlignedStdMax> >(); + testAssociativeContainerAlignment<QHash<Aligned4, Aligned4> >(); + testAssociativeContainerAlignment<QHash<Aligned4, AlignedStdMax> >(); testAssociativeContainerAlignment<QHash<Aligned4, AlignedBiggest> >(); + testAssociativeContainerAlignment<QHash<AlignedStdMax, Aligned4> >(); + testAssociativeContainerAlignment<QHash<AlignedStdMax, AlignedStdMax> >(); + testAssociativeContainerAlignment<QHash<AlignedStdMax, AlignedBiggest> >(); testAssociativeContainerAlignment<QHash<AlignedBiggest, Aligned4> >(); + testAssociativeContainerAlignment<QHash<AlignedBiggest, AlignedStdMax> >(); testAssociativeContainerAlignment<QHash<AlignedBiggest, AlignedBiggest> >(); } @@ -3329,7 +3245,7 @@ template<template<class> class C> void QTBUG13079_collectionInsideCollectionImpl QCOMPARE(nodeList.first().s, QString::fromLatin1("child")); nodeList = nodeList.first().children; - QCOMPARE(nodeList.count(), 0); + QCOMPARE(nodeList.size(), 0); nodeList << QTBUG13079_Node<C>(); } @@ -3354,12 +3270,12 @@ template<template<class, class> class C> void QTBUG13079_collectionInsideCollect QCOMPARE(nodeMap[12].s, QString::fromLatin1("child")); nodeMap = nodeMap[12].children; - QCOMPARE(nodeMap.count(), 0); + QCOMPARE(nodeMap.size(), 0); nodeMap[42] = QTBUG13079_NodeAssoc<C>(); } -quint32 qHash(const QTBUG13079_Node<QSet> &) +size_t qHash(const QTBUG13079_Node<QSet> &) { return 0; } @@ -3415,14 +3331,13 @@ void tst_Collections::QTBUG13079_collectionInsideCollection() QTBUG13079_collectionInsideCollectionImpl<QVector>(); QTBUG13079_collectionInsideCollectionImpl<QStack>(); QTBUG13079_collectionInsideCollectionImpl<QList>(); - QTBUG13079_collectionInsideCollectionImpl<QLinkedList>(); QTBUG13079_collectionInsideCollectionImpl<QQueue>(); { QSet<QTBUG13079_Node<QSet> > nodeSet; nodeSet << QTBUG13079_Node<QSet>(); nodeSet = nodeSet.begin()->children; - QCOMPARE(nodeSet.count(), 0); + QCOMPARE(nodeSet.size(), 0); } QTBUG13079_collectionInsideCollectionAssocImpl<QMap>(); @@ -3444,7 +3359,7 @@ template<class Container> void foreach_test_arrays(const Container &container) set << val; i++; } - QCOMPARE(set.count(), container.count()); + QCOMPARE(set.size(), container.size()); //modify the container while iterating. Container c2 = container; @@ -3481,9 +3396,9 @@ void tst_Collections::foreach_2() varl2 << i; varl3 << i; } - QCOMPARE(varl1.count(), intlist.count()); - QCOMPARE(varl2.count(), intlist.count()); - QCOMPARE(varl3.count(), intlist.count()); + QCOMPARE(varl1.size(), intlist.size()); + QCOMPARE(varl2.size(), intlist.size()); + QCOMPARE(varl3.size(), intlist.size()); QVarLengthArray<QString> varl4; QVarLengthArray<QString, 3> varl5; @@ -3493,9 +3408,9 @@ void tst_Collections::foreach_2() varl5 << str; varl6 << str; } - QCOMPARE(varl4.count(), strlist.count()); - QCOMPARE(varl5.count(), strlist.count()); - QCOMPARE(varl6.count(), strlist.count()); + QCOMPARE(varl4.size(), strlist.size()); + QCOMPARE(varl5.size(), strlist.size()); + QCOMPARE(varl6.size(), strlist.size()); } struct IntOrString @@ -3516,14 +3431,17 @@ template<class Container> void insert_remove_loop_impl() t.append(T(IntOrString(1))); t << (T(IntOrString(2))); t += (T(IntOrString(3))); - t.prepend(T(IntOrString(4))); + if constexpr (has_prepend_v<Container>) + t.prepend(T(IntOrString(4))); + else + t.insert(t.cbegin(), T(IntOrString(4))); t.insert(2, 3 , T(IntOrString(5))); t.insert(4, T(IntOrString(6))); t.insert(t.begin() + 2, T(IntOrString(7))); t.insert(t.begin() + 5, 3, T(IntOrString(8))); int expect1[] = { 4 , 1 , 7, 5 , 5 , 8, 8, 8, 6, 5, 2 , 3 }; - QCOMPARE(size_t(t.count()), sizeof(expect1)/sizeof(int)); - for (int i = 0; i < t.count(); i++) { + QCOMPARE(size_t(t.size()), sizeof(expect1)/sizeof(int)); + for (int i = 0; i < t.size(); i++) { QCOMPARE(t[i], T(IntOrString(expect1[i]))); } @@ -3537,8 +3455,8 @@ template<class Container> void insert_remove_loop_impl() t.remove(7); t.remove(2, 3); int expect2[] = { 4 , 1 , 9, 8, 6, 5, 2 , 3 }; - QCOMPARE(size_t(t.count()), sizeof(expect2)/sizeof(int)); - for (int i = 0; i < t.count(); i++) { + QCOMPARE(size_t(t.size()), sizeof(expect2)/sizeof(int)); + for (int i = 0; i < t.size(); i++) { QCOMPARE(t[i], T(IntOrString(expect2[i]))); } @@ -3550,16 +3468,16 @@ template<class Container> void insert_remove_loop_impl() } int expect3[] = { 1 , 9, 5, 3 }; - QCOMPARE(size_t(t.count()), sizeof(expect3)/sizeof(int)); - for (int i = 0; i < t.count(); i++) { + QCOMPARE(size_t(t.size()), sizeof(expect3)/sizeof(int)); + for (int i = 0; i < t.size(); i++) { QCOMPARE(t[i], T(IntOrString(expect3[i]))); } t.erase(t.begin() + 1, t.end() - 1); int expect4[] = { 1 , 3 }; - QCOMPARE(size_t(t.count()), sizeof(expect4)/sizeof(int)); - for (int i = 0; i < t.count(); i++) { + QCOMPARE(size_t(t.size()), sizeof(expect4)/sizeof(int)); + for (int i = 0; i < t.size(); i++) { QCOMPARE(t[i], T(IntOrString(expect4[i]))); } @@ -3576,10 +3494,22 @@ template<class Container> void insert_remove_loop_impl() int expect5[] = { 1, 1, 2, 3*3, 3, 3*3+1, 10, 11*11, 11, 11*11+1, 12 , 13*13, 13, 13*13+1, 14, 15*15, 15, 15*15+1, 16 , 17*17, 17, 17*17+1 ,18 , 19*19, 19, 19*19+1, 20, 21*21, 21, 21*21+1 }; - QCOMPARE(size_t(t.count()), sizeof(expect5)/sizeof(int)); - for (int i = 0; i < t.count(); i++) { + QCOMPARE(size_t(t.size()), sizeof(expect5)/sizeof(int)); + for (int i = 0; i < t.size(); i++) { QCOMPARE(t[i], T(IntOrString(expect5[i]))); } + + t.clear(); + t << T(IntOrString(1)) << T(IntOrString(2)) << T(IntOrString(3)) << T(IntOrString(4)); + t.insert(2, 4, T(IntOrString(9))); + t.insert(2, 4, T(IntOrString(7))); + + int expect6[] = { 1, 2, 7, 7, 7, 7, 9, 9, 9, 9, 3, 4 }; + QCOMPARE(size_t(t.size()), sizeof(expect6)/sizeof(int)); + for (int i = 0; i < t.size(); i++) { + QCOMPARE(t[i], T(IntOrString(expect6[i]))); + } + } @@ -3588,28 +3518,84 @@ using ExtList = QList<T>; void tst_Collections::insert_remove_loop() { - insert_remove_loop_impl<ExtList<int> >(); - insert_remove_loop_impl<ExtList<QString> >(); - insert_remove_loop_impl<QVector<int> >(); - insert_remove_loop_impl<QVector<QString> >(); - insert_remove_loop_impl<QVarLengthArray<int> >(); - insert_remove_loop_impl<QVarLengthArray<QString> >(); - insert_remove_loop_impl<QVarLengthArray<int, 10> >(); - insert_remove_loop_impl<QVarLengthArray<QString, 10> >(); - insert_remove_loop_impl<QVarLengthArray<int, 3> >(); - insert_remove_loop_impl<QVarLengthArray<QString, 3> >(); - insert_remove_loop_impl<QVarLengthArray<int, 15> >(); - insert_remove_loop_impl<QVarLengthArray<QString, 15> >(); - - insert_remove_loop_impl<ExtList<std::string> >(); - insert_remove_loop_impl<QVector<std::string> >(); - insert_remove_loop_impl<QVarLengthArray<std::string> >(); - insert_remove_loop_impl<QVarLengthArray<std::string, 10> >(); - insert_remove_loop_impl<QVarLengthArray<std::string, 3> >(); - insert_remove_loop_impl<QVarLengthArray<std::string, 15> >(); + insert_remove_loop_impl<ExtList<int>>(); + insert_remove_loop_impl<ExtList<QString>>(); + insert_remove_loop_impl<QList<int>>(); + insert_remove_loop_impl<QList<QString>>(); + insert_remove_loop_impl<QVarLengthArray<int>>(); + insert_remove_loop_impl<QVarLengthArray<QString>>(); + insert_remove_loop_impl<QVarLengthArray<int, 10>>(); + insert_remove_loop_impl<QVarLengthArray<QString, 10>>(); + insert_remove_loop_impl<QVarLengthArray<int, 3>>(); + insert_remove_loop_impl<QVarLengthArray<QString, 3>>(); + insert_remove_loop_impl<QVarLengthArray<int, 15>>(); + insert_remove_loop_impl<QVarLengthArray<QString, 15>>(); + + insert_remove_loop_impl<ExtList<std::string>>(); + insert_remove_loop_impl<QList<std::string>>(); + insert_remove_loop_impl<QVarLengthArray<std::string>>(); + insert_remove_loop_impl<QVarLengthArray<std::string, 10>>(); + insert_remove_loop_impl<QVarLengthArray<std::string, 3>>(); + insert_remove_loop_impl<QVarLengthArray<std::string, 15>>(); +} + +template <template<typename, typename> typename Container> +void tst_Collections::detachAssociativeContainerImpl() +{ + constexpr int RUNS = 50; + + for (int run = 0; run < RUNS; ++run) { + Container<int, int> container; + + for (int i = 0; i < 1'000; ++i) { + container.insert(i, i); + container.insert(i, i); // for multi-keyed containers + } + + const auto it = container.constBegin(); + const auto &key = it.key(); + const auto &value = it.value(); + const auto keyCopy = key; + const auto valueCopy = value; + + QSemaphore sem1, sem2; + auto detachInAnotherThread = [&sem1, &sem2, copy = container]() mutable { + sem1.release(); + sem2.acquire(); + copy.clear(); // <== + }; + + QScopedPointer thread(QThread::create(std::move(detachInAnotherThread))); + thread->start(); + + sem2.release(); + sem1.acquire(); + + // The following call may detach (because the container is + // shared), and then use key/value to search+insert. + // + // This means that key/value, as references, have to be valid + // throughout the insertion procedure. Note that they are + // references into the container *itself*; and that the + // insertion procedure is working on a new (detached) copy of + // the container's payload. + // + // There is now a possible scenario in which the clear() above + // finds the copy's refcount at 1, hence not perform a detach, + // and destroy its payload. But key/value were references into + // *that* payload (it's the payload that `container` itself + // used to share). If inside insert() we don't take extra + // measures to keep the payload alive, now they're dangling and + // the insertion will malfunction. + + container.insert(key, value); + + QVERIFY(container.contains(keyCopy)); + QCOMPARE(container.value(keyCopy), valueCopy); + + thread->wait(); + } } - - QTEST_APPLESS_MAIN(tst_Collections) #include "tst_collections.moc" diff --git a/tests/auto/corelib/tools/containerapisymmetry/CMakeLists.txt b/tests/auto/corelib/tools/containerapisymmetry/CMakeLists.txt new file mode 100644 index 0000000000..0ae1092043 --- /dev/null +++ b/tests/auto/corelib/tools/containerapisymmetry/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_containerapisymmetry Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_containerapisymmetry LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_containerapisymmetry + SOURCES + tst_containerapisymmetry.cpp +) + +## Scopes: +##################################################################### diff --git a/tests/auto/corelib/tools/containerapisymmetry/containerapisymmetry.pro b/tests/auto/corelib/tools/containerapisymmetry/containerapisymmetry.pro deleted file mode 100644 index efdb7fc2df..0000000000 --- a/tests/auto/corelib/tools/containerapisymmetry/containerapisymmetry.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_containerapisymmetry -SOURCES += tst_containerapisymmetry.cpp -QT = core testlib - -DEFINES -= QT_NO_LINKED_LIST diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp index 0e4517e740..5eb9dbfa36 100644 --- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp +++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp @@ -1,72 +1,47 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include "qbytearray.h" -#include "qlinkedlist.h" +#include "qdebug.h" +#include "qhash.h" +#include "qmap.h" +#include "qset.h" #include "qlist.h" #include "qstring.h" #include "qvarlengtharray.h" -#include "qvector.h" -#include "qhash.h" -#include "qdebug.h" #include <algorithm> #include <functional> -#include <vector> // for reference +#include <iostream> #include <list> #include <set> +#include <sstream> #include <map> - -// MSVC has these containers from the Standard Library, but it lacks -// a __has_include mechanism (that we need to use for other stdlibs). -// For the sake of increasing our test coverage, work around the issue. - -#ifdef Q_CC_MSVC -#define COMPILER_HAS_STDLIB_INCLUDE(x) 1 -#else -#define COMPILER_HAS_STDLIB_INCLUDE(x) __has_include(x) -#endif - -#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>) #include <forward_list> -#endif -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) #include <unordered_set> -#endif -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) #include <unordered_map> -#endif +#include <q20vector.h> // For reference + +QT_BEGIN_NAMESPACE +std::ostream &operator<<(std::ostream &os, const QChar &c) +{ + Q_ASSERT(c == QLatin1Char{c.toLatin1()}); + return os << c.toLatin1(); +} +std::istream &operator>>(std::istream &os, QChar &c) +{ + char cL1; + os >> cL1; + c = QLatin1Char{cL1}; + return os; +} +QT_END_NAMESPACE struct Movable { - explicit Movable(int i = 0) Q_DECL_NOTHROW + explicit Movable(int i = 0) noexcept : i(i) { ++instanceCount; @@ -85,14 +60,19 @@ struct Movable int i; static int instanceCount; + + friend std::ostream &operator<<(std::ostream &os, const Movable &m) + { return os << m.i; } + friend std::istream &operator>>(std::istream &os, Movable &m) + { return os >> m.i; } }; int Movable::instanceCount = 0; -bool operator==(Movable lhs, Movable rhs) Q_DECL_NOTHROW { return lhs.i == rhs.i; } -bool operator!=(Movable lhs, Movable rhs) Q_DECL_NOTHROW { return lhs.i != rhs.i; } -bool operator<(Movable lhs, Movable rhs) Q_DECL_NOTHROW { return lhs.i < rhs.i; } +bool operator==(Movable lhs, Movable rhs) noexcept { return lhs.i == rhs.i; } +bool operator!=(Movable lhs, Movable rhs) noexcept { return lhs.i != rhs.i; } +bool operator<(Movable lhs, Movable rhs) noexcept { return lhs.i < rhs.i; } -uint qHash(Movable m, uint seed = 0) Q_DECL_NOTHROW { return qHash(m.i, seed); } +size_t qHash(Movable m, size_t seed = 0) noexcept { return qHash(m.i, seed); } QDebug &operator<<(QDebug &d, Movable m) { const QDebugStateSaver saver(d); @@ -100,12 +80,12 @@ QDebug &operator<<(QDebug &d, Movable m) } QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(Movable, Q_RELOCATABLE_TYPE); QT_END_NAMESPACE struct Complex { - explicit Complex(int i = 0) Q_DECL_NOTHROW + explicit Complex(int i = 0) noexcept : i(i) { ++instanceCount; @@ -121,17 +101,24 @@ struct Complex { --instanceCount; } + constexpr Complex &operator=(const Complex &o) noexcept + { i = o.i; return *this; } int i; static int instanceCount; + + friend std::ostream &operator<<(std::ostream &os, const Complex &c) + { return os << c.i; } + friend std::istream &operator>>(std::istream &os, Complex &c) + { return os >> c.i; } }; int Complex::instanceCount = 0; -bool operator==(Complex lhs, Complex rhs) Q_DECL_NOTHROW { return lhs.i == rhs.i; } -bool operator!=(Complex lhs, Complex rhs) Q_DECL_NOTHROW { return lhs.i != rhs.i; } -bool operator<(Complex lhs, Complex rhs) Q_DECL_NOTHROW { return lhs.i < rhs.i; } +bool operator==(Complex lhs, Complex rhs) noexcept { return lhs.i == rhs.i; } +bool operator!=(Complex lhs, Complex rhs) noexcept { return lhs.i != rhs.i; } +bool operator<(Complex lhs, Complex rhs) noexcept { return lhs.i < rhs.i; } -uint qHash(Complex c, uint seed = 0) Q_DECL_NOTHROW { return qHash(c.i, seed); } +size_t qHash(Complex c, size_t seed = 0) noexcept { return qHash(c.i, seed); } QDebug &operator<<(QDebug &d, Complex c) { const QDebugStateSaver saver(d); @@ -141,7 +128,7 @@ QDebug &operator<<(QDebug &d, Complex c) struct DuplicateStrategyTestType { - explicit DuplicateStrategyTestType(int i = 0) Q_DECL_NOTHROW + explicit DuplicateStrategyTestType(int i = 0) noexcept : i(i), j(++counter) { @@ -157,27 +144,27 @@ int DuplicateStrategyTestType::counter = 0; // only look at the i member, not j. j allows us to identify which instance // gets inserted in containers that don't allow for duplicates -bool operator==(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW +bool operator==(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) noexcept { return lhs.i == rhs.i; } -bool operator!=(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW +bool operator!=(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) noexcept { return lhs.i != rhs.i; } -bool operator<(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW +bool operator<(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) noexcept { return lhs.i < rhs.i; } -uint qHash(DuplicateStrategyTestType c, uint seed = 0) Q_DECL_NOTHROW +size_t qHash(DuplicateStrategyTestType c, size_t seed = 0) noexcept { return qHash(c.i, seed); } -bool reallyEqual(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW +bool reallyEqual(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) noexcept { return lhs.i == rhs.i && lhs.j == rhs.j; } @@ -193,7 +180,7 @@ namespace std { template<> struct hash<Movable> { - std::size_t operator()(Movable m) const Q_DECL_NOTHROW + std::size_t operator()(Movable m) const noexcept { return hash<int>()(m.i); } @@ -202,7 +189,7 @@ struct hash<Movable> template<> struct hash<Complex> { - std::size_t operator()(Complex m) const Q_DECL_NOTHROW + std::size_t operator()(Complex m) const noexcept { return hash<int>()(m.i); } @@ -211,7 +198,7 @@ struct hash<Complex> template<> struct hash<DuplicateStrategyTestType> { - std::size_t operator()(DuplicateStrategyTestType m) const Q_DECL_NOTHROW + std::size_t operator()(DuplicateStrategyTestType m) const noexcept { return hash<int>()(m.i); } @@ -224,20 +211,7 @@ template<typename T> class VarLengthArray : public QVarLengthArray<T> { public: -#ifdef Q_COMPILER_INHERITING_CONSTRUCTORS using QVarLengthArray<T>::QVarLengthArray; -#else - template<typename InputIterator> - VarLengthArray(InputIterator first, InputIterator last) - : QVarLengthArray<T>(first, last) - { - } - - VarLengthArray(std::initializer_list<T> args) - : QVarLengthArray<T>(args) - { - } -#endif }; class tst_ContainerApiSymmetry : public QObject @@ -270,19 +244,14 @@ private Q_SLOTS: void ranged_ctor_std_vector_Complex() { ranged_ctor_non_associative_impl<std::vector<Complex>>(); } void ranged_ctor_std_vector_duplicates_strategy() { non_associative_container_duplicates_strategy<std::vector>(); } - void ranged_ctor_QVector_int() { ranged_ctor_non_associative_impl<QVector<int>>(); } - void ranged_ctor_QVector_char() { ranged_ctor_non_associative_impl<QVector<char>>(); } - void ranged_ctor_QVector_QChar() { ranged_ctor_non_associative_impl<QVector<QChar>>(); } - void ranged_ctor_QVector_Movable() { ranged_ctor_non_associative_impl<QVector<Movable>>(); } - void ranged_ctor_QVector_Complex() { ranged_ctor_non_associative_impl<QVector<Complex>>(); } - void ranged_ctor_QVector_duplicates_strategy() { non_associative_container_duplicates_strategy<QVector>(); } - void ranged_ctor_QVarLengthArray_int() { ranged_ctor_non_associative_impl<QVarLengthArray<int>>(); } void ranged_ctor_QVarLengthArray_Movable() { ranged_ctor_non_associative_impl<QVarLengthArray<Movable>>(); } void ranged_ctor_QVarLengthArray_Complex() { ranged_ctor_non_associative_impl<QVarLengthArray<Complex>>(); } void ranged_ctor_QVarLengthArray_duplicates_strategy() { non_associative_container_duplicates_strategy<VarLengthArray>(); } // note the VarLengthArray passed void ranged_ctor_QList_int() { ranged_ctor_non_associative_impl<QList<int>>(); } + void ranged_ctor_QList_char() { ranged_ctor_non_associative_impl<QList<char>>(); } + void ranged_ctor_QList_QChar() { ranged_ctor_non_associative_impl<QList<QChar>>(); } void ranged_ctor_QList_Movable() { ranged_ctor_non_associative_impl<QList<Movable>>(); } void ranged_ctor_QList_Complex() { ranged_ctor_non_associative_impl<QList<Complex>>(); } void ranged_ctor_QList_duplicates_strategy() { non_associative_container_duplicates_strategy<QList>(); } @@ -292,42 +261,10 @@ private Q_SLOTS: void ranged_ctor_std_list_Complex() { ranged_ctor_non_associative_impl<std::list<Complex>>(); } void ranged_ctor_std_list_duplicates_strategy() { non_associative_container_duplicates_strategy<std::list>(); } - void ranged_ctor_std_forward_list_int() { -#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>) - ranged_ctor_non_associative_impl<std::forward_list<int>>(); -#else - QSKIP("<forward_list> is needed for this test"); -#endif - } - - void ranged_ctor_std_forward_list_Movable() { -#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>) - ranged_ctor_non_associative_impl<std::forward_list<Movable>>(); -#else - QSKIP("<forward_list> is needed for this test"); -#endif - } - - void ranged_ctor_std_forward_list_Complex() { -#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>) - ranged_ctor_non_associative_impl<std::forward_list<Complex>>(); -#else - QSKIP("<forward_list> is needed for this test"); -#endif - } - - void ranged_ctor_std_forward_list_duplicates_strategy() { -#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>) - non_associative_container_duplicates_strategy<std::forward_list>(); -#else - QSKIP("<forward_list> is needed for this test"); -#endif - } - - void ranged_ctor_QLinkedList_int() { ranged_ctor_non_associative_impl<QLinkedList<int>>(); } - void ranged_ctor_QLinkedList_Movable() { ranged_ctor_non_associative_impl<QLinkedList<Movable>>(); } - void ranged_ctor_QLinkedList_Complex() { ranged_ctor_non_associative_impl<QLinkedList<Complex>>(); } - void ranged_ctor_QLinkedList_duplicates_strategy() { non_associative_container_duplicates_strategy<QLinkedList>(); } + void ranged_ctor_std_forward_list_int() { ranged_ctor_non_associative_impl<std::forward_list<int>>(); } + void ranged_ctor_std_forward_list_Movable() {ranged_ctor_non_associative_impl<std::forward_list<Movable>>(); } + void ranged_ctor_std_forward_list_Complex() { ranged_ctor_non_associative_impl<std::forward_list<Complex>>(); } + void ranged_ctor_std_forward_list_duplicates_strategy() { non_associative_container_duplicates_strategy<std::forward_list>(); } void ranged_ctor_std_set_int() { ranged_ctor_non_associative_impl<std::set<int>>(); } void ranged_ctor_std_set_Movable() { ranged_ctor_non_associative_impl<std::set<Movable>>(); } @@ -339,70 +276,15 @@ private Q_SLOTS: void ranged_ctor_std_multiset_Complex() { ranged_ctor_non_associative_impl<std::multiset<Complex>>(); } void ranged_ctor_std_multiset_duplicates_strategy() { non_associative_container_duplicates_strategy<std::multiset>(); } - void ranged_ctor_std_unordered_set_int() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) - ranged_ctor_non_associative_impl<std::unordered_set<int>>(); -#else - QSKIP("<unordered_set> is needed for this test"); -#endif - } + void ranged_ctor_std_unordered_set_int() { ranged_ctor_non_associative_impl<std::unordered_set<int>>(); } + void ranged_ctor_std_unordered_set_Movable() { ranged_ctor_non_associative_impl<std::unordered_set<Movable>>(); } + void ranged_ctor_std_unordered_set_Complex() { ranged_ctor_non_associative_impl<std::unordered_set<Complex>>(); } + void ranged_ctor_std_unordered_set_duplicates_strategy() { non_associative_container_duplicates_strategy<std::unordered_set>(); } - void ranged_ctor_std_unordered_set_Movable() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) - ranged_ctor_non_associative_impl<std::unordered_set<Movable>>(); -#else - QSKIP("<unordered_set> is needed for this test"); -#endif - } - - void ranged_ctor_std_unordered_set_Complex() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) - ranged_ctor_non_associative_impl<std::unordered_set<Complex>>(); -#else - QSKIP("<unordered_set> is needed for this test"); -#endif - } - - void ranged_ctor_unordered_set_duplicates_strategy() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) - non_associative_container_duplicates_strategy<std::unordered_set>(); -#else - QSKIP("<unordered_set> is needed for this test"); -#endif - } - - - void ranged_ctor_std_unordered_multiset_int() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) - ranged_ctor_non_associative_impl<std::unordered_multiset<int>>(); -#else - QSKIP("<unordered_set> is needed for this test"); -#endif - } - - void ranged_ctor_std_unordered_multiset_Movable() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) - ranged_ctor_non_associative_impl<std::unordered_multiset<Movable>>(); -#else - QSKIP("<unordered_set> is needed for this test"); -#endif - } - - void ranged_ctor_std_unordered_multiset_Complex() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) - ranged_ctor_non_associative_impl<std::unordered_multiset<Complex>>(); -#else - QSKIP("<unordered_set> is needed for this test"); -#endif - } - - void ranged_ctor_std_unordered_multiset_duplicates_strategy() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) - non_associative_container_duplicates_strategy<std::unordered_multiset>(); -#else - QSKIP("<unordered_set> is needed for this test"); -#endif - } + void ranged_ctor_std_unordered_multiset_int() { ranged_ctor_non_associative_impl<std::unordered_multiset<int>>(); } + void ranged_ctor_std_unordered_multiset_Movable() { ranged_ctor_non_associative_impl<std::unordered_multiset<Movable>>(); } + void ranged_ctor_std_unordered_multiset_Complex() { ranged_ctor_non_associative_impl<std::unordered_multiset<Complex>>(); } + void ranged_ctor_std_unordered_multiset_duplicates_strategy() { non_associative_container_duplicates_strategy<std::unordered_multiset>(); } void ranged_ctor_QSet_int() { ranged_ctor_non_associative_impl<QSet<int>>(); } void ranged_ctor_QSet_Movable() { ranged_ctor_non_associative_impl<QSet<Movable>>(); } @@ -418,57 +300,17 @@ private Q_SLOTS: void ranged_ctor_std_multimap_Movable() { ranged_ctor_associative_impl<std::multimap<Movable, int>>(); } void ranged_ctor_std_multimap_Complex() { ranged_ctor_associative_impl<std::multimap<Complex, int>>(); } - void ranged_ctor_unordered_map_int() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) - ranged_ctor_associative_impl<std::unordered_map<int, int>>(); -#else - QSKIP("<unordered_map> is needed for this test"); -#endif - } - - void ranged_ctor_unordered_map_Movable() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) - ranged_ctor_associative_impl<std::unordered_map<Movable, Movable>>(); -#else - QSKIP("<unordered_map> is needed for this test"); -#endif - } - - void ranged_ctor_unordered_map_Complex() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) - ranged_ctor_associative_impl<std::unordered_map<Complex, Complex>>(); -#else - QSKIP("<unordered_map> is needed for this test"); -#endif - } + void ranged_ctor_unordered_map_int() { ranged_ctor_associative_impl<std::unordered_map<int, int>>(); } + void ranged_ctor_unordered_map_Movable() { ranged_ctor_associative_impl<std::unordered_map<Movable, Movable>>(); } + void ranged_ctor_unordered_map_Complex() { ranged_ctor_associative_impl<std::unordered_map<Complex, Complex>>(); } void ranged_ctor_QHash_int() { ranged_ctor_associative_impl<QHash<int, int>>(); } void ranged_ctor_QHash_Movable() { ranged_ctor_associative_impl<QHash<Movable, int>>(); } void ranged_ctor_QHash_Complex() { ranged_ctor_associative_impl<QHash<Complex, int>>(); } - void ranged_ctor_unordered_multimap_int() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) - ranged_ctor_associative_impl<std::unordered_multimap<int, int>>(); -#else - QSKIP("<unordered_map> is needed for this test"); -#endif - } - - void ranged_ctor_unordered_multimap_Movable() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) - ranged_ctor_associative_impl<std::unordered_multimap<Movable, Movable>>(); -#else - QSKIP("<unordered_map> is needed for this test"); -#endif - } - - void ranged_ctor_unordered_multimap_Complex() { -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) - ranged_ctor_associative_impl<std::unordered_multimap<Complex, Complex>>(); -#else - QSKIP("<unordered_map> is needed for this test"); -#endif - } + void ranged_ctor_unordered_multimap_int() { ranged_ctor_associative_impl<std::unordered_multimap<int, int>>(); } + void ranged_ctor_unordered_multimap_Movable() { ranged_ctor_associative_impl<std::unordered_multimap<Movable, Movable>>(); } + void ranged_ctor_unordered_multimap_Complex() { ranged_ctor_associative_impl<std::unordered_multimap<Complex, Complex>>(); } void ranged_ctor_QMultiHash_int() { ranged_ctor_associative_impl<QMultiHash<int, int>>(); } void ranged_ctor_QMultiHash_Movable() { ranged_ctor_associative_impl<QMultiHash<Movable, int>>(); } @@ -476,19 +318,117 @@ private Q_SLOTS: private: template <typename Container> + void resize_impl() const; + +private Q_SLOTS: + void resize_std_vector() { resize_impl<std::vector<int>>(); } + void resize_QList() { resize_impl<QList<qintptr>>(); } + void resize_QVarLengthArray() { resize_impl<QVarLengthArray<int>>(); } + void resize_QString() { resize_impl<QString>(); } + void resize_QByteArray() { resize_impl<QByteArray>(); } + +private: + template <typename Container> + void copesWithValueTypesWithConstMembers_impl(); + + struct ConstMember { + #ifndef __cpp_aggregate_paren_init // also check that we can emplace aggregates (C++20 only) + explicit ConstMember(int n) : n(n) {} + #endif + const int n; + + friend bool operator==(const ConstMember &lhs, const ConstMember &rhs) noexcept + { return lhs.n == rhs.n; } + friend bool operator!=(const ConstMember &lhs, const ConstMember &rhs) noexcept + { return !(lhs == rhs); } + }; + +private Q_SLOTS: + void copesWithValueTypesWithConstMembers_std_vector() { copesWithValueTypesWithConstMembers_impl<std::vector<ConstMember>>(); } + void copesWithValueTypesWithConstMembers_QVarLengthArray() { copesWithValueTypesWithConstMembers_impl<QVarLengthArray<ConstMember, 2>>(); } + +private: + template <typename Container> + void assign_impl() const; + +private Q_SLOTS: + void assign_std_vector() { assign_impl<std::vector<int>>(); }; + void assign_std_string() { assign_impl<std::string>(); } + void assign_QVarLengthArray() { assign_impl<QVarLengthArray<int, 4>>(); }; + void assign_QList() { assign_impl<QList<int>>(); } + void assign_QByteArray() { assign_impl<QByteArray>(); } + void assign_QString() { assign_impl<QString>(); } + +private: + template <typename Container> void front_back_impl() const; private Q_SLOTS: void front_back_std_vector() { front_back_impl<std::vector<int>>(); } - void front_back_QVector() { front_back_impl<QVector<int>>(); } void front_back_QList() { front_back_impl<QList<qintptr>>(); } - void front_back_QLinkedList() { front_back_impl<QLinkedList<int>>(); } void front_back_QVarLengthArray() { front_back_impl<QVarLengthArray<int>>(); } void front_back_QString() { front_back_impl<QString>(); } - void front_back_QStringRef() { front_back_impl<QStringRef>(); } void front_back_QStringView() { front_back_impl<QStringView>(); } void front_back_QLatin1String() { front_back_impl<QLatin1String>(); } void front_back_QByteArray() { front_back_impl<QByteArray>(); } + +private: + template <typename Container> + void erase_impl() const; + + template <typename Container> + void erase_if_impl() const; + + template <typename Container> + void erase_if_associative_impl() const; + + template <typename Container> + void member_erase_impl() const; + + template <typename Container> + void member_erase_associative_impl() const; + + template <typename Container> + void member_erase_set_impl() const; + +private Q_SLOTS: + void erase_QList() { erase_impl<QList<int>>(); } + void erase_QVarLengthArray() { erase_impl<QVarLengthArray<int>>(); } + void erase_QString() { erase_impl<QString>(); } + void erase_QByteArray() { erase_impl<QByteArray>(); } + void erase_std_vector() { erase_impl<std::vector<int>>(); } + + void erase_if_QList() { erase_if_impl<QList<int>>(); } + void erase_if_QVarLengthArray() { erase_if_impl<QVarLengthArray<int>>(); } + void erase_if_QSet() { erase_if_impl<QSet<int>>(); } + void erase_if_QString() { erase_if_impl<QString>(); } + void erase_if_QByteArray() { erase_if_impl<QByteArray>(); } + void erase_if_std_vector() { erase_if_impl<std::vector<int>>(); } + void erase_if_QMap() { erase_if_associative_impl<QMap<int, int>>(); } + void erase_if_QMultiMap() {erase_if_associative_impl<QMultiMap<int, int>>(); } + void erase_if_QHash() { erase_if_associative_impl<QHash<int, int>>(); } + void erase_if_QMultiHash() { erase_if_associative_impl<QMultiHash<int, int>>(); } + + void member_erase_QList() { member_erase_impl<QList<int>>(); } + void member_erase_QVarLengthArray() { member_erase_impl<QVarLengthArray<int>>(); } + void member_erase_QString() { member_erase_impl<QString>(); } + void member_erase_QByteArray() { member_erase_impl<QByteArray>(); } + void member_erase_QSet() { member_erase_set_impl<QSet<int>>(); } + + void member_erase_QMap() { member_erase_associative_impl<QMap<int, int>>(); } + void member_erase_QMultiMap() {member_erase_associative_impl<QMultiMap<int, int>>(); } + void member_erase_QHash() { member_erase_associative_impl<QHash<int, int>>(); } + void member_erase_QMultiHash() { member_erase_associative_impl<QMultiHash<int, int>>(); } + +private: + template <typename Container> + void keyValueRange_impl() const; + +private Q_SLOTS: + void keyValueRange_QMap() { keyValueRange_impl<QMap<int, int>>(); } + void keyValueRange_QMultiMap() { keyValueRange_impl<QMultiMap<int, int>>(); } + void keyValueRange_QHash() { keyValueRange_impl<QHash<int, int>>(); } + void keyValueRange_QMultiHash() { keyValueRange_impl<QMultiHash<int, int>>(); } }; void tst_ContainerApiSymmetry::init() @@ -547,12 +487,25 @@ void tst_ContainerApiSymmetry::ranged_ctor_non_associative_impl() const // from itself const Container c4(reference.begin(), reference.end()); + // from stringsteam (= pure input_iterator) + const Container c5 = [&] { + { + std::stringstream ss; + for (auto &v : values1) + ss << v << ' '; + ss.seekg(0); + return Container(std::istream_iterator<V>{ss}, + std::istream_iterator<V>{}); + } + }(); + QCOMPARE(c1, reference); QCOMPARE(c2a, reference); QCOMPARE(c2b, reference); QCOMPARE(c3a, reference); QCOMPARE(c3b, reference); QCOMPARE(c4, reference); + QCOMPARE(c5, reference); } @@ -571,7 +524,7 @@ template<typename ... T> struct ContainerDuplicatedValuesStrategy<std::vector<T...>> : ContainerAcceptsDuplicateValues {}; template<typename ... T> -struct ContainerDuplicatedValuesStrategy<QVector<T...>> : ContainerAcceptsDuplicateValues {}; +struct ContainerDuplicatedValuesStrategy<QList<T...>> : ContainerAcceptsDuplicateValues {}; template<typename ... T> struct ContainerDuplicatedValuesStrategy<QVarLengthArray<T...>> : ContainerAcceptsDuplicateValues {}; @@ -582,13 +535,8 @@ struct ContainerDuplicatedValuesStrategy<VarLengthArray<T...>> : ContainerAccept template<typename ... T> struct ContainerDuplicatedValuesStrategy<std::list<T...>> : ContainerAcceptsDuplicateValues {}; -#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>) template<typename ... T> struct ContainerDuplicatedValuesStrategy<std::forward_list<T...>> : ContainerAcceptsDuplicateValues {}; -#endif - -template<typename ... T> -struct ContainerDuplicatedValuesStrategy<QLinkedList<T...>> : ContainerAcceptsDuplicateValues {}; // assuming https://cplusplus.github.io/LWG/lwg-active.html#2844 resolution template<typename ... T> @@ -597,14 +545,12 @@ struct ContainerDuplicatedValuesStrategy<std::set<T...>> : ContainerRejectsDupli template<typename ... T> struct ContainerDuplicatedValuesStrategy<std::multiset<T...>> : ContainerAcceptsDuplicateValues {}; -#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) // assuming https://cplusplus.github.io/LWG/lwg-active.html#2844 resolution template<typename ... T> struct ContainerDuplicatedValuesStrategy<std::unordered_set<T...>> : ContainerRejectsDuplicateValues {}; template<typename ... T> struct ContainerDuplicatedValuesStrategy<std::unordered_multiset<T...>> : ContainerAcceptsDuplicateValues {}; -#endif template<typename ... T> struct ContainerDuplicatedValuesStrategy<QSet<T...>> : ContainerRejectsDuplicateValues {}; @@ -782,36 +728,539 @@ template <typename Container> Container make(int size) { Container c; - int i = 1; - while (size--) - c.push_back(typename Container::value_type(i++)); + c.reserve(size); + using V = typename Container::value_type; + int i = 0; + std::generate_n(std::inserter(c, c.end()), size, [&i] { return V(++i); }); + return c; +} + +template <typename Container> +Container makeAssociative(int size) +{ + using K = typename Container::key_type; + using V = typename Container::mapped_type; + Container c; + for (int i = 1; i <= size; ++i) + c.insert(K(i), V(i)); return c; } static QString s_string = QStringLiteral("\1\2\3\4\5\6\7"); -template <> QStringRef make(int size) { return s_string.leftRef(size); } +template <> QString make(int size) { return s_string.left(size); } template <> QStringView make(int size) { return QStringView(s_string).left(size); } template <> QLatin1String make(int size) { return QLatin1String("\1\2\3\4\5\6\7", size); } +template <> QByteArray make(int size) { return QByteArray("\1\2\3\4\5\6\7", size); } template <typename T> T clean(T &&t) { return std::forward<T>(t); } inline char clean(QLatin1Char ch) { return ch.toLatin1(); } template <typename Container> +void tst_ContainerApiSymmetry::resize_impl() const +{ + using V = typename Container::value_type; + using S = typename Container::size_type; + auto c = make<Container>(3); + QCOMPARE(c.size(), S(3)); + c.resize(4, V(5)); + QCOMPARE(std::size(c), S(4)); + QCOMPARE(c.back(), V(5)); + + // ctor/resize symmetry: + { + Container c1(S(5), V(4)); + QCOMPARE(c1.size(), S(5)); + + Container c2; + c2.resize(S(5), V(4)); + QCOMPARE(c2.size(), S(5)); + + QCOMPARE(c1, c2); + } +} + +template <typename T> +[[maybe_unused]] +constexpr bool is_vector_v = false; +template <typename...Args> +constexpr bool is_vector_v<std::vector<Args...>> = true; + +template <typename Container, typename Value> +void wrap_resize(Container &c, typename Container::size_type n, const Value &v) +{ +#ifdef __GLIBCXX__ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83981 + if constexpr (is_vector_v<Container>) { + while (c.size() < n) + c.push_back(v); + } else +#endif + { + c.resize(n, v); + } +} + +template <typename Container> +void tst_ContainerApiSymmetry::copesWithValueTypesWithConstMembers_impl() +{ + // The problem: + // + // using V = ConstMember; + // V v{42}; + // assert(v.n == 42); // OK + // new (&v) V{24}; + // assert(v.n == 24); // UB in C++17: v.n could still be 42 (C++17 [basic.life]/8) + // // OK in C++20 (C++20 [basic.life]/8) + // assert(std::launder(&v)->n == 24); // OK + // assert(v.n == 24); // _still_ UB! + // + // Containers: + // - must not expose this problem + // - must compile in the first place, even though V + // - is not assignable + // - is not default-constructible + + using S = typename Container::size_type; + using V = typename Container::value_type; + + Container c; + // the following are all functions that by rights should not require the type to be + // - default-constructible + // - assignable + // make sure they work + c.reserve(S(5)); + c.shrink_to_fit(); + wrap_resize(c, 1, V(42)); + QCOMPARE(c[0], V(42)); + wrap_resize(c, 2, V(48)); + QCOMPARE(c[0], V(42)); + QCOMPARE(c[1], V(48)); + c.clear(); + c.emplace_back(24); + QCOMPARE(c.front(), V(24)); + c.push_back(V(41)); + QCOMPARE(c.back(), V(41)); + { + const auto v142 = V(142); + c.push_back(v142); + } + QCOMPARE(c.size(), S(3)); + QCOMPARE(c[0], V(24)); + QCOMPARE(c[1], V(41)); + QCOMPARE(c[2], V(142)); +} + +template <typename Container> +void tst_ContainerApiSymmetry::assign_impl() const +{ +#define CHECK(Arr, ComparisonData, Sz_n, Sz_e) \ + QCOMPARE(Sz_n, Sz_e); \ + for (const auto &e : Arr) \ + QCOMPARE(e, ComparisonData) \ + /*end*/ +#define RET_CHECK(...) \ + do { \ + if constexpr (std::is_void_v<decltype( __VA_ARGS__ )>) { \ + /* e.g. std::vector */ \ + __VA_ARGS__ ; \ + } else { \ + /* e.g. std::basic_string */ \ + auto &&r = __VA_ARGS__ ; \ + QCOMPARE_EQ(&r, &c); \ + } \ + } while (false) \ + /* end */ + using V = typename Container::value_type; + using S = typename Container::size_type; + auto tData = V(65); + { + // fill version + auto c = make<Container>(4); + const S oldCapacity = c.capacity(); + RET_CHECK(c.assign(4, tData)); + CHECK(c, tData, c.size(), S(4)); + QCOMPARE_EQ(c.capacity(), oldCapacity); + + tData = V(66); + c.assign(8, tData); // may reallocate + CHECK(c, tData, c.size(), S(8)); + + const S grownCapacity = c.capacity(); + c.assign(0, tData); + CHECK(c, tData, c.size(), S(0)); + QCOMPARE_EQ(c.capacity(), grownCapacity); + } + { + // range version for non input iterator + auto c = make<Container>(4); + auto iter = make<Container>(1); + + iter.assign(8, tData); + RET_CHECK(c.assign(iter.begin(), iter.end())); // may reallocate + CHECK(c, tData, c.size(), S(8)); + + const S oldCapacity = c.capacity(); + c.assign(iter.begin(), iter.begin()); + CHECK(c, tData, c.size(), S(0)); + QCOMPARE_EQ(c.capacity(), oldCapacity); + } + { + // range version for input iterator + auto c = make<Container>(4); + const S oldCapacity = c.capacity(); + + std::stringstream ss; + ss << tData << ' ' << tData << ' '; + RET_CHECK(c.assign(std::istream_iterator<V>{ss}, std::istream_iterator<V>{})); + CHECK(c, tData, c.size(), S(2)); + QCOMPARE_EQ(c.capacity(), oldCapacity); + + ss.str(""); + ss.clear(); + tData = V(66); + ss << tData << ' ' << tData << ' ' << tData << ' ' << tData << ' '; + c.assign(std::istream_iterator<V>{ss}, std::istream_iterator<V>{}); + CHECK(c, tData, c.size(), S(4)); + QCOMPARE_EQ(c.capacity(), oldCapacity); + + ss.str(""); + ss.clear(); + tData = V(67); + ss << tData << ' ' << tData << ' ' << tData << ' ' << tData << ' ' + << tData << ' ' << tData << ' ' << tData << ' '; + c.assign(std::istream_iterator<V>{ss}, std::istream_iterator<V>{}); // may reallocate + CHECK(c, tData, c.size(), S(7)); + } + { + // initializer-list version + auto c = make<Container>(4); + const S oldCapacity = c.capacity(); + std::initializer_list<V> list = {tData, tData, tData}; + RET_CHECK(c.assign(list)); + CHECK(c, tData, c.size(), S(3)); + QCOMPARE_EQ(c.capacity(), oldCapacity); + } + +#undef RET_CHECK +#undef CHECK +} + +template<typename Container> void tst_ContainerApiSymmetry::front_back_impl() const { using V = typename Container::value_type; auto c1 = make<Container>(1); QCOMPARE(clean(c1.front()), V(1)); QCOMPARE(clean(c1.back()), V(1)); - QCOMPARE(clean(qAsConst(c1).front()), V(1)); - QCOMPARE(clean(qAsConst(c1).back()), V(1)); + QCOMPARE(clean(std::as_const(c1).front()), V(1)); + QCOMPARE(clean(std::as_const(c1).back()), V(1)); auto c2 = make<Container>(2); QCOMPARE(clean(c2.front()), V(1)); QCOMPARE(clean(c2.back()), V(2)); - QCOMPARE(clean(qAsConst(c2).front()), V(1)); - QCOMPARE(clean(qAsConst(c2).back()), V(2)); + QCOMPARE(clean(std::as_const(c2).front()), V(1)); + QCOMPARE(clean(std::as_const(c2).back()), V(2)); +} + +namespace { +struct Conv { + template <typename T> + static int toInt(T i) { return i; } + static int toInt(QChar ch) { return ch.unicode(); } +}; +} + +template <typename Container> +void tst_ContainerApiSymmetry::erase_impl() const +{ + using S = typename Container::size_type; + using V = typename Container::value_type; + auto c = make<Container>(7); // {1, 2, 3, 4, 5, 6, 7} + QCOMPARE(c.size(), S(7)); + + using q20::erase; // For std::vector + auto result = erase(c, V(1)); + QCOMPARE(result, S(1)); + QCOMPARE(c.size(), S(6)); + + result = erase(c, V(5)); + QCOMPARE(result, S(1)); + QCOMPARE(c.size(), S(5)); + + result = erase(c, V(123)); + QCOMPARE(result, S(0)); + QCOMPARE(c.size(), S(5)); +} + +template <typename Container> +void tst_ContainerApiSymmetry::erase_if_impl() const +{ + using S = typename Container::size_type; + using V = typename Container::value_type; + auto c = make<Container>(7); // {1, 2, 3, 4, 5, 6, 7} + QCOMPARE(c.size(), S(7)); + + decltype(c.size()) oldSize, count; + + oldSize = c.size(); + count = 0; + + using q20::erase_if; // For std::vector + + S result = erase_if(c, [&](V i) { ++count; return Conv::toInt(i) % 2 == 0; }); + QCOMPARE(result, S(3)); + QCOMPARE(c.size(), S(4)); + QCOMPARE(count, oldSize); + + oldSize = c.size(); + count = 0; + result = erase_if(c, [&](V i) { ++count; return Conv::toInt(i) % 123 == 0; }); + QCOMPARE(result, S(0)); + QCOMPARE(c.size(), S(4)); + QCOMPARE(count, oldSize); + + oldSize = c.size(); + count = 0; + result = erase_if(c, [&](V i) { ++count; return Conv::toInt(i) % 3 == 0; }); + QCOMPARE(result, S(1)); + QCOMPARE(c.size(), S(3)); + QCOMPARE(count, oldSize); + + oldSize = c.size(); + count = 0; + result = erase_if(c, [&](V i) { ++count; return Conv::toInt(i) % 2 == 1; }); + QCOMPARE(result, S(3)); + QCOMPARE(c.size(), S(0)); + QCOMPARE(count, oldSize); +} + +template <typename Container> +void tst_ContainerApiSymmetry::erase_if_associative_impl() const +{ + using S = typename Container::size_type; + using K = typename Container::key_type; + using V = typename Container::mapped_type; + using I = typename Container::iterator; + using P = std::pair<const K &, V &>; + + auto c = makeAssociative<Container>(20); + QCOMPARE(c.size(), S(20)); + + auto result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 2 == 0; }); + QCOMPARE(result, S(10)); + QCOMPARE(c.size(), S(10)); + + result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 3 == 0; }); + QCOMPARE(result, S(3)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 42 == 0; }); + QCOMPARE(result, S(0)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 2 == 1; }); + QCOMPARE(result, S(7)); + QCOMPARE(c.size(), S(0)); + + // same, but with a predicate taking a Qt iterator + c = makeAssociative<Container>(20); + QCOMPARE(c.size(), S(20)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 2 == 0; }); + QCOMPARE(result, S(10)); + QCOMPARE(c.size(), S(10)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 3 == 0; }); + QCOMPARE(result, S(3)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 42 == 0; }); + QCOMPARE(result, S(0)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 2 == 1; }); + QCOMPARE(result, S(7)); + QCOMPARE(c.size(), S(0)); +} + +template <typename Container> +void tst_ContainerApiSymmetry::member_erase_impl() const +{ + using S = typename Container::size_type; + using V = typename Container::value_type; + const S size = 7; + auto c = make<Container>(size); // {1, 2, 3, 4, 5, 6, 7} + QCOMPARE(c.size(), size); + + auto copy = c; + // Container::erase() returns an iterator, not const_iterator + auto it = c.erase(c.cbegin(), c.cbegin()); + static_assert(std::is_same_v<decltype(it), typename Container::iterator>); + QCOMPARE(c.size(), size); + const V newVal{100}; + QCOMPARE_NE(*it, newVal); + *it = newVal; + QCOMPARE(it, c.cbegin()); + QCOMPARE(*c.cbegin(), newVal); + + QCOMPARE(std::find(copy.cbegin(), copy.cend(), newVal), copy.cend()); +} + +template <typename Container> +void tst_ContainerApiSymmetry::member_erase_associative_impl() const +{ + using S = typename Container::size_type; + using V = typename Container::mapped_type; + + const S size = 20; + auto c = makeAssociative<Container>(size); + QCOMPARE(c.size(), size); + + // Verify Container::erase() returns iterator, not const_iterator + auto it = c.erase(c.cbegin()); + static_assert(std::is_same_v<decltype(it), typename Container::iterator>); + QCOMPARE(c.size(), size - 1); + QCOMPARE(it, c.cbegin()); + const auto current = it.value(); + it.value() = current + V(5); + QCOMPARE(c.cbegin().value(),current + V(5)); +} + +template <typename Container> +void tst_ContainerApiSymmetry::member_erase_set_impl() const +{ + using S = typename Container::size_type; + + const S size = 20; + auto c = make<Container>(size); + QCOMPARE(c.size(), size); + + // Verify Container::erase() returns iterator, not const_iterator + auto it = c.erase(c.cbegin()); + static_assert(std::is_same_v<decltype(it), typename Container::iterator>); + QCOMPARE(c.size(), size - 1); + QCOMPARE(it, c.cbegin()); +} + +template <typename Container> +void tst_ContainerApiSymmetry::keyValueRange_impl() const +{ + constexpr int COUNT = 20; + + using K = typename Container::key_type; + using V = typename Container::mapped_type; + QVector<K> keys; + keys.reserve(COUNT); + QVector<V> values; + values.reserve(COUNT); + + auto c = makeAssociative<Container>(COUNT); + auto returnC = [&](){ return c; }; + + const auto verify = [](QVector<K> v, int count, int offset = 0) -> bool { + if (v.size() != count) + return false; + std::sort(v.begin(), v.end()); + for (int i = 0; i < count; ++i) { + // vector is indexed from 0, but makeAssociative starts from 1 + if (v[i] != i + 1 + offset) + return false; + } + return true; + }; + + // Check that the range has the right size + auto range = c.asKeyValueRange(); + QCOMPARE(std::distance(range.begin(), range.end()), COUNT); + + auto constRange = std::as_const(c).asKeyValueRange(); + QCOMPARE(std::distance(constRange.begin(), constRange.end()), COUNT); + + auto rvalueRange = returnC().asKeyValueRange(); + QCOMPARE(std::distance(rvalueRange.begin(), rvalueRange.end()), COUNT); + + // auto, mutating + keys.clear(); values.clear(); + for (auto [key, value] : c.asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value); + QCOMPARE(c.value(key), value); + ++value; + QCOMPARE(key, value - 1); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT)); + + // auto, non-mutating + keys.clear(); values.clear(); + for (auto [key, value] : c.asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 1); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 1)); + + // auto &&, mutating + keys.clear(); values.clear(); + for (auto &&[key, value] : c.asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 1); + QCOMPARE(c.value(key), value); + ++value; + QCOMPARE(key, value - 2); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 1)); + + // auto, non-mutating (const map) + keys.clear(); values.clear(); + for (auto [key, value] : std::as_const(c).asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 2); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 2)); + + // auto &&, non-mutating (const map) + keys.clear(); values.clear(); + for (auto &&[key, value] : std::as_const(c).asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 2); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 2)); + + // auto, non-mutating (rvalue map) + keys.clear(); values.clear(); + for (auto [key, value] : returnC().asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 2); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 2)); + + // auto &&, non-mutating (rvalue map) + keys.clear(); values.clear(); + for (auto &&[key, value] : returnC().asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 2); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 2)); } QTEST_APPLESS_MAIN(tst_ContainerApiSymmetry) diff --git a/tests/auto/corelib/tools/qalgorithms/CMakeLists.txt b/tests/auto/corelib/tools/qalgorithms/CMakeLists.txt new file mode 100644 index 0000000000..9e87144a4c --- /dev/null +++ b/tests/auto/corelib/tools/qalgorithms/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qalgorithms Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qalgorithms LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qalgorithms + SOURCES + tst_qalgorithms.cpp +) diff --git a/tests/auto/corelib/tools/qalgorithms/qalgorithms.pro b/tests/auto/corelib/tools/qalgorithms/qalgorithms.pro deleted file mode 100644 index 0e6e830185..0000000000 --- a/tests/auto/corelib/tools/qalgorithms/qalgorithms.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qalgorithms -QT = core testlib -SOURCES = tst_qalgorithms.cpp diff --git a/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp b/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp index 18432e51a6..8d68a7a270 100644 --- a/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp +++ b/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp @@ -1,33 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../../../../../src/corelib/tools/qalgorithms.h" -#include <QtTest/QtTest> +#include <QTest> + +QT_WARNING_DISABLE_DEPRECATED #include <iostream> #include <iomanip> @@ -35,10 +12,10 @@ #include <iterator> #include <algorithm> #include <qalgorithms.h> -#include <QStringList> -#include <QString> +#include <QList> #include <QRandomGenerator> -#include <QVector> +#include <QString> +#include <QStringList> #define Q_TEST_PERFORMANCE 0 @@ -52,31 +29,6 @@ private slots: void swap2(); void convenienceAPI(); -#if QT_DEPRECATED_SINCE(5, 2) - void test_qLowerBound_data(); - void test_qLowerBound(); - void test_qUpperBound_data(); - void test_qUpperBound(); - void test_qBinaryFind_data(); - void test_qBinaryFind(); - void qBinaryFindOneEntry(); - void sortEmptyList(); - void sortedList(); - void sortAPItest(); - void stableSortTest(); - void stableSortCorrectnessTest_data(); - void stableSortCorrectnessTest(); - void convenienceAPI_deprecated(); - void qCountIterators() const; - void qCountContainer() const; - void binaryFindOnLargeContainer() const; - -#if Q_TEST_PERFORMANCE - void performance(); -#endif - -#endif // QT_DEPRECATED_SINCE(5, 2) - void popCount08_data() { popCount_data_impl(sizeof(quint8 )); } void popCount16_data() { popCount_data_impl(sizeof(quint16)); } void popCount32_data() { popCount_data_impl(sizeof(quint32)); } @@ -118,155 +70,18 @@ private: void countLeading_impl(); }; -#if QT_DEPRECATED_SINCE(5, 2) - -class TestInt +template <typename T> struct PrintIfFailed { -public: - TestInt(int number) :m_number(number) {} ; - TestInt() : m_number(0) {}; - bool operator<(const TestInt &other) const { ++TestInt::lessThanRefCount; return (m_number < other.m_number); } - int m_number; -static long int lessThanRefCount; -}; - -long int TestInt::lessThanRefCount; - - -QStringList dataSetTypes = QStringList() << "Random" << "Ascending" - << "Descending" << "Equal" << "Duplicates" << "Almost Sorted" ; - -template <typename DataType> -QVector<DataType> generateData(QString dataSetType, const int length) -{ - QVector<DataType> container; - if (dataSetType == "Random") { - for (int i = 0; i < length; ++i) - container.append(QRandomGenerator::global()->generate()); - } else if (dataSetType == "Ascending") { - for (int i = 0; i < length; ++i) - container.append(i); - } else if (dataSetType == "Descending") { - for (int i = 0; i < length; ++i) - container.append(length - i); - } else if (dataSetType == "Equal") { - for (int i = 0; i < length; ++i) - container.append(43); - } else if (dataSetType == "Duplicates") { - for (int i = 0; i < length; ++i) - container.append(i % 10); - } else if (dataSetType == "Almost Sorted") { - for (int i = 0; i < length; ++i) - container.append(i); - for (int i = 0; i <= length / 10; ++i) { - const int iswap = i * 9; - DataType tmp = container.at(iswap); - container[iswap] = container.at(iswap + 1); - container[iswap + 1] = tmp; - } + T value; + PrintIfFailed(T v) : value(v) {} + ~PrintIfFailed() + { + if (!QTest::currentTestFailed()) + return; + qWarning() << "Original value was" << Qt::hex << Qt::showbase << T(value); } - return container; -} - -struct ResultSet -{ - int numSorts; - long int lessThanRefCount; }; - -template <typename ContainerType, typename Algorithm> -ResultSet testRun(ContainerType &container, Algorithm &algorithm, int millisecs) -{ - TestInt::lessThanRefCount = 0; - int count = 0; - QElapsedTimer t; - t.start(); - while(t.elapsed() < millisecs) { - ++count; - algorithm(container); - } - ResultSet result; - result.numSorts = count; - result.lessThanRefCount = TestInt::lessThanRefCount; - return result; -} - -template <typename ContainerType, typename LessThan> -bool isSorted(ContainerType &container, LessThan lessThan) -{ - for (int i=0; i < container.count() - 1; ++i) - if (lessThan(container.at(i+1), container.at(i))) { - return false; - } - return true; -} - -template <typename ContainerType> -bool isSorted(ContainerType &container) -{ - return isSorted(container, qLess<typename ContainerType::value_type>()); -} - - -#if Q_TEST_PERFORMANCE -void printHeader(QStringList &headers) -{ - cout << setw(10) << setiosflags(ios_base::left) << " "; - for (int h = 0; h < headers.count(); ++h) { - cout << setw(20) << setiosflags(ios_base::left) << headers.at(h).toLatin1().constData(); - } - cout << Qt::endl; -} - -template <typename ContainerType> -void print(ContainerType testContainer) -{ - typedef typename ContainerType::value_type T; - - foreach(T value, testContainer) { - cout << value << " "; - } - - cout << Qt::endl; -} - -template <typename Algorithm, typename DataType> -QList<ResultSet> testAlgorithm(Algorithm &algorithm, QStringList dataSetTypes, int size, int time) -{ - QList<ResultSet> results; - foreach(QString dataSetType, dataSetTypes) { - QVector<DataType> container = generateData<DataType>(dataSetType, size); - results.append(testRun(container, algorithm, time)); - if (!isSorted(container)) - qWarning("%s: container is not sorted after test", Q_FUNC_INFO); - } - return results; -} - -template <typename Algorithm, typename DataType> -void testAlgorithm(Algorithm algorithm, QStringList &dataSetTypes) -{ - QList<int> sizes = QList<int>() << 5 << 15 << 35 << 70 << 200 << 1000 << 10000; - printHeader(dataSetTypes); - for (int s = 0; s < sizes.count(); ++s){ - cout << setw(10) << setiosflags(ios_base::left)<< sizes.at(s); - QList<ResultSet> results = - testAlgorithm<Algorithm, DataType>(algorithm, dataSetTypes, sizes.at(s), 100); - foreach(ResultSet result, results) { - stringstream numSorts; - numSorts << setiosflags(ios_base::left) << setw(10) << result.numSorts; - stringstream lessThan; - lessThan << setiosflags(ios_base::left) << setw(10) << result.lessThanRefCount / result.numSorts; - cout << numSorts.str() << lessThan.str(); - } - cout << Qt::endl; - } -} -#endif - -#endif // QT_DEPRECATED_SINCE(5, 2) - void tst_QAlgorithms::swap() { { @@ -339,42 +154,42 @@ void tst_QAlgorithms::swap() } { - void *a = 0, *b = 0; + void *a = nullptr, *b = nullptr; qSwap(a, b); } { - const void *a = 0, *b = 0; + const void *a = nullptr, *b = nullptr; qSwap(a, b); } { - QString *a = 0, *b = 0; + QString *a = nullptr, *b = nullptr; qSwap(a, b); } { - const QString *a = 0, *b = 0; + const QString *a = nullptr, *b = nullptr; qSwap(a, b); } { - QString **a = 0, **b = 0; + QString **a = nullptr, **b = nullptr; qSwap(a, b); } { - const QString **a = 0, **b = 0; + const QString **a = nullptr, **b = nullptr; qSwap(a, b); } { - QString * const *a = 0, * const *b = 0; + QString * const *a = nullptr, * const *b = nullptr; qSwap(a, b); } { - const QString * const *a = 0, * const *b = 0; + const QString * const *a = nullptr, * const *b = nullptr; qSwap(a, b); } } @@ -410,676 +225,26 @@ void tst_QAlgorithms::convenienceAPI() qDeleteAll(pointerList.begin(), pointerList.end()); } -#if QT_DEPRECATED_SINCE(5, 2) - -void tst_QAlgorithms::sortEmptyList() -{ - // Only test if it crashes - QStringList stringList; - stringList.sort(); - QVERIFY(true); -} - -void tst_QAlgorithms::sortedList() -{ - QList<int> list; - list << 4 << 3 << 6; - - ::qSort(list.begin(), list.end()); - - QCOMPARE(list.count(), 3); - QCOMPARE(list.at(0), 3); - QCOMPARE(list.at(1), 4); - QCOMPARE(list.at(2), 6); - - list.insert(qUpperBound(list.begin(), list.end(), 5), 5); - list.insert(qUpperBound(list.begin(), list.end(), 1), 1); - list.insert(qUpperBound(list.begin(), list.end(), 8), 8); - - QCOMPARE(list.count(), 6); - QCOMPARE(list.at(0), 1); - QCOMPARE(list.at(1), 3); - QCOMPARE(list.at(2), 4); - QCOMPARE(list.at(3), 5); - QCOMPARE(list.at(4), 6); - QCOMPARE(list.at(5), 8); -} - - -void tst_QAlgorithms::test_qLowerBound_data() -{ - QTest::addColumn<QList<int> >("data"); - QTest::addColumn<int>("resultValue"); - QTest::addColumn<int>("resultIndex"); - - QTest::newRow("sorted-duplicate") << (QList<int>() << 1 << 2 << 2 << 3) << 2 << 1; -} - -void tst_QAlgorithms::test_qLowerBound() -{ - QFETCH(QList<int>, data); - QFETCH(int, resultValue); - QFETCH(int, resultIndex); - - - QCOMPARE(qLowerBound(data.constBegin(), data.constEnd(), resultValue), data.constBegin() + resultIndex); - QCOMPARE(qLowerBound(data.begin(), data.end(), resultValue), data.begin() + resultIndex); - QCOMPARE(qLowerBound(data, resultValue), data.constBegin() + resultIndex); - QCOMPARE(qLowerBound(data.constBegin(), data.constEnd(), resultValue, qLess<int>()), data.constBegin() + resultIndex); -} - -void tst_QAlgorithms::test_qUpperBound_data() -{ - QTest::addColumn<QList<int> >("data"); - QTest::addColumn<int>("resultValue"); - QTest::addColumn<int>("resultIndex"); - - QTest::newRow("sorted-duplicate") << (QList<int>() << 1 << 2 << 2 << 3) << 2 << 3; -} - -void tst_QAlgorithms::test_qUpperBound() -{ - QFETCH(QList<int>, data); - QFETCH(int, resultValue); - QFETCH(int, resultIndex); - - QCOMPARE(qUpperBound(data.constBegin(), data.constEnd(), resultValue), data.constBegin() + resultIndex); - QCOMPARE(qUpperBound(data.begin(), data.end(), resultValue), data.begin() + resultIndex); - QCOMPARE(qUpperBound(data, resultValue), data.constBegin() + resultIndex); - QCOMPARE(qUpperBound(data.constBegin(), data.constEnd(), resultValue, qLess<int>()), data.constBegin() + resultIndex); -} - -void tst_QAlgorithms::test_qBinaryFind_data() -{ - QTest::addColumn<QList<int> >("data"); - QTest::addColumn<int>("resultValue"); // -42 means not found - - QTest::newRow("sorted-duplicate") << (QList<int>() << 1 << 2 << 2 << 3) << 2; - QTest::newRow("sorted-end") << (QList<int>() << -5 << -2 << 0 << 8) << 8; - QTest::newRow("sorted-beginning") << (QList<int>() << -5 << -2 << 0 << 8) << -5; - QTest::newRow("sorted-duplicate-beginning") << (QList<int>() << -5 << -5 << -2 << 0 << 8) << -5; - QTest::newRow("empty") << (QList<int>()) << -42; - QTest::newRow("not found 1 ") << (QList<int>() << 1 << 5 << 8 << 65) << -42; - QTest::newRow("not found 2 ") << (QList<int>() << -456 << -5 << 8 << 65) << -42; -} - -void tst_QAlgorithms::test_qBinaryFind() -{ - QFETCH(QList<int>, data); - QFETCH(int, resultValue); - - //-42 means not found - if (resultValue == -42) { - QVERIFY(qBinaryFind(data.constBegin(), data.constEnd(), resultValue) == data.constEnd()); - QVERIFY(qBinaryFind(data, resultValue) == data.constEnd()); - QVERIFY(qBinaryFind(data.begin(), data.end(), resultValue) == data.end()); - QVERIFY(qBinaryFind(data.begin(), data.end(), resultValue, qLess<int>()) == data.end()); - return; - } - - QCOMPARE(*qBinaryFind(data.constBegin(), data.constEnd(), resultValue), resultValue); - QCOMPARE(*qBinaryFind(data.begin(), data.end(), resultValue), resultValue); - QCOMPARE(*qBinaryFind(data, resultValue), resultValue); - QCOMPARE(*qBinaryFind(data.constBegin(), data.constEnd(), resultValue, qLess<int>()), resultValue); -} - -void tst_QAlgorithms::qBinaryFindOneEntry() -{ - QList<int> list; - list << 2; - - QVERIFY(::qBinaryFind(list.constBegin(), list.constEnd(), 2) != list.constEnd()); -} - - -void tst_QAlgorithms::sortAPItest() -{ - QVector<int> testVector = generateData<int>("Random", 101); - qSort(testVector); - QVERIFY(isSorted(testVector)); - qSort(testVector.begin(), testVector.end()); - QVERIFY(isSorted(testVector)); - qSort(testVector.begin(), testVector.end(), qLess<int>()); - QVERIFY(isSorted(testVector)); - - testVector = generateData<int>("Random", 71); - qStableSort(testVector); - QVERIFY(isSorted(testVector)); - qStableSort(testVector.begin(), testVector.end()); - QVERIFY(isSorted(testVector)); - qStableSort(testVector.begin(), testVector.end(), qLess<int>()); - QVERIFY(isSorted(testVector)); - - QList<int> testList = generateData<int>("Random", 101).toList(); - qSort(testList); - QVERIFY(isSorted(testList)); - qSort(testList.begin(), testList.end()); - QVERIFY(isSorted(testList)); - qSort(testList.begin(), testList.end(), qLess<int>()); - QVERIFY(isSorted(testList)); - - testList = generateData<int>("Random", 71).toList(); - qStableSort(testList); - QVERIFY(isSorted(testList)); - qStableSort(testList.begin(), testList.end()); - QVERIFY(isSorted(testList)); - qStableSort(testList.begin(), testList.end(), qLess<int>()); - QVERIFY(isSorted(testList)); -} - - -class StableSortTest -{ -public: - StableSortTest(){}; - StableSortTest(int Major, int Minor) : Major(Major), Minor(Minor) {} - bool operator<(const StableSortTest &other) const {return (Major < other.Major); } - bool testMinor(const StableSortTest &other) const {return Minor < other.Minor; } - -int Major; -int Minor; -}; - -ostream &operator<<(ostream &out, const StableSortTest& obj) { out << obj.Major << "-" << obj.Minor; return out; } - -QVector<StableSortTest> createStableTestVector() -{ - QVector<StableSortTest> stableTestVector; - for (int i=500; i>=0; --i) { - for (int j=0; j<10; ++j) { - stableTestVector.append(StableSortTest(i, j)); - } - } - return stableTestVector; -} - -template <typename ContainerType, typename LessThan> -bool isStableSorted(ContainerType &container, LessThan lessThan) -{ - for (int i=0; i < container.count() - 1; ++i) { - //not sorted? - if (lessThan(container.at(i + 1), container.at(i))) - return false; - // equal? - if (lessThan(container.at(i), container.at(i + 1))) - continue; - // minor version? - if(container.at(i + 1).testMinor(container.at(i))) - return false; - } - return true; -} - -void tst_QAlgorithms::stableSortTest() -{ - // Selftests: - { - QVector<StableSortTest> stableTestVector = createStableTestVector(); - qSort(stableTestVector.begin(), stableTestVector.end(), qLess<StableSortTest>()); - QVERIFY(isSorted(stableTestVector, qLess<StableSortTest>())); - QVERIFY(!isStableSorted(stableTestVector, qLess<StableSortTest>())); - } - { - QVector<StableSortTest> stableTestVector = createStableTestVector(); - qSort(stableTestVector.begin(), stableTestVector.end(), qGreater<StableSortTest>()); - QVERIFY(isSorted(stableTestVector, qGreater<StableSortTest>())); - QVERIFY(!isStableSorted(stableTestVector, qGreater<StableSortTest>())); - } - { - QVector<StableSortTest> stableTestVector = createStableTestVector(); - qSort(stableTestVector.begin(), stableTestVector.end(), qGreater<StableSortTest>()); - QVERIFY(!isSorted(stableTestVector, qLess<StableSortTest>())); - QVERIFY(!isStableSorted(stableTestVector, qGreater<StableSortTest>())); - } - - - // Stable sort with qLess - { - QVector<StableSortTest> stableTestVector = createStableTestVector(); - std::stable_sort(stableTestVector.begin(), stableTestVector.end(), qLess<StableSortTest>()); - QVERIFY(isSorted(stableTestVector, qLess<StableSortTest>())); - QVERIFY(isStableSorted(stableTestVector, qLess<StableSortTest>())); - } - { - QVector<StableSortTest> stableTestVector = createStableTestVector(); - qStableSort(stableTestVector.begin(), stableTestVector.end(), qLess<StableSortTest>()); - QVERIFY(isSorted(stableTestVector, qLess<StableSortTest>())); - QVERIFY(isStableSorted(stableTestVector, qLess<StableSortTest>())); - } - - // Stable sort with qGreater - { - QVector<StableSortTest> stableTestVector = createStableTestVector(); - std::stable_sort(stableTestVector.begin(), stableTestVector.end(), qGreater<StableSortTest>()); - QVERIFY(isSorted(stableTestVector, qGreater<StableSortTest>())); - QVERIFY(isStableSorted(stableTestVector, qGreater<StableSortTest>())); - } - - { - QVector<StableSortTest> stableTestVector = createStableTestVector(); - qStableSort(stableTestVector.begin(), stableTestVector.end(), qGreater<StableSortTest>()); - QVERIFY(isSorted(stableTestVector, qGreater<StableSortTest>())); - QVERIFY(isStableSorted(stableTestVector, qGreater<StableSortTest>())); - } -} - - -void tst_QAlgorithms::stableSortCorrectnessTest_data() -{ - const int dataSize = 1000; - QTest::addColumn<QVector<int> >("unsorted"); - QTest::newRow("From documentation") << (QVector<int>() << 33 << 12 << 68 << 6 << 12); - QTest::newRow("Equal") << (generateData<int>("Equal", dataSize)); - QTest::newRow("Ascending") << (generateData<int>("Ascending", dataSize)); - QTest::newRow("Descending") << (generateData<int>("Descending", dataSize)); - QTest::newRow("Duplicates") << (generateData<int>("Duplicates", dataSize)); - QTest::newRow("Almost Sorted") << (generateData<int>("Almost Sorted", dataSize)); - QTest::newRow("Random") << (generateData<int>("Random", dataSize)); -} - -void tst_QAlgorithms::stableSortCorrectnessTest() -{ - QFETCH(QVector<int>, unsorted); - - QVector<int> sorted = unsorted; - qStableSort(sorted.begin(), sorted.end()); - - // Verify that sorted contains the same numbers as unsorted. - foreach(int value, unsorted) { - QVERIFY(sorted.contains(value)); - int unsortedCount = 0; - qCount(unsorted.begin(), unsorted.end(), value, unsortedCount); - int sortedCount = 0; - qCount(sorted.begin(), sorted.end(), value, sortedCount); - QCOMPARE(sortedCount, unsortedCount); - } - - QVERIFY(isSorted(sorted)); -} - -void tst_QAlgorithms::convenienceAPI_deprecated() -{ - // Compile-test for QAlgorithm convenience functions. - QList<int> list, list2; - - qCopy(list.begin(), list.end(), list2.begin()); - qCopyBackward(list.begin(), list.end(), list2.begin()); - qEqual(list.begin(), list.end(), list2.begin()); - - qFill(list, 1); - qFill(list.begin(), list.end(), 1); - - qFind(list, 1); - qFind(list.begin(), list.end(), 1); - - int count1 = 0 , count2 = 0, count3 = 0; - qCount(list, 1, count1); - qCount(list.begin(), list.end(), 1, count2); - QCOMPARE(count1, count2); - QCOMPARE(count2, count3); - - qSort(list); - qSort(list.begin(), list.end()); - qSort(list.begin(), list.end(), qLess<int>()); - - qStableSort(list); - qStableSort(list.begin(), list.end()); - qStableSort(list.begin(), list.end(), qLess<int>()); - - qLowerBound(list, 1);; - qLowerBound(list.begin(), list.end(), 1); - qLowerBound(list.begin(), list.end(), 1, qLess<int>()); - - qUpperBound(list, 1); - qUpperBound(list.begin(), list.end(), 1); - qUpperBound(list.begin(), list.end(), 1, qLess<int>()); - - qBinaryFind(list, 1); - qBinaryFind(list.begin(), list.end(), 1); - qBinaryFind(list.begin(), list.end(), 1, qLess<int>()); -} - -template <typename DataType> -class QuickSortHelper -{ -public: - void operator()(QVector<DataType> list) - { - ::qSort(list); - } -}; - -template <typename DataType> -class StableSortHelper -{ -public: - void operator()(QVector<DataType> list) - { - ::qStableSort(list); - } -}; - -template <typename DataType> -class StlSortHelper -{ -public: - void operator()(QVector<DataType> list) - { - std::sort(list.begin(), list.end()); - } -}; - -template <typename DataType> -class StlStableSortHelper -{ -public: - void operator()(QVector<DataType> list) - { - std::stable_sort(list.begin(), list.end()); - } -}; - -#if Q_TEST_PERFORMANCE -void tst_QAlgorithms::performance() -{ - cout << Qt::endl << "Quick sort" << Qt::endl; - testAlgorithm<QuickSortHelper<TestInt>, TestInt>(QuickSortHelper<TestInt>(), dataSetTypes); - cout << Qt::endl << "stable sort" << Qt::endl; - testAlgorithm<StableSortHelper<TestInt>, TestInt>(StableSortHelper<TestInt>(), dataSetTypes); - cout << Qt::endl << "std::sort" << Qt::endl; - testAlgorithm<StlSortHelper<TestInt>, TestInt>(StlSortHelper<TestInt>(), dataSetTypes); - cout << Qt::endl << "std::stable_sort" << Qt::endl; - testAlgorithm<StlStableSortHelper<TestInt>, TestInt>(StlStableSortHelper<TestInt>(), dataSetTypes); -/* - cout << Qt::endl << "Sorting lists of ints" << Qt::endl; - cout << Qt::endl << "Quick sort" << Qt::endl; - testAlgorithm<QuickSortHelper<int>, int>(QuickSortHelper<int>(), dataSetTypes); - cout << Qt::endl << "std::sort" << Qt::endl; - testAlgorithm<StlSortHelper<int>, int>(StlSortHelper<int>(), dataSetTypes); - cout << Qt::endl << "std::stable_sort" << Qt::endl; - testAlgorithm<StlStableSortHelper<int>, int>(StlStableSortHelper<int>(), dataSetTypes); -*/ -} -#endif - -void tst_QAlgorithms::qCountIterators() const -{ - QList<int> list; - list << 3 << 3 << 6 << 6 << 6 << 8; - - { - int countOf7 = 0; - ::qCount(list.begin(), list.end(), 7, countOf7); - QCOMPARE(countOf7, 0); - } - - { - int countOf3 = 0; - ::qCount(list.begin(), list.end(), 3, countOf3); - QCOMPARE(countOf3, 2); - } - - { - int countOf6 = 0; - ::qCount(list.begin(), list.end(), 6, countOf6); - QCOMPARE(countOf6, 3); - } - - { - int countOf8 = 0; - ::qCount(list.begin(), list.end(), 8, countOf8); - QCOMPARE(countOf8, 1); - } - - /* Check that we add to the count, not set it. */ - { - int countOf8 = 5; - ::qCount(list.begin(), list.end(), 8, countOf8); - QCOMPARE(countOf8, 6); - } -} - -void tst_QAlgorithms::qCountContainer() const -{ - QList<int> list; - list << 3 << 3 << 6 << 6 << 6 << 8; - - { - int countOf7 = 0; - ::qCount(list, 7, countOf7); - QCOMPARE(countOf7, 0); - } - - { - int countOf3 = 0; - ::qCount(list, 3, countOf3); - QCOMPARE(countOf3, 2); - } - - { - int countOf6 = 0; - ::qCount(list, 6, countOf6); - QCOMPARE(countOf6, 3); - } - - { - int countOf8 = 0; - ::qCount(list, 8, countOf8); - QCOMPARE(countOf8, 1); - } - - /* Check that we add to the count, not set it. */ - { - int countOf8 = 5; - ::qCount(list, 8, countOf8); - QCOMPARE(countOf8, 6); - } -} - -class RAI -{ - public: - typedef int difference_type; - typedef int value_type; - typedef std::random_access_iterator_tag iterator_category; - typedef int *pointer; - typedef int &reference; - - RAI(int searched = 5, int hidePos = 4, int len = 10) - : curPos_(0) - , length_(len) - , searchedVal_(searched) - , searchedValPos_(hidePos) - { - } - - int at(int pos) const - { - if (pos == searchedValPos_) { - return searchedVal_; - } - else if (pos < searchedValPos_) { - return searchedVal_ - 1; - } - - return searchedVal_ + 1; - } - - RAI begin() const - { - RAI rai = *this; - rai.setCurPos(0); - return rai; - } - - RAI end() const - { - RAI rai = *this; - rai.setCurPos(length_); - return rai; - } - - int pos() const - { - return curPos(); - } - - int size() const - { - return length_; - } - - RAI operator+(int i) const - { - RAI rai = *this; - rai.setCurPos( rai.curPos() + i ); - if (rai.curPos() > length_) { - rai.setCurPos(length_); - } - return rai; - } - - RAI operator-(int i) const - { - RAI rai = *this; - rai.setCurPos( rai.curPos() - i ); - if (rai.curPos() < 0) { - rai.setCurPos(0); - } - return rai; - } - - int operator-(const RAI& it) const - { - return curPos() - it.curPos(); - } - - RAI& operator+=(int i) - { - setCurPos( curPos() + i ); - if (curPos() > length_) { - setCurPos(length_); - } - return *this; - } - - RAI& operator-=(int i) - { - setCurPos( curPos() - i); - if (curPos() < 0) { - setCurPos(0); - } - return *this; - } - - RAI& operator++() - { - if (curPos() < length_) { - setCurPos( curPos() + 1 ); - } - return *this; - } - - RAI operator++(int) - { - RAI rai = *this; - - if (curPos() < length_) { - setCurPos( curPos() + 1 ); - } - - return rai; - } - - RAI& operator--() - { - if (curPos() > 0) { - setCurPos( curPos() - 1 ); - } - return *this; - } - - RAI operator--(int) - { - RAI rai = *this; - - if (curPos() > 0) { - setCurPos( curPos() - 1 ); - } - - return rai; - } - - bool operator==(const RAI& rai) const - { - return rai.curPos() == curPos(); - } - - bool operator!=(const RAI& rai) const - { - return !operator==(rai); - } - - int operator*() const - { - return at(curPos()); - } - - int operator[](int i) const - { - return at(i); - } - - private: - - int curPos() const - { - return curPos_; - } - - void setCurPos(int pos) - { - curPos_ = pos; - } - - int curPos_; - int length_; - int searchedVal_; - int searchedValPos_; -}; - -void tst_QAlgorithms::binaryFindOnLargeContainer() const -{ - const int len = 2 * 1000 * 1000 * 537; - const int pos = len - 12345; - RAI rai(5, pos, len); - - RAI foundIt = qBinaryFind(rai.begin(), rai.end(), 5); - QCOMPARE(foundIt.pos(), 1073987655); -} - -#endif // QT_DEPRECATED_SINCE(5, 2) - // alternative implementation of qPopulationCount for comparison: -static Q_DECL_CONSTEXPR const uint bitsSetInNibble[] = { +static constexpr const uint bitsSetInNibble[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, }; -Q_STATIC_ASSERT(sizeof bitsSetInNibble / sizeof *bitsSetInNibble == 16); +static_assert(sizeof bitsSetInNibble / sizeof *bitsSetInNibble == 16); -static Q_DECL_CONSTEXPR uint bitsSetInByte(quint8 byte) +static constexpr uint bitsSetInByte(quint8 byte) { return bitsSetInNibble[byte & 0xF] + bitsSetInNibble[byte >> 4]; } -static Q_DECL_CONSTEXPR uint bitsSetInShort(quint16 word) +static constexpr uint bitsSetInShort(quint16 word) { return bitsSetInByte(word & 0xFF) + bitsSetInByte(word >> 8); } -static Q_DECL_CONSTEXPR uint bitsSetInInt(quint32 word) +static constexpr uint bitsSetInInt(quint32 word) { return bitsSetInShort(word & 0xFFFF) + bitsSetInShort(word >> 16); } -static Q_DECL_CONSTEXPR uint bitsSetInInt64(quint64 word) +static constexpr uint bitsSetInInt64(quint64 word) { return bitsSetInInt(word & 0xFFFFFFFF) + bitsSetInInt(word >> 32); } @@ -1096,23 +261,26 @@ void tst_QAlgorithms::popCount_data_impl(size_t sizeof_T_Int) const uint bits = bitsSetInByte(byte); const quint64 value = static_cast<quint64>(byte); const quint64 input = value << ((i % sizeof_T_Int) * 8U); - QTest::addRow("0x%016llx", input) << input << bits; + QTest::addRow("%u-bits", i) << input << bits; } // and some random ones: - if (sizeof_T_Int >= 8) + if (sizeof_T_Int >= 8) { for (size_t i = 0; i < 1000; ++i) { const quint64 input = QRandomGenerator::global()->generate64(); - QTest::addRow("0x%016llx", input) << input << bitsSetInInt64(input); + QTest::addRow("random-%zu", i) << input << bitsSetInInt64(input); } - else if (sizeof_T_Int >= 2) - for (size_t i = 0; i < 1000 ; ++i) { - const quint32 input = QRandomGenerator::global()->generate(); - if (sizeof_T_Int >= 4) - QTest::addRow("0x%08x", input) << quint64(input) << bitsSetInInt(input); - else - QTest::addRow("0x%04x", quint16(input & 0xFFFF)) << quint64(input & 0xFFFF) << bitsSetInShort(input & 0xFFFF); + } else if (sizeof_T_Int >= 2) { + for (size_t i = 0; i < 1000 ; ++i) { + const quint32 input = QRandomGenerator::global()->generate(); + if (sizeof_T_Int >= 4) { + QTest::addRow("random-%zu", i) << quint64(input) << bitsSetInInt(input); + } else { + QTest::addRow("random-%zu", i) + << quint64(input & 0xFFFF) << bitsSetInShort(input & 0xFFFF); } + } + } } template <typename T_Int> @@ -1122,22 +290,23 @@ void tst_QAlgorithms::popCount_impl() QFETCH(uint, expected); const T_Int value = static_cast<T_Int>(input); - + PrintIfFailed pf(value); QCOMPARE(qPopulationCount(value), expected); } +// Number of test-cases per offset into each size (arbitrary): +static constexpr int casesPerOffset = 3; + void tst_QAlgorithms::countTrailing_data_impl(size_t sizeof_T_Int) { using namespace QTest; addColumn<quint64>("input"); addColumn<uint>("expected"); - int nibs = sizeof_T_Int*2; - - newRow(("0x"+QByteArray::number(0,16).rightJustified(nibs,'0')).constData()) << Q_UINT64_C(0) << uint(sizeof_T_Int*8); + addRow("0") << Q_UINT64_C(0) << uint(sizeof_T_Int*8); for (uint i = 0; i < sizeof_T_Int*8; ++i) { const quint64 input = Q_UINT64_C(1) << i; - newRow(("0x"+QByteArray::number(input,16).rightJustified(nibs,'0')).constData()) << input << i; + addRow("bit-%u", i) << input << i; } quint64 type_mask; @@ -1148,12 +317,12 @@ void tst_QAlgorithms::countTrailing_data_impl(size_t sizeof_T_Int) // and some random ones: for (uint i = 0; i < sizeof_T_Int*8; ++i) { - for (uint j = 0; j < sizeof_T_Int*3; ++j) { // 3 is arbitrary + const quint64 b = Q_UINT64_C(1) << i; + const quint64 mask = ((~(b - 1)) ^ b) & type_mask; + for (uint j = 0; j < sizeof_T_Int * casesPerOffset; ++j) { const quint64 r = QRandomGenerator::global()->generate64(); - const quint64 b = Q_UINT64_C(1) << i; - const quint64 mask = ((~(b-1)) ^ b) & type_mask; const quint64 input = (r&mask) | b; - newRow(("0x"+QByteArray::number(input,16).rightJustified(nibs,'0')).constData()) << input << i; + addRow("%u-bits-random-%u", i, j) << input << i; } } } @@ -1165,7 +334,7 @@ void tst_QAlgorithms::countTrailing_impl() QFETCH(uint, expected); const T_Int value = static_cast<T_Int>(input); - + PrintIfFailed pf(value); QCOMPARE(qCountTrailingZeroBits(value), expected); } @@ -1175,22 +344,20 @@ void tst_QAlgorithms::countLeading_data_impl(size_t sizeof_T_Int) addColumn<quint64>("input"); addColumn<uint>("expected"); - int nibs = sizeof_T_Int*2; - - newRow(("0x"+QByteArray::number(0,16).rightJustified(nibs,'0')).constData()) << Q_UINT64_C(0) << uint(sizeof_T_Int*8); + addRow("0") << Q_UINT64_C(0) << uint(sizeof_T_Int*8); for (uint i = 0; i < sizeof_T_Int*8; ++i) { const quint64 input = Q_UINT64_C(1) << i; - newRow(("0x"+QByteArray::number(input,16).rightJustified(nibs,'0')).constData()) << input << uint(sizeof_T_Int*8-i-1); + addRow("bit-%u", i) << input << uint(sizeof_T_Int*8-i-1); } // and some random ones: for (uint i = 0; i < sizeof_T_Int*8; ++i) { - for (uint j = 0; j < sizeof_T_Int*3; ++j) { // 3 is arbitrary + const quint64 b = Q_UINT64_C(1) << i; + const quint64 mask = b - 1; + for (uint j = 0; j < sizeof_T_Int * casesPerOffset; ++j) { const quint64 r = QRandomGenerator::global()->generate64(); - const quint64 b = Q_UINT64_C(1) << i; - const quint64 mask = b-1; const quint64 input = (r&mask) | b; - newRow(("0x"+QByteArray::number(input,16).rightJustified(nibs,'0')).constData()) << input << uint(sizeof_T_Int*8-i-1); + addRow("%u-bits-random-%u", i, j) << input << uint(sizeof_T_Int*8-i-1); } } } @@ -1202,7 +369,7 @@ void tst_QAlgorithms::countLeading_impl() QFETCH(uint, expected); const T_Int value = static_cast<T_Int>(input); - + PrintIfFailed pf(value); QCOMPARE(qCountLeadingZeroBits(value), expected); } diff --git a/tests/auto/corelib/tools/qarraydata/CMakeLists.txt b/tests/auto/corelib/tools/qarraydata/CMakeLists.txt new file mode 100644 index 0000000000..1d84630de2 --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qarraydata Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qarraydata LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qarraydata + EXCEPTIONS + SOURCES + simplevector.h + tst_qarraydata.cpp +) diff --git a/tests/auto/corelib/tools/qarraydata/qarraydata.pro b/tests/auto/corelib/tools/qarraydata/qarraydata.pro deleted file mode 100644 index ee3faa9ad7..0000000000 --- a/tests/auto/corelib/tools/qarraydata/qarraydata.pro +++ /dev/null @@ -1,5 +0,0 @@ -TARGET = tst_qarraydata -SOURCES += $$PWD/tst_qarraydata.cpp -HEADERS += $$PWD/simplevector.h -QT = core testlib -CONFIG += testcase diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 94cee5d887..b92cd4a887 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef QARRAY_TEST_SIMPLE_VECTOR_H @@ -32,6 +7,7 @@ #include <QtCore/qarraydata.h> #include <QtCore/qarraydatapointer.h> +#include <QtCore/qvarlengtharray.h> #include <algorithm> @@ -40,46 +16,42 @@ struct SimpleVector { private: typedef QTypedArrayData<T> Data; + typedef QArrayDataPointer<T> DataPointer; public: typedef T value_type; - typedef typename Data::iterator iterator; - typedef typename Data::const_iterator const_iterator; + typedef T *iterator; + typedef const T *const_iterator; SimpleVector() { } - explicit SimpleVector(size_t n) - : d(Data::allocate(n)) + explicit SimpleVector(size_t n, bool capacityReserved = false) + : d(n) { if (n) d->appendInitialize(n); + if (capacityReserved) + d.setFlag(QArrayData::CapacityReserved); } - SimpleVector(size_t n, const T &t) - : d(Data::allocate(n)) + SimpleVector(size_t n, const T &t, bool capacityReserved = false) + : d(n) { if (n) d->copyAppend(n, t); + if (capacityReserved) + d.setFlag(QArrayData::CapacityReserved); } - SimpleVector(const T *begin, const T *end) - : d(Data::allocate(end - begin)) + SimpleVector(const T *begin, const T *end, bool capacityReserved = false) + : d(end - begin) { if (end - begin) d->copyAppend(begin, end); - } - - SimpleVector(QArrayDataPointerRef<T> ptr) - : d(ptr) - { - } - - template <size_t N> - explicit SimpleVector(QStaticArrayData<T, N> &ptr) - : d(static_cast<Data *>(&ptr.header), ptr.data, N) - { + if (capacityReserved) + d.setFlag(QArrayData::CapacityReserved); } SimpleVector(Data *header, T *data, size_t len = 0) @@ -87,8 +59,8 @@ public: { } - explicit SimpleVector(QPair<Data*, T*> ptr, size_t len = 0) - : d(ptr, len) + SimpleVector(const QArrayDataPointer<T> &other) + : d(other) { } @@ -96,7 +68,7 @@ public: bool isNull() const { return d.isNull(); } bool isEmpty() const { return this->empty(); } - bool isStatic() const { return d->isStatic(); } + bool isStatic() const { return !d.isMutable(); } bool isShared() const { return d->isShared(); } bool isSharedWith(const SimpleVector &other) const { return d == other.d; } @@ -153,15 +125,16 @@ public: if (d->flags() & Data::CapacityReserved) return; if (!d->isShared()) { - d->flags() |= Data::CapacityReserved; + d.setFlag(Data::CapacityReserved); return; } } - SimpleVector detached(Data::allocate(qMax(n, size()), - d->detachFlags() | Data::CapacityReserved)); - if (size()) + SimpleVector detached(DataPointer(qMax(n, size()))); + if (size()) { detached.d->copyAppend(constBegin(), constEnd()); + detached.d->setFlag(QArrayData::CapacityReserved); + } detached.swap(*this); } @@ -171,8 +144,7 @@ public: return; if (d->needsDetach() || newSize > capacity()) { - SimpleVector detached(Data::allocate( - d->detachCapacity(newSize), d->detachFlags())); + SimpleVector detached(DataPointer(d->detachCapacity(newSize))); if (newSize) { if (newSize < size()) { const T *const begin = constBegin(); @@ -205,46 +177,10 @@ public: if (first == last) return; - T *const begin = d->begin(); - if (d->needsDetach() - || capacity() - size() < size_t(last - first)) { - SimpleVector detached(Data::allocate( - d->detachCapacity(size() + (last - first)), - d->detachFlags() | Data::GrowsForward)); - - detached.d->copyAppend(first, last); - detached.d->copyAppend(begin, begin + d->size); - detached.swap(*this); - - return; - } - - d->insert(begin, first, last); + d->insert(0, first, last - first); } - void append(const_iterator first, const_iterator last) - { - if (first == last) - return; - - if (d->needsDetach() - || capacity() - size() < size_t(last - first)) { - SimpleVector detached(Data::allocate( - d->detachCapacity(size() + (last - first)), - d->detachFlags() | Data::GrowsForward)); - - if (d->size) { - const T *const begin = constBegin(); - detached.d->copyAppend(begin, begin + d->size); - } - detached.d->copyAppend(first, last); - detached.swap(*this); - - return; - } - - d->copyAppend(first, last); - } + void append(const_iterator first, const_iterator last) { d->growAppend(first, last); } void insert(int position, const_iterator first, const_iterator last) { @@ -264,37 +200,13 @@ public: if (first == last) return; - const iterator begin = d->begin(); - const iterator where = begin + position; - const iterator end = begin + d->size; - if (d->needsDetach() - || capacity() - size() < size_t(last - first)) { - SimpleVector detached(Data::allocate( - d->detachCapacity(size() + (last - first)), - d->detachFlags() | Data::GrowsForward)); - - if (position) - detached.d->copyAppend(begin, where); - detached.d->copyAppend(first, last); - detached.d->copyAppend(where, end); - detached.swap(*this); - - return; - } - - if ((first >= where && first < end) - || (last > where && last <= end)) { - // Copy overlapping data first and only then shuffle it into place - iterator start = d->begin() + position; - iterator middle = d->end(); - - d->copyAppend(first, last); - std::rotate(start, middle, d->end()); - + if (first >= d.begin() && first <= d.end()) { + QVarLengthArray<T> copy(first, last); + insert(position, copy.begin(), copy.end()); return; } - d->insert(where, first, last); + d->insert(position, first, last - first); } void erase(iterator first, iterator last) @@ -306,9 +218,7 @@ public: const T *const end = begin + d->size; if (d->needsDetach()) { - SimpleVector detached(Data::allocate( - d->detachCapacity(size() - (last - first)), - d->detachFlags())); + SimpleVector detached(DataPointer(d->detachCapacity(size() - (last - first)))); if (first != begin) detached.d->copyAppend(begin, first); detached.d->copyAppend(last, end); @@ -320,7 +230,7 @@ public: if (last == end) d->truncate(end - first); else - d->erase(first, last); + d->erase(first, last - first); } void swap(SimpleVector &other) @@ -338,10 +248,9 @@ public: d.detach(); } - static SimpleVector fromRawData(const T *data, size_t size, - QArrayData::ArrayOptions options = Data::DefaultRawFlags) + static SimpleVector fromRawData(const T *data, size_t size) { - return SimpleVector(Data::fromRawData(data, size, options)); + return SimpleVector(QArrayDataPointer<T>::fromRawData(data, size)); } private: diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 1366eebf97..e7a84d57ee 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -1,53 +1,30 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses + +#include <QTest> #include <QtCore/QString> #include <QtCore/qarraydata.h> #include "simplevector.h" -struct SharedNullVerifier -{ - SharedNullVerifier() - { - Q_ASSERT(QArrayData::shared_null[0].isStatic()); - Q_ASSERT(QArrayData::shared_null[0].isShared()); - } -}; - -// This is meant to verify/ensure that shared_null is not being dynamically -// initialized and stays away from the order-of-static-initialization fiasco. -// -// Of course, if this was to fail, qmake and the build should have crashed and -// burned before we ever got to this point :-) -SharedNullVerifier globalInit; +#include <array> +#include <tuple> +#include <algorithm> +#include <vector> +#include <set> +#include <stdexcept> +#include <functional> +#include <memory> + +// A wrapper for a test function. Calls a function, if it fails, reports failure +#define RUN_TEST_FUNC(test, ...) \ +do { \ + test(__VA_ARGS__); \ + if (QTest::currentTestFailed()) \ + QFAIL("Test case " #test "(" #__VA_ARGS__ ") failed"); \ +} while (false) class tst_QArrayData : public QObject { @@ -55,7 +32,6 @@ class tst_QArrayData : public QObject private slots: void referenceCounting(); - void sharedNullEmpty(); void simpleVector(); void simpleVectorReserve_data(); void simpleVectorReserve(); @@ -67,14 +43,28 @@ private slots: void alignment(); void typedData(); void gccBug43247(); + void arrayOps_data(); void arrayOps(); + void arrayOps2_data(); void arrayOps2(); + void arrayOpsExtra_data(); + void arrayOpsExtra(); void fromRawData_data(); void fromRawData(); void literals(); void variadicLiterals(); void rValueReferences(); void grow(); + void freeSpace_data(); + void freeSpace(); + void dataPointerAllocate_data(); + void dataPointerAllocate(); + void selfEmplaceBackwards(); + void selfEmplaceForward(); +#ifndef QT_NO_EXCEPTIONS + void relocateWithExceptions_data(); + void relocateWithExceptions(); +#endif // QT_NO_EXCEPTIONS }; template <class T> const T &const_(const T &t) { return t; } @@ -83,12 +73,10 @@ void tst_QArrayData::referenceCounting() { { // Reference counting initialized to 1 (owned) - QArrayData array = { Q_BASIC_ATOMIC_INITIALIZER(1), QArrayData::DefaultRawFlags, 0 }; + QArrayData array = { Q_BASIC_ATOMIC_INITIALIZER(1), {}, 0 }; QCOMPARE(array.ref_.loadRelaxed(), 1); - QVERIFY(!array.isStatic()); - QVERIFY(array.ref()); QCOMPARE(array.ref_.loadRelaxed(), 2); @@ -106,72 +94,21 @@ void tst_QArrayData::referenceCounting() // Now would be a good time to free/release allocated data } - { - // Reference counting initialized to -1 (static read-only data) - QArrayData array = { Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 }; - - QCOMPARE(array.ref_.loadRelaxed(), -1); - - QVERIFY(array.isStatic()); - - QVERIFY(array.ref()); - QCOMPARE(array.ref_.loadRelaxed(), -1); - - QVERIFY(array.deref()); - QCOMPARE(array.ref_.loadRelaxed(), -1); - - } -} - -void tst_QArrayData::sharedNullEmpty() -{ - QArrayData *null = const_cast<QArrayData *>(QArrayData::shared_null); - QArrayData *empty; - QArrayData::allocate(&empty, 1, alignof(QArrayData), 0); - - QVERIFY(null->isStatic()); - QVERIFY(null->isShared()); - - QVERIFY(empty->isStatic()); - QVERIFY(empty->isShared()); - - QCOMPARE(null->ref_.loadRelaxed(), -1); - QCOMPARE(empty->ref_.loadRelaxed(), -1); - - QCOMPARE(null->ref_.loadRelaxed(), -1); - QCOMPARE(empty->ref_.loadRelaxed(), -1); - - QVERIFY(null->deref()); - QVERIFY(empty->deref()); - - QCOMPARE(null->ref_.loadRelaxed(), -1); - QCOMPARE(empty->ref_.loadRelaxed(), -1); - - QVERIFY(null != empty); - - QCOMPARE(null->allocatedCapacity(), size_t(0)); - - QCOMPARE(empty->allocatedCapacity(), size_t(0)); } void tst_QArrayData::simpleVector() { - QArrayData data0 = { Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 }; - QStaticArrayData<int, 7> data1 = { - { Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 }, - { 0, 1, 2, 3, 4, 5, 6 } - }; - + int data[] = { 0, 1, 2, 3, 4, 5, 6 }; int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; SimpleVector<int> v1; SimpleVector<int> v2(v1); - SimpleVector<int> v3(static_cast<QTypedArrayData<int> *>(&data0), 0, 0); - SimpleVector<int> v4(data1); - SimpleVector<int> v5(static_cast<QTypedArrayData<int> *>(&data0), 0, 0); - SimpleVector<int> v6(data1); - SimpleVector<int> v7(10, 5); - SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array)); + SimpleVector<int> v3(nullptr, (int *)nullptr, 0); + SimpleVector<int> v4(nullptr, data, 0); + SimpleVector<int> v5(nullptr, data, 1); + SimpleVector<int> v6(nullptr, data, 7); + const SimpleVector<int> v7(10, 5); + const SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array)); v3 = v1; v1.swap(v3); @@ -190,7 +127,7 @@ void tst_QArrayData::simpleVector() QVERIFY(v2.isEmpty()); QVERIFY(v3.isEmpty()); QVERIFY(v4.isEmpty()); - QVERIFY(v5.isEmpty()); + QVERIFY(!v5.isEmpty()); QVERIFY(!v6.isEmpty()); QVERIFY(!v7.isEmpty()); QVERIFY(!v8.isEmpty()); @@ -199,7 +136,7 @@ void tst_QArrayData::simpleVector() QCOMPARE(v2.size(), size_t(0)); QCOMPARE(v3.size(), size_t(0)); QCOMPARE(v4.size(), size_t(0)); - QCOMPARE(v5.size(), size_t(0)); + QCOMPARE(v5.size(), size_t(1)); QCOMPARE(v6.size(), size_t(7)); QCOMPARE(v7.size(), size_t(10)); QCOMPARE(v8.size(), size_t(10)); @@ -248,13 +185,13 @@ void tst_QArrayData::simpleVector() QVERIFY(v1 == v2); QVERIFY(v1 == v3); QVERIFY(v1 == v4); - QVERIFY(v1 == v5); + QVERIFY(v1 != v5); QVERIFY(!(v1 == v6)); QVERIFY(v1 != v6); QVERIFY(v4 != v6); QVERIFY(v5 != v6); - QVERIFY(!(v1 != v5)); + QVERIFY(!(v1 == v5)); QVERIFY(v1 < v6); QVERIFY(!(v6 < v1)); @@ -299,7 +236,7 @@ void tst_QArrayData::simpleVector() { int count = 0; - Q_FOREACH (int value, v7) { + for (int value : v7) { QCOMPARE(value, 5); ++count; } @@ -309,7 +246,7 @@ void tst_QArrayData::simpleVector() { int count = 0; - Q_FOREACH (int value, v8) { + for (int value : v8) { QCOMPARE(value, count); ++count; } @@ -428,17 +365,10 @@ void tst_QArrayData::simpleVectorReserve_data() QTest::newRow("empty") << SimpleVector<int>(0, 42) << size_t(0) << size_t(0); QTest::newRow("non-empty") << SimpleVector<int>(5, 42) << size_t(5) << size_t(5); - static const QStaticArrayData<int, 15> array = { - { Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 }, - { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } }; - const QArrayDataPointerRef<int> p = { - static_cast<QTypedArrayData<int> *>( - const_cast<QArrayData *>(&array.header)), - const_cast<int *>(array.data), - sizeof(array.data) / sizeof(array.data[0]) }; - - QTest::newRow("static") << SimpleVector<int>(p) << size_t(0) << size_t(15); - QTest::newRow("raw-data") << SimpleVector<int>::fromRawData(array.data, 15) << size_t(0) << size_t(15); + static const int array[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + + QTest::newRow("raw-data") << SimpleVector<int>::fromRawData(array, 15) << size_t(0) << size_t(15); } void tst_QArrayData::simpleVectorReserve() @@ -494,7 +424,7 @@ struct Deallocator size_t objectSize; size_t alignment; - QVector<QArrayData *> headers; + QList<QArrayData *> headers; }; Q_DECLARE_METATYPE(const QArrayData *) @@ -504,9 +434,7 @@ void tst_QArrayData::allocate_data() { QTest::addColumn<size_t>("objectSize"); QTest::addColumn<size_t>("alignment"); - QTest::addColumn<QArrayData::ArrayOptions>("allocateOptions"); - QTest::addColumn<bool>("isCapacityReserved"); - QTest::addColumn<const QArrayData *>("commonEmpty"); + QTest::addColumn<bool>("grow"); struct { char const *typeName; @@ -518,19 +446,12 @@ void tst_QArrayData::allocate_data() { "void *", sizeof(void *), alignof(void *) } }; - QArrayData *shared_empty; - QArrayData::allocate(&shared_empty, 1, alignof(QArrayData), 0); - QVERIFY(shared_empty); - struct { char const *description; - QArrayData::ArrayOptions allocateOptions; - bool isCapacityReserved; - const QArrayData *commonEmpty; + bool grow; } options[] = { - { "Default", QArrayData::DefaultAllocationFlags, false, shared_empty }, - { "Reserved", QArrayData::CapacityReserved, true, shared_empty }, - { "Grow", QArrayData::GrowsForward, false, shared_empty } + { "Default", false }, + { "Grow", true } }; for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i) @@ -540,42 +461,32 @@ void tst_QArrayData::allocate_data() + QLatin1String(": ") + QLatin1String(options[j].description))) << types[i].objectSize << types[i].alignment - << options[j].allocateOptions << options[j].isCapacityReserved - << options[j].commonEmpty; + << options[j].grow; } void tst_QArrayData::allocate() { QFETCH(size_t, objectSize); QFETCH(size_t, alignment); - QFETCH(QArrayData::ArrayOptions, allocateOptions); - QFETCH(bool, isCapacityReserved); - QFETCH(const QArrayData *, commonEmpty); + QFETCH(bool, grow); // Minimum alignment that can be requested is that of QArrayData. // Typically, this alignment is sizeof(void *) and ensured by malloc. size_t minAlignment = qMax(alignment, alignof(QArrayData)); - // Shared Empty - QArrayData *empty; - QCOMPARE((QArrayData::allocate(&empty, objectSize, minAlignment, 0, - QArrayData::ArrayOptions(allocateOptions)), empty), commonEmpty); - Deallocator keeper(objectSize, minAlignment); keeper.headers.reserve(1024); - for (int capacity = 1; capacity <= 1024; capacity <<= 1) { + for (qsizetype capacity = 1; capacity <= 1024; capacity <<= 1) { QArrayData *data; - void *dataPointer = QArrayData::allocate(&data, objectSize, minAlignment, - capacity, QArrayData::ArrayOptions(allocateOptions)); + void *dataPointer = QArrayData::allocate(&data, objectSize, minAlignment, capacity, grow ? QArrayData::Grow : QArrayData::KeepSize); keeper.headers.append(data); - if (allocateOptions & QArrayData::GrowsForward) - QVERIFY(data->allocatedCapacity() > uint(capacity)); + if (grow) + QCOMPARE_GE(data->allocatedCapacity(), capacity); else - QCOMPARE(data->allocatedCapacity(), size_t(capacity)); - QCOMPARE(bool(data->flags & QArrayData::CapacityReserved), isCapacityReserved); + QCOMPARE(data->allocatedCapacity(), capacity); // Check that the allocated array can be used. Best tested with a // memory checker, such as valgrind, running. @@ -587,8 +498,7 @@ void tst_QArrayData::reallocate() { QFETCH(size_t, objectSize); QFETCH(size_t, alignment); - QFETCH(QArrayData::ArrayOptions, allocateOptions); - QFETCH(bool, isCapacityReserved); + QFETCH(bool, grow); // Minimum alignment that can be requested is that of QArrayData. // Typically, this alignment is sizeof(void *) and ensured by malloc. @@ -597,27 +507,24 @@ void tst_QArrayData::reallocate() int capacity = 10; Deallocator keeper(objectSize, minAlignment); QArrayData *data; - void *dataPointer = QArrayData::allocate(&data, objectSize, minAlignment, capacity, - QArrayData::ArrayOptions(allocateOptions) & ~QArrayData::GrowsForward); + void *dataPointer = QArrayData::allocate(&data, objectSize, minAlignment, capacity, grow ? QArrayData::Grow : QArrayData::KeepSize); keeper.headers.append(data); memset(dataPointer, 'A', objectSize * capacity); // now try to reallocate int newCapacity = 40; - auto pair = QArrayData::reallocateUnaligned(data, dataPointer, objectSize, newCapacity, - QArrayData::ArrayOptions(allocateOptions)); + auto pair = QArrayData::reallocateUnaligned(data, dataPointer, objectSize, newCapacity, grow ? QArrayData::Grow : QArrayData::KeepSize); data = pair.first; dataPointer = pair.second; QVERIFY(data); keeper.headers.clear(); keeper.headers.append(data); - if (allocateOptions & QArrayData::GrowsForward) - QVERIFY(data->allocatedCapacity() > size_t(newCapacity)); + if (grow) + QVERIFY(data->allocatedCapacity() > newCapacity); else - QCOMPARE(data->allocatedCapacity(), size_t(newCapacity)); - QCOMPARE(!(data->flags & QArrayData::CapacityReserved), !isCapacityReserved); + QCOMPARE(data->allocatedCapacity(), newCapacity); for (int i = 0; i < capacity; ++i) QCOMPARE(static_cast<char *>(dataPointer)[i], 'A'); @@ -625,7 +532,7 @@ void tst_QArrayData::reallocate() class Unaligned { - char dummy[8]; + Q_DECL_UNUSED_MEMBER char dummy[8]; }; void tst_QArrayData::alignment_data() @@ -651,8 +558,7 @@ void tst_QArrayData::alignment() for (int i = 0; i < 100; ++i) { QArrayData *data; - void *dataPointer = QArrayData::allocate(&data, sizeof(Unaligned), - minAlignment, 8, QArrayData::DefaultAllocationFlags); + void *dataPointer = QArrayData::allocate(&data, sizeof(Unaligned), minAlignment, 8, QArrayData::KeepSize); keeper.headers.append(data); QVERIFY(data); @@ -685,7 +591,7 @@ void tst_QArrayData::typedData() keeper.headers.append(array); QVERIFY(array); - QCOMPARE(array->allocatedCapacity(), size_t(10)); + QCOMPARE(array->allocatedCapacity(), qsizetype(10)); // Check that the allocated array can be used. Best tested with a // memory checker, such as valgrind, running. @@ -705,7 +611,7 @@ void tst_QArrayData::typedData() keeper.headers.append(array); QVERIFY(array); - QCOMPARE(array->allocatedCapacity(), size_t(10)); + QCOMPARE(array->allocatedCapacity(), qsizetype(10)); // Check that the allocated array can be used. Best tested with a // memory checker, such as valgrind, running. @@ -725,7 +631,7 @@ void tst_QArrayData::typedData() keeper.headers.append(array); QVERIFY(array); - QCOMPARE(array->allocatedCapacity(), size_t(10)); + QCOMPARE(array->allocatedCapacity(), qsizetype(10)); // Check that the allocated array can be used. Best tested with a // memory checker, such as valgrind, running. @@ -742,7 +648,7 @@ void tst_QArrayData::gccBug43247() { // This test tries to verify QArrayData is not affected by GCC optimizer // bug #43247. - // Reported on GCC 4.4.3, Linux, affects QVector + // Reported on GCC 4.4.3, Linux, affects QList QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (3)"); QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (4)"); @@ -751,7 +657,7 @@ void tst_QArrayData::gccBug43247() QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (7)"); SimpleVector<int> array(10, 0); - // QVector<int> vector(10, 0); + // QList<int> list(10, 0); for (int i = 0; i < 10; ++i) { if (i >= 3 && i < 8) @@ -761,7 +667,7 @@ void tst_QArrayData::gccBug43247() // line lets the compiler assume i == 0, and the conditional above is // skipped. QVERIFY(array.at(i) == 0); - // QVERIFY(vector.at(i) == 0); + // QVERIFY(list.at(i) == 0); } } @@ -822,11 +728,25 @@ struct CountedObject static size_t liveCount; }; +bool operator==(const CountedObject &lhs, const CountedObject &rhs) +{ + return lhs.id == rhs.id; // TODO: anything better than this? +} + size_t CountedObject::liveCount = 0; +void tst_QArrayData::arrayOps_data() +{ + QTest::addColumn<bool>("capacityReserved"); + + QTest::newRow("default") << false; + QTest::newRow("capacity-reserved") << true; +} + void tst_QArrayData::arrayOps() { - CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker) + QFETCH(bool, capacityReserved); + CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker); const int intArray[5] = { 80, 101, 100, 114, 111 }; const QString stringArray[5] = { @@ -838,9 +758,12 @@ void tst_QArrayData::arrayOps() }; const CountedObject objArray[5]; - QVERIFY(!QTypeInfo<int>::isComplex && !QTypeInfo<int>::isStatic); - QVERIFY(QTypeInfo<QString>::isComplex && !QTypeInfo<QString>::isStatic); - QVERIFY(QTypeInfo<CountedObject>::isComplex && QTypeInfo<CountedObject>::isStatic); + static_assert(!QTypeInfo<int>::isComplex); + static_assert(QTypeInfo<int>::isRelocatable); + static_assert(QTypeInfo<QString>::isComplex); + static_assert(QTypeInfo<QString>::isRelocatable); + static_assert(QTypeInfo<CountedObject>::isComplex); + static_assert(!QTypeInfo<CountedObject>::isRelocatable); QCOMPARE(CountedObject::liveCount, size_t(5)); for (size_t i = 0; i < 5; ++i) @@ -848,9 +771,9 @@ void tst_QArrayData::arrayOps() //////////////////////////////////////////////////////////////////////////// // copyAppend (I) - SimpleVector<int> vi(intArray, intArray + 5); - SimpleVector<QString> vs(stringArray, stringArray + 5); - SimpleVector<CountedObject> vo(objArray, objArray + 5); + SimpleVector<int> vi(intArray, intArray + 5, capacityReserved); + SimpleVector<QString> vs(stringArray, stringArray + 5, capacityReserved); + SimpleVector<CountedObject> vo(objArray, objArray + 5, capacityReserved); QCOMPARE(CountedObject::liveCount, size_t(10)); for (int i = 0; i < 5; ++i) { @@ -876,9 +799,9 @@ void tst_QArrayData::arrayOps() QString referenceString = QLatin1String("reference"); CountedObject referenceObject; - vi = SimpleVector<int>(5, referenceInt); - vs = SimpleVector<QString>(5, referenceString); - vo = SimpleVector<CountedObject>(5, referenceObject); + vi = SimpleVector<int>(5, referenceInt, capacityReserved); + vs = SimpleVector<QString>(5, referenceString, capacityReserved); + vo = SimpleVector<CountedObject>(5, referenceObject, capacityReserved); QCOMPARE(vi.size(), size_t(5)); QCOMPARE(vs.size(), size_t(5)); @@ -890,8 +813,11 @@ void tst_QArrayData::arrayOps() QVERIFY(vs[i].isSharedWith(referenceString)); QCOMPARE(vo[i].id, referenceObject.id); - QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed - | CountedObject::DefaultConstructed); + + // A temporary object is created as DefaultConstructed | + // CopyConstructed, then it is used instead of the original value to + // construct elements in the container which are CopyConstructed only + //QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed); } //////////////////////////////////////////////////////////////////////////// @@ -941,8 +867,14 @@ void tst_QArrayData::arrayOps() QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); QCOMPARE(vo[i].id, objArray[i % 5].id); - QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed - | CountedObject::CopyAssigned); + + // Insertion at begin (prepend) caused the elements to move, meaning + // that instead of being displaced, newly added elements got constructed + // in uninitialized memory with DefaultConstructed | CopyConstructed + // ### QArrayData::insert does copy assign some of the values, so this test doesn't + // work +// QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed +// | CountedObject::CopyConstructed); } for (int i = 5; i < 15; ++i) { @@ -950,8 +882,8 @@ void tst_QArrayData::arrayOps() QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); QCOMPARE(vo[i].id, objArray[i % 5].id); - QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed - | CountedObject::CopyAssigned); +// QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed +// | CountedObject::CopyAssigned); } for (int i = 15; i < 20; ++i) { @@ -959,8 +891,8 @@ void tst_QArrayData::arrayOps() QVERIFY(vs[i].isSharedWith(referenceString)); QCOMPARE(vo[i].id, referenceObject.id); - QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed - | CountedObject::CopyAssigned); +// QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed +// | CountedObject::CopyAssigned); } for (int i = 20; i < 25; ++i) { @@ -975,8 +907,8 @@ void tst_QArrayData::arrayOps() // Depending on implementation of rotate, final assignment can be: // - straight from source: DefaultConstructed | CopyAssigned // - through a temporary: CopyConstructed | CopyAssigned - QCOMPARE(vo[i].flags & CountedObject::CopyAssigned, - int(CountedObject::CopyAssigned)); +// QCOMPARE(vo[i].flags & CountedObject::CopyAssigned, +// int(CountedObject::CopyAssigned)); } for (int i = 25; i < 30; ++i) { @@ -984,20 +916,26 @@ void tst_QArrayData::arrayOps() QVERIFY(vs[i].isSharedWith(referenceString)); QCOMPARE(vo[i].id, referenceObject.id); - QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed - | CountedObject::CopyAssigned); +// QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed +// | CountedObject::CopyAssigned); } } +void tst_QArrayData::arrayOps2_data() +{ + arrayOps_data(); +} + void tst_QArrayData::arrayOps2() { - CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker) + QFETCH(bool, capacityReserved); + CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker); //////////////////////////////////////////////////////////////////////////// // appendInitialize - SimpleVector<int> vi(5); - SimpleVector<QString> vs(5); - SimpleVector<CountedObject> vo(5); + SimpleVector<int> vi(5, capacityReserved); + SimpleVector<QString> vs(5, capacityReserved); + SimpleVector<CountedObject> vo(5, capacityReserved); QCOMPARE(vi.size(), size_t(5)); QCOMPARE(vs.size(), size_t(5)); @@ -1106,6 +1044,8 @@ void tst_QArrayData::arrayOps2() QVERIFY(vs[i].isNull()); QCOMPARE(vo[i].id, i); + // Erasing not from begin always shifts left - consistency with + // std::vector::erase. Elements before erase position are not affected. QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed | CountedObject::CopyConstructed); } @@ -1129,24 +1069,631 @@ void tst_QArrayData::arrayOps2() } } -Q_DECLARE_METATYPE(QArrayDataPointer<int>) +void tst_QArrayData::arrayOpsExtra_data() +{ + dataPointerAllocate_data(); +} -static inline bool arrayIsFilledWith(const QArrayDataPointer<int> &array, - int fillValue, size_t size) +void tst_QArrayData::arrayOpsExtra() { - const int *iter = array->begin(); - const int *const end = array->end(); + QSKIP("Skipped while changing QArrayData operations.", SkipAll); + QFETCH(QArrayData::GrowthPosition, GrowthPosition); + CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker); + + constexpr size_t inputSize = 5; + const std::array<int, inputSize> intArray = { 80, 101, 100, 114, 111 }; + const std::array<QString, inputSize> stringArray = { + QLatin1String("just"), QLatin1String("for"), QLatin1String("testing"), QLatin1String("a"), + QLatin1String("vector") + }; + const std::array<CountedObject, inputSize> objArray; + + QVERIFY(!QTypeInfo<int>::isComplex && QTypeInfo<int>::isRelocatable); + QVERIFY(QTypeInfo<QString>::isComplex && QTypeInfo<QString>::isRelocatable); + QVERIFY(QTypeInfo<CountedObject>::isComplex && !QTypeInfo<CountedObject>::isRelocatable); + + QCOMPARE(CountedObject::liveCount, inputSize); + for (size_t i = 0; i < 5; ++i) + QCOMPARE(objArray[i].id, i); + + const auto setupDataPointers = [&GrowthPosition] (size_t capacity, size_t initialSize = 0) { + const qsizetype alloc = qsizetype(capacity); + auto i = QArrayDataPointer<int>::allocateGrow(QArrayDataPointer<int>(), alloc, GrowthPosition); + auto s = QArrayDataPointer<QString>::allocateGrow(QArrayDataPointer<QString>(), alloc, GrowthPosition); + auto o = QArrayDataPointer<CountedObject>::allocateGrow(QArrayDataPointer<CountedObject>(), alloc, GrowthPosition); + if (initialSize) { + i->appendInitialize(initialSize); + s->appendInitialize(initialSize); + o->appendInitialize(initialSize); + } + + // assign unique values + std::generate(i.begin(), i.end(), [] () { static int i = 0; return i++; }); + std::generate(s.begin(), s.end(), [] () { static int i = 0; return QString::number(i++); }); + std::generate(o.begin(), o.end(), [] () { return CountedObject(); }); + return std::make_tuple(i, s, o); + }; + + const auto cloneArrayDataPointer = [] (auto &dataPointer, size_t capacity) { + using ArrayPointer = std::decay_t<decltype(dataPointer)>; + ArrayPointer copy{qsizetype(capacity)}; + copy->copyAppend(dataPointer.begin(), dataPointer.end()); + return copy; + }; + + // Test allocation first + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + auto [intData, strData, objData] = setupDataPointers(inputSize); + QVERIFY(intData.size == 0); + QVERIFY(intData.d_ptr() != nullptr); + QVERIFY(size_t(intData.constAllocatedCapacity()) >= inputSize); + QVERIFY(intData.data() != nullptr); + + QVERIFY(strData.size == 0); + QVERIFY(strData.d_ptr() != nullptr); + QVERIFY(size_t(strData.constAllocatedCapacity()) >= inputSize); + QVERIFY(strData.data() != nullptr); + + QVERIFY(objData.size == 0); + QVERIFY(objData.d_ptr() != nullptr); + QVERIFY(size_t(objData.constAllocatedCapacity()) >= inputSize); + QVERIFY(objData.data() != nullptr); + } + + // copyAppend (iterator version) + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + const auto testCopyAppend = [&] (auto &dataPointer, auto first, auto last) { + const size_t originalSize = dataPointer.size; + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + const size_t distance = std::distance(first, last); + + dataPointer->appendIteratorRange(first, last); + QCOMPARE(size_t(dataPointer.size), originalSize + distance); + size_t i = 0; + for (; i < originalSize; ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], *(first + (i - originalSize))); + }; + + auto [intData, strData, objData] = setupDataPointers(inputSize * 2, inputSize / 2); + // empty range + const std::array<int, 0> emptyIntArray{}; + const std::array<QString, 0> emptyStrArray{}; + const std::array<CountedObject, 0> emptyObjArray{}; + RUN_TEST_FUNC(testCopyAppend, intData, emptyIntArray.begin(), emptyIntArray.end()); + RUN_TEST_FUNC(testCopyAppend, strData, emptyStrArray.begin(), emptyStrArray.end()); + RUN_TEST_FUNC(testCopyAppend, objData, emptyObjArray.begin(), emptyObjArray.end()); + + // from arbitrary iterators + RUN_TEST_FUNC(testCopyAppend, intData, intArray.begin(), intArray.end()); + RUN_TEST_FUNC(testCopyAppend, strData, stringArray.begin(), stringArray.end()); + RUN_TEST_FUNC(testCopyAppend, objData, objArray.begin(), objArray.end()); + + // append to full + const size_t intDataFreeSpace = intData.freeSpaceAtEnd(); +// QVERIFY(intDataFreeSpace > 0); + const size_t strDataFreeSpace = strData.freeSpaceAtEnd(); +// QVERIFY(strDataFreeSpace > 0); + const size_t objDataFreeSpace = objData.freeSpaceAtEnd(); +// QVERIFY(objDataFreeSpace > 0); + const std::vector<int> intVec(intDataFreeSpace, int(55)); + const std::vector<QString> strVec(strDataFreeSpace, QLatin1String("filler")); + const std::vector<CountedObject> objVec(objDataFreeSpace, CountedObject()); + RUN_TEST_FUNC(testCopyAppend, intData, intVec.begin(), intVec.end()); + RUN_TEST_FUNC(testCopyAppend, strData, strVec.begin(), strVec.end()); + RUN_TEST_FUNC(testCopyAppend, objData, objVec.begin(), objVec.end()); + QCOMPARE(intData.size, intData.constAllocatedCapacity() - intData.freeSpaceAtBegin()); + QCOMPARE(strData.size, strData.constAllocatedCapacity() - strData.freeSpaceAtBegin()); + QCOMPARE(objData.size, objData.constAllocatedCapacity() - objData.freeSpaceAtBegin()); + } + + // copyAppend (iterator version) - special case of copying from self iterators + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + const auto testCopyAppendSelf = [&] (auto &dataPointer, auto first, auto last) { + const size_t originalSize = dataPointer.size; + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + const size_t distance = std::distance(first, last); + auto firstCopy = copy->begin() + std::distance(dataPointer->begin(), first); + + dataPointer->copyAppend(first, last); + QCOMPARE(size_t(dataPointer.size), originalSize + distance); + size_t i = 0; + for (; i < originalSize; ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], *(firstCopy + (i - originalSize))); + }; - for (size_t i = 0; i < size; ++i, ++iter) - if (*iter != fillValue) - return false; + auto [intData, strData, objData] = setupDataPointers(inputSize * 2, inputSize / 2); + // make no free space at the end + intData->appendInitialize(intData.size + intData.freeSpaceAtEnd()); + strData->appendInitialize(strData.size + strData.freeSpaceAtEnd()); + objData->appendInitialize(objData.size + objData.freeSpaceAtEnd()); + + // make all values unique. this would ensure that we do not have erroneously passed test + int i = 0; + std::generate(intData.begin(), intData.end(), [&i] () { return i++; }); + std::generate(strData.begin(), strData.end(), [&i] () { return QString::number(i++); }); + std::generate(objData.begin(), objData.end(), [] () { return CountedObject(); }); + + // sanity checks: + if (GrowthPosition & QArrayData::GrowsAtBeginning) { + QVERIFY(intData.freeSpaceAtBegin() > 0); + QVERIFY(strData.freeSpaceAtBegin() > 0); + QVERIFY(objData.freeSpaceAtBegin() > 0); + } + QVERIFY(intData.freeSpaceAtBegin() <= intData.size); + QVERIFY(strData.freeSpaceAtBegin() <= strData.size); + QVERIFY(objData.freeSpaceAtBegin() <= objData.size); + QVERIFY(intData.freeSpaceAtEnd() == 0); + QVERIFY(strData.freeSpaceAtEnd() == 0); + QVERIFY(objData.freeSpaceAtEnd() == 0); + + // now, append to full size causing the data to move internally. passed + // iterators that refer to the object itself must be used correctly + RUN_TEST_FUNC(testCopyAppendSelf, intData, intData.begin(), + intData.begin() + intData.freeSpaceAtBegin()); + RUN_TEST_FUNC(testCopyAppendSelf, strData, strData.begin(), + strData.begin() + strData.freeSpaceAtBegin()); + RUN_TEST_FUNC(testCopyAppendSelf, objData, objData.begin(), + objData.begin() + objData.freeSpaceAtBegin()); + } - if (iter != end) - return false; + // copyAppend (value version) + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + const auto testCopyAppend = [&] (auto &dataPointer, size_t n, auto value) { + const size_t originalSize = dataPointer.size; + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + + dataPointer->copyAppend(n, value); + QCOMPARE(size_t(dataPointer.size), originalSize + n); + size_t i = 0; + for (; i < originalSize; ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], value); + }; - return true; + auto [intData, strData, objData] = setupDataPointers(inputSize * 2, inputSize / 2); + // no values + RUN_TEST_FUNC(testCopyAppend, intData, 0, int()); + RUN_TEST_FUNC(testCopyAppend, strData, 0, QString()); + RUN_TEST_FUNC(testCopyAppend, objData, 0, CountedObject()); + + // several values + RUN_TEST_FUNC(testCopyAppend, intData, inputSize, int(5)); + RUN_TEST_FUNC(testCopyAppend, strData, inputSize, QLatin1String("42")); + RUN_TEST_FUNC(testCopyAppend, objData, inputSize, CountedObject()); + + // from self + RUN_TEST_FUNC(testCopyAppend, intData, 2, intData.data()[3]); + RUN_TEST_FUNC(testCopyAppend, strData, 2, strData.data()[3]); + RUN_TEST_FUNC(testCopyAppend, objData, 2, objData.data()[3]); + + // append to full + const size_t intDataFreeSpace = intData.constAllocatedCapacity() - intData.size; + QVERIFY(intDataFreeSpace > 0); + const size_t strDataFreeSpace = strData.constAllocatedCapacity() - strData.size; + QVERIFY(strDataFreeSpace > 0); + const size_t objDataFreeSpace = objData.constAllocatedCapacity() - objData.size; + QVERIFY(objDataFreeSpace > 0); + RUN_TEST_FUNC(testCopyAppend, intData, intDataFreeSpace, int(-1)); + RUN_TEST_FUNC(testCopyAppend, strData, strDataFreeSpace, QLatin1String("foo")); + RUN_TEST_FUNC(testCopyAppend, objData, objDataFreeSpace, CountedObject()); + QCOMPARE(intData.size, intData.constAllocatedCapacity()); + QCOMPARE(strData.size, strData.constAllocatedCapacity()); + QCOMPARE(objData.size, objData.constAllocatedCapacity()); + } + + // copyAppend (value version) - special case of copying self value + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + const auto testCopyAppendSelf = [&] (auto &dataPointer, size_t n, const auto &value) { + const size_t originalSize = dataPointer.size; + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + auto valueCopy = value; + + dataPointer->copyAppend(n, value); + QCOMPARE(size_t(dataPointer.size), originalSize + n); + size_t i = 0; + for (; i < originalSize; ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], valueCopy); + }; + + auto [intData, strData, objData] = setupDataPointers(inputSize * 2, inputSize / 2); + // make no free space at the end + intData->appendInitialize(intData.size + intData.freeSpaceAtEnd()); + strData->appendInitialize(strData.size + strData.freeSpaceAtEnd()); + objData->appendInitialize(objData.size + objData.freeSpaceAtEnd()); + + // make all values unique. this would ensure that we do not have erroneously passed test + int i = 0; + std::generate(intData.begin(), intData.end(), [&i] () { return i++; }); + std::generate(strData.begin(), strData.end(), [&i] () { return QString::number(i++); }); + std::generate(objData.begin(), objData.end(), [] () { return CountedObject(); }); + + // sanity checks: + if (GrowthPosition & QArrayData::GrowsAtBeginning) { + QVERIFY(intData.freeSpaceAtBegin() > 0); + QVERIFY(strData.freeSpaceAtBegin() > 0); + QVERIFY(objData.freeSpaceAtBegin() > 0); + } + QVERIFY(intData.freeSpaceAtEnd() == 0); + QVERIFY(strData.freeSpaceAtEnd() == 0); + QVERIFY(objData.freeSpaceAtEnd() == 0); + + // now, append to full size causing the data to move internally. passed + // value that refers to the object itself must be used correctly + RUN_TEST_FUNC(testCopyAppendSelf, intData, intData.freeSpaceAtBegin(), intData.data()[0]); + RUN_TEST_FUNC(testCopyAppendSelf, strData, strData.freeSpaceAtBegin(), strData.data()[0]); + RUN_TEST_FUNC(testCopyAppendSelf, objData, objData.freeSpaceAtBegin(), objData.data()[0]); + } + + // moveAppend + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + // now there's only one version that accepts "T*" as input parameters + const auto testMoveAppend = [&] (auto &dataPointer, const auto &source) + { + const size_t originalSize = dataPointer.size; + const size_t addedSize = std::distance(source.begin(), source.end()); + auto sourceCopy = source; + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + + dataPointer->moveAppend(sourceCopy.data(), sourceCopy.data() + sourceCopy.size()); + QCOMPARE(size_t(dataPointer.size), originalSize + addedSize); + size_t i = 0; + for (; i < originalSize; ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], source[i - originalSize]); + }; + + auto [intData, strData, objData] = setupDataPointers(inputSize * 2, inputSize / 2); + // empty range + RUN_TEST_FUNC(testMoveAppend, intData, std::array<int, 0>{}); + RUN_TEST_FUNC(testMoveAppend, strData, std::array<QString, 0>{}); + RUN_TEST_FUNC(testMoveAppend, objData, std::array<CountedObject, 0>{}); + + // non-empty range + RUN_TEST_FUNC(testMoveAppend, intData, intArray); + RUN_TEST_FUNC(testMoveAppend, strData, stringArray); + RUN_TEST_FUNC(testMoveAppend, objData, objArray); + + // append to full + const size_t intDataFreeSpace = intData.constAllocatedCapacity() - intData.size; + QVERIFY(intDataFreeSpace > 0); + const size_t strDataFreeSpace = strData.constAllocatedCapacity() - strData.size; + QVERIFY(strDataFreeSpace > 0); + const size_t objDataFreeSpace = objData.constAllocatedCapacity() - objData.size; + QVERIFY(objDataFreeSpace > 0); + RUN_TEST_FUNC(testMoveAppend, intData, std::vector<int>(intDataFreeSpace, int(55))); + RUN_TEST_FUNC(testMoveAppend, strData, + std::vector<QString>(strDataFreeSpace, QLatin1String("barbaz"))); + RUN_TEST_FUNC(testMoveAppend, objData, + std::vector<CountedObject>(objDataFreeSpace, CountedObject())); + QCOMPARE(intData.size, intData.constAllocatedCapacity()); + QCOMPARE(strData.size, strData.constAllocatedCapacity()); + QCOMPARE(objData.size, objData.constAllocatedCapacity()); + } + + // moveAppend - special case of moving from self (this is legal yet rather useless) + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + const auto testMoveAppendSelf = [&] (auto &dataPointer, auto first, auto last) { + const size_t originalSize = dataPointer.size; + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + const size_t addedSize = std::distance(first, last); + const size_t firstPos = std::distance(dataPointer->begin(), first); + auto firstCopy = copy->begin() + firstPos; + + dataPointer->moveAppend(first, last); + QCOMPARE(size_t(dataPointer.size), originalSize + addedSize); + size_t i = 0; + for (; i < originalSize; ++i) { + if (i >= firstPos && i < (firstPos + addedSize)) // skip "moved from" chunk + continue; + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + } + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], *(firstCopy + (i - originalSize))); + }; + + auto [intData, strData, objData] = setupDataPointers(inputSize * 2, inputSize / 2); + // make no free space at the end + intData->appendInitialize(intData.size + intData.freeSpaceAtEnd()); + strData->appendInitialize(strData.size + strData.freeSpaceAtEnd()); + objData->appendInitialize(objData.size + objData.freeSpaceAtEnd()); + + // make all values unique. this would ensure that we do not have erroneously passed test + int i = 0; + std::generate(intData.begin(), intData.end(), [&i] () { return i++; }); + std::generate(strData.begin(), strData.end(), [&i] () { return QString::number(i++); }); + std::generate(objData.begin(), objData.end(), [] () { return CountedObject(); }); + + // sanity checks: + if (GrowthPosition & QArrayData::GrowsAtBeginning) { + QVERIFY(intData.freeSpaceAtBegin() > 0); + QVERIFY(strData.freeSpaceAtBegin() > 0); + QVERIFY(objData.freeSpaceAtBegin() > 0); + } + QVERIFY(intData.freeSpaceAtBegin() <= intData.size); + QVERIFY(strData.freeSpaceAtBegin() <= strData.size); + QVERIFY(objData.freeSpaceAtBegin() <= objData.size); + QVERIFY(intData.freeSpaceAtEnd() == 0); + QVERIFY(strData.freeSpaceAtEnd() == 0); + QVERIFY(objData.freeSpaceAtEnd() == 0); + + // now, append to full size causing the data to move internally. passed + // iterators that refer to the object itself must be used correctly + RUN_TEST_FUNC(testMoveAppendSelf, intData, intData.begin(), + intData.begin() + intData.freeSpaceAtBegin()); + RUN_TEST_FUNC(testMoveAppendSelf, strData, strData.begin(), + strData.begin() + strData.freeSpaceAtBegin()); + RUN_TEST_FUNC(testMoveAppendSelf, objData, objData.begin(), + objData.begin() + objData.freeSpaceAtBegin()); + } + + // truncate + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + const auto testTruncate = [&] (auto &dataPointer, size_t newSize) + { + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + dataPointer->truncate(newSize); + QCOMPARE(size_t(dataPointer.size), newSize); + for (size_t i = 0; i < newSize; ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + }; + + auto [intData, strData, objData] = setupDataPointers(inputSize, inputSize); + // truncate one + RUN_TEST_FUNC(testTruncate, intData, inputSize - 1); + RUN_TEST_FUNC(testTruncate, strData, inputSize - 1); + RUN_TEST_FUNC(testTruncate, objData, inputSize - 1); + + // truncate all + RUN_TEST_FUNC(testTruncate, intData, 0); + RUN_TEST_FUNC(testTruncate, strData, 0); + RUN_TEST_FUNC(testTruncate, objData, 0); + } + + // insert + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + const auto testInsertRange = [&] (auto &dataPointer, size_t pos, auto first, auto last) + { + const size_t originalSize = dataPointer.size; + const size_t distance = std::distance(first, last); + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + + dataPointer->insert(pos, first, last - first); + QCOMPARE(size_t(dataPointer.size), originalSize + distance); + size_t i = 0; + for (; i < pos; ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + for (; i < pos + distance; ++i) + QCOMPARE(dataPointer.data()[i], *(first + (i - pos))); + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i - distance]); + }; + + const auto testInsertValue = [&] (auto &dataPointer, size_t pos, size_t n, auto value) + { + const size_t originalSize = dataPointer.size; + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + + dataPointer->insert(pos, n, value); + QCOMPARE(size_t(dataPointer.size), originalSize + n); + size_t i = 0; + for (; i < pos; ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + for (; i < pos + n; ++i) + QCOMPARE(dataPointer.data()[i], value); + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i - n]); + }; + + auto [intData, strData, objData] = setupDataPointers(100, 10); + + // empty ranges + RUN_TEST_FUNC(testInsertRange, intData, 0, intArray.data(), intArray.data()); + RUN_TEST_FUNC(testInsertRange, strData, 0, stringArray.data(), stringArray.data()); + RUN_TEST_FUNC(testInsertRange, objData, 0, objArray.data(), objArray.data()); + RUN_TEST_FUNC(testInsertValue, intData, 1, 0, int()); + RUN_TEST_FUNC(testInsertValue, strData, 1, 0, QString()); + RUN_TEST_FUNC(testInsertValue, objData, 1, 0, CountedObject()); + + // insert at the beginning + RUN_TEST_FUNC(testInsertRange, intData, 0, intArray.data(), intArray.data() + 1); + RUN_TEST_FUNC(testInsertRange, strData, 0, stringArray.data(), stringArray.data() + 1); + RUN_TEST_FUNC(testInsertRange, objData, 0, objArray.data(), objArray.data() + 1); + RUN_TEST_FUNC(testInsertValue, intData, 0, 1, int(-100)); + RUN_TEST_FUNC(testInsertValue, strData, 0, 1, QLatin1String("12")); + RUN_TEST_FUNC(testInsertValue, objData, 0, 1, CountedObject()); + + // insert into the middle (with the left part of the data being smaller) + RUN_TEST_FUNC(testInsertRange, intData, 1, intArray.data() + 2, intArray.data() + 4); + RUN_TEST_FUNC(testInsertRange, strData, 1, stringArray.data() + 2, stringArray.data() + 4); + RUN_TEST_FUNC(testInsertRange, objData, 1, objArray.data() + 2, objArray.data() + 4); + RUN_TEST_FUNC(testInsertValue, intData, 2, 2, int(11)); + RUN_TEST_FUNC(testInsertValue, strData, 2, 2, QLatin1String("abcdefxdeadbeef")); + RUN_TEST_FUNC(testInsertValue, objData, 2, 2, CountedObject()); + + // insert into the middle (with the right part of the data being smaller) + RUN_TEST_FUNC(testInsertRange, intData, intData.size - 1, intArray.data(), + intArray.data() + intArray.size()); + RUN_TEST_FUNC(testInsertRange, strData, strData.size - 1, stringArray.data(), + stringArray.data() + stringArray.size()); + RUN_TEST_FUNC(testInsertRange, objData, objData.size - 1, objArray.data(), + objArray.data() + objArray.size()); + RUN_TEST_FUNC(testInsertValue, intData, intData.size - 3, 3, int(512)); + RUN_TEST_FUNC(testInsertValue, strData, strData.size - 3, 3, QLatin1String("foo")); + RUN_TEST_FUNC(testInsertValue, objData, objData.size - 3, 3, CountedObject()); + + // insert at the end + RUN_TEST_FUNC(testInsertRange, intData, intData.size, intArray.data(), intArray.data() + 3); + RUN_TEST_FUNC(testInsertRange, strData, strData.size, stringArray.data(), + stringArray.data() + 3); + RUN_TEST_FUNC(testInsertRange, objData, objData.size, objArray.data(), objArray.data() + 3); + RUN_TEST_FUNC(testInsertValue, intData, intData.size, 1, int(-42)); + RUN_TEST_FUNC(testInsertValue, strData, strData.size, 1, QLatin1String("hello, world")); + RUN_TEST_FUNC(testInsertValue, objData, objData.size, 1, CountedObject()); + } + + // insert - special case of inserting from self value. this test only makes + // sense for prepend - insert at begin. + { + const auto testInsertValueSelf = [&] (auto &dataPointer, size_t n, const auto &value) { + const size_t originalSize = dataPointer.size; + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + auto valueCopy = value; + + dataPointer->insert(0, n, value); + QCOMPARE(size_t(dataPointer.size), originalSize + n); + size_t i = 0; + for (; i < n; ++i) + QCOMPARE(dataPointer.data()[i], valueCopy); + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i - n]); + }; + + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + auto [intData, strData, objData] = setupDataPointers(inputSize * 2, inputSize / 2); + + // make no free space at the begin + intData->insert(0, intData.freeSpaceAtBegin(), intData.data()[0]); + strData->insert(0, strData.freeSpaceAtBegin(), strData.data()[0]); + objData->insert(0, objData.freeSpaceAtBegin(), objData.data()[0]); + + // make all values unique. this would ensure that we do not have erroneously passed test + int i = 0; + std::generate(intData.begin(), intData.end(), [&i] () { return i++; }); + std::generate(strData.begin(), strData.end(), [&i] () { return QString::number(i++); }); + std::generate(objData.begin(), objData.end(), [] () { return CountedObject(); }); + + // sanity checks: + QVERIFY(intData.freeSpaceAtEnd() > 0); + QVERIFY(strData.freeSpaceAtEnd() > 0); + QVERIFY(objData.freeSpaceAtEnd() > 0); + QVERIFY(intData.freeSpaceAtBegin() == 0); + QVERIFY(strData.freeSpaceAtBegin() == 0); + QVERIFY(objData.freeSpaceAtBegin() == 0); + + // now, prepend to full size causing the data to move internally. passed + // value that refers to the object itself must be used correctly + RUN_TEST_FUNC(testInsertValueSelf, intData, intData.freeSpaceAtEnd(), + intData.data()[intData.size - 1]); + RUN_TEST_FUNC(testInsertValueSelf, strData, strData.freeSpaceAtEnd(), + strData.data()[strData.size - 1]); + RUN_TEST_FUNC(testInsertValueSelf, objData, objData.freeSpaceAtEnd(), + objData.data()[objData.size - 1]); + } + + // emplace + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + // testing simple case when emplacing a copy of the same type + const auto testEmplace = [&] (auto &dataPointer, size_t pos, auto value) + { + const size_t originalSize = dataPointer.size; + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + + dataPointer->emplace(pos, value); + QCOMPARE(size_t(dataPointer.size), originalSize + 1); + size_t i = 0; + for (; i < pos; ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + QCOMPARE(dataPointer.data()[i++], value); + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i - 1]); + }; + + auto [intData, strData, objData] = setupDataPointers(20, 5); + + // emplace at the beginning + RUN_TEST_FUNC(testEmplace, intData, 0, int(2)); + RUN_TEST_FUNC(testEmplace, strData, 0, QLatin1String("foo")); + RUN_TEST_FUNC(testEmplace, objData, 0, CountedObject()); + // emplace into the middle (with the left part of the data being smaller) + RUN_TEST_FUNC(testEmplace, intData, 1, int(-1)); + RUN_TEST_FUNC(testEmplace, strData, 1, QLatin1String("bar")); + RUN_TEST_FUNC(testEmplace, objData, 1, CountedObject()); + // emplace into the middle (with the right part of the data being smaller) + RUN_TEST_FUNC(testEmplace, intData, intData.size - 2, int(42)); + RUN_TEST_FUNC(testEmplace, strData, strData.size - 2, QLatin1String("baz")); + RUN_TEST_FUNC(testEmplace, objData, objData.size - 2, CountedObject()); + // emplace at the end + RUN_TEST_FUNC(testEmplace, intData, intData.size, int(123)); + RUN_TEST_FUNC(testEmplace, strData, strData.size, QLatin1String("bak")); + RUN_TEST_FUNC(testEmplace, objData, objData.size, CountedObject()); + } + + // erase + { + CountedObject::LeakChecker localLeakChecker; Q_UNUSED(localLeakChecker); + const auto testErase = [&] (auto &dataPointer, auto first, auto last) + { + const size_t originalSize = dataPointer.size; + const size_t distance = std::distance(first, last); + const size_t pos = std::distance(dataPointer.begin(), first); + auto copy = cloneArrayDataPointer(dataPointer, dataPointer.size); + + dataPointer->erase(first, last - first); + QCOMPARE(size_t(dataPointer.size), originalSize - distance); + size_t i = 0; + for (; i < pos; ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i]); + for (; i < size_t(dataPointer.size); ++i) + QCOMPARE(dataPointer.data()[i], copy.data()[i + distance]); + }; + + auto [intData, strData, objData] = setupDataPointers(100, 100); + + // erase chunk from the beginning + RUN_TEST_FUNC(testErase, intData, intData.begin(), intData.begin() + 10); + RUN_TEST_FUNC(testErase, strData, strData.begin(), strData.begin() + 10); + RUN_TEST_FUNC(testErase, objData, objData.begin(), objData.begin() + 10); + + // erase chunk from the end + RUN_TEST_FUNC(testErase, intData, intData.end() - 10, intData.end()); + RUN_TEST_FUNC(testErase, strData, strData.end() - 10, strData.end()); + RUN_TEST_FUNC(testErase, objData, objData.end() - 10, objData.end()); + + // erase the middle chunk + RUN_TEST_FUNC(testErase, intData, intData.begin() + (intData.size / 2) - 5, + intData.begin() + (intData.size / 2) + 5); + RUN_TEST_FUNC(testErase, strData, strData.begin() + (strData.size / 2) - 5, + strData.begin() + (strData.size / 2) + 5); + RUN_TEST_FUNC(testErase, objData, objData.begin() + (objData.size / 2) - 5, + objData.begin() + (objData.size / 2) + 5); + + // erase chunk in the left part of the data + RUN_TEST_FUNC(testErase, intData, intData.begin() + 1, intData.begin() + 6); + RUN_TEST_FUNC(testErase, strData, strData.begin() + 1, strData.begin() + 6); + RUN_TEST_FUNC(testErase, objData, objData.begin() + 1, objData.begin() + 6); + + // erase chunk in the right part of the data + RUN_TEST_FUNC(testErase, intData, intData.end() - 6, intData.end() - 1); + RUN_TEST_FUNC(testErase, strData, strData.end() - 6, strData.end() - 1); + RUN_TEST_FUNC(testErase, objData, objData.end() - 6, objData.end() - 1); + + // erase all + RUN_TEST_FUNC(testErase, intData, intData.begin(), intData.end()); + RUN_TEST_FUNC(testErase, strData, strData.begin(), strData.end()); + RUN_TEST_FUNC(testErase, objData, objData.begin(), objData.end()); + } } +Q_DECLARE_METATYPE(QArrayDataPointer<int>) + struct ResetOnDtor { ResetOnDtor() @@ -1180,15 +1727,15 @@ void fromRawData_impl() { // Default: Immutable, sharable SimpleVector<T> raw = SimpleVector<T>::fromRawData(array, - sizeof(array)/sizeof(array[0]), QArrayData::DefaultRawFlags); + sizeof(array)/sizeof(array[0])); QCOMPARE(raw.size(), size_t(11)); QCOMPARE((const T *)raw.constBegin(), array); QCOMPARE((const T *)raw.constEnd(), (const T *)(array + sizeof(array)/sizeof(array[0]))); - QVERIFY(!raw.isShared()); + QVERIFY(raw.isShared()); QVERIFY(SimpleVector<T>(raw).isSharedWith(raw)); - QVERIFY(!raw.isShared()); + QVERIFY(raw.isShared()); // Detach QCOMPARE(raw.back(), T(11)); @@ -1225,46 +1772,62 @@ void tst_QArrayData::literals() { { QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ"); - QCOMPARE(d.size, 10u + 1u); + QCOMPARE(d.size, 10 + 1); for (int i = 0; i < 10; ++i) QCOMPARE(d.data()[i], char('A' + i)); } { - // wchar_t is not necessarily 2-bytes - QArrayDataPointer<wchar_t> d = Q_ARRAY_LITERAL(wchar_t, L"ABCDEFGHIJ"); - QCOMPARE(d.size, 10u + 1u); + QList<char> l(Q_ARRAY_LITERAL(char, "ABCDEFGHIJ")); + QCOMPARE(l.size(), 11); + QCOMPARE(l.capacity(), 0); for (int i = 0; i < 10; ++i) - QCOMPARE(d.data()[i], wchar_t('A' + i)); - } - - { - SimpleVector<char> v = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ"); + QCOMPARE(l.at(i), char('A' + i)); - QVERIFY(!v.isNull()); - QVERIFY(!v.isEmpty()); - QCOMPARE(v.size(), size_t(11)); - // v.capacity() is unspecified, for now - - QVERIFY(v.isStatic()); - QCOMPARE((void*)(const char*)(v.constBegin() + v.size()), (void*)(const char*)v.constEnd()); + (void)l.begin(); // "detach" + QCOMPARE(l.size(), 11); + QVERIFY(l.capacity() >= l.size()); for (int i = 0; i < 10; ++i) - QCOMPARE(const_(v)[i], char('A' + i)); - QCOMPARE(const_(v)[10], char('\0')); + QCOMPARE(l[i], char('A' + i)); } { - struct LiteralType { - int value; - Q_DECL_CONSTEXPR LiteralType(int v = 0) : value(v) {} - }; + // wchar_t is not necessarily 2-bytes + QArrayDataPointer<wchar_t> d = Q_ARRAY_LITERAL(wchar_t, L"ABCDEFGHIJ"); + QCOMPARE(d.size, 10 + 1); + for (int i = 0; i < 10; ++i) + QCOMPARE(d.data()[i], wchar_t('A' + i)); + } + struct LiteralType { + int value; + constexpr LiteralType(int v = 0) : value(v) {} + }; + + { QArrayDataPointer<LiteralType> d = Q_ARRAY_LITERAL(LiteralType, LiteralType(0), LiteralType(1), LiteralType(2)); QCOMPARE(d->size, 3); for (int i = 0; i < 3; ++i) QCOMPARE(d->data()[i].value, i); } + + { + QList<LiteralType> l(Q_ARRAY_LITERAL(LiteralType, LiteralType(0), LiteralType(1), LiteralType(2))); + QCOMPARE(l.size(), 3); + QCOMPARE(l.capacity(), 0); + for (int i = 0; i < 3; ++i) + QCOMPARE(l.at(i).value, i); + l.squeeze(); // shouldn't detach + QCOMPARE(l.capacity(), 0); + + (void)l.begin(); // "detach" + + QCOMPARE(l.size(), 3); + QVERIFY(l.capacity() >= l.size()); + for (int i = 0; i < 3; ++i) + QCOMPARE(l[i].value, i); + } } // Variadic Q_ARRAY_LITERAL need to be available in the current configuration. @@ -1273,7 +1836,7 @@ void tst_QArrayData::variadicLiterals() { QArrayDataPointer<int> d = Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - QCOMPARE(d.size, 10u); + QCOMPARE(d.size, 10); for (int i = 0; i < 10; ++i) QCOMPARE(d.data()[i], i); } @@ -1281,7 +1844,7 @@ void tst_QArrayData::variadicLiterals() { QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'); - QCOMPARE(d.size, 10u); + QCOMPARE(d.size, 10); for (int i = 0; i < 10; ++i) QCOMPARE(d.data()[i], char('A' + i)); } @@ -1289,27 +1852,12 @@ void tst_QArrayData::variadicLiterals() { QArrayDataPointer<const char *> d = Q_ARRAY_LITERAL(const char *, "A", "B", "C", "D", "E", "F", "G", "H", "I", "J"); - QCOMPARE(d.size, 10u); + QCOMPARE(d.size, 10); for (int i = 0; i < 10; ++i) { QCOMPARE(d.data()[i][0], char('A' + i)); QCOMPARE(d.data()[i][1], '\0'); } } - - { - SimpleVector<int> v = Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6); - - QVERIFY(!v.isNull()); - QVERIFY(!v.isEmpty()); - QCOMPARE(v.size(), size_t(7)); - // v.capacity() is unspecified, for now - - QVERIFY(v.isStatic()); - QCOMPARE((const int *)(v.constBegin() + v.size()), (const int *)v.constEnd()); - - for (int i = 0; i < 7; ++i) - QCOMPARE(const_(v)[i], i); - } } // std::remove_reference is in C++11, but requires library support @@ -1333,17 +1881,17 @@ struct CompilerHasCxx11ImplicitMoves struct DetectConstructor { - Q_DECL_CONSTEXPR DetectConstructor() + constexpr DetectConstructor() : constructor(DefaultConstructor) { } - Q_DECL_CONSTEXPR DetectConstructor(const DetectConstructor &) + constexpr DetectConstructor(const DetectConstructor &) : constructor(CopyConstructor) { } - Q_DECL_CONSTEXPR DetectConstructor(DetectConstructor &&) + constexpr DetectConstructor(DetectConstructor &&) : constructor(MoveConstructor) { } @@ -1443,5 +1991,546 @@ void tst_QArrayData::grow() } } +void tst_QArrayData::freeSpace_data() +{ + QTest::addColumn<size_t>("n"); + + for (const size_t n : {1, 3, 5, 7, 16, 25}) { + QString suffix = QString::number(n) + QLatin1String("-elements"); + QTest::newRow(qPrintable(QLatin1String("alloc-") + suffix)) + << n; + } +} + +void tst_QArrayData::freeSpace() +{ + QFETCH(size_t, n); + const auto testFreeSpace = [] (auto dummy, qsizetype n) { + using Type = std::decay_t<decltype(dummy)>; + using DataPointer = QArrayDataPointer<Type>; + Q_UNUSED(dummy); + const qsizetype capacity = n + 1; + auto ptr = DataPointer::allocateGrow(DataPointer(), capacity, QArrayData::GrowsAtEnd); + const auto alloc = qsizetype(ptr.constAllocatedCapacity()); + QVERIFY(alloc >= capacity); + QCOMPARE(ptr.freeSpaceAtBegin() + ptr.freeSpaceAtEnd(), alloc); + }; + RUN_TEST_FUNC(testFreeSpace, char(0), n); + RUN_TEST_FUNC(testFreeSpace, char16_t(0), n); + RUN_TEST_FUNC(testFreeSpace, int(0), n); + RUN_TEST_FUNC(testFreeSpace, QString(), n); + RUN_TEST_FUNC(testFreeSpace, CountedObject(), n); +} + +void tst_QArrayData::dataPointerAllocate_data() +{ + QTest::addColumn<QArrayData::GrowthPosition>("GrowthPosition"); + + QTest::newRow("at-end") << QArrayData::GrowsAtEnd; + QTest::newRow("at-begin") << QArrayData::GrowsAtBeginning; +} + +void tst_QArrayData::dataPointerAllocate() +{ + QFETCH(QArrayData::GrowthPosition, GrowthPosition); + const auto createDataPointer = [] (qsizetype capacity, auto initValue) { + using Type = std::decay_t<decltype(initValue)>; + Q_UNUSED(initValue); + return QArrayDataPointer<Type>(capacity); + }; + + const auto testRealloc = [&] (qsizetype capacity, qsizetype newSize, auto initValue) { + using Type = std::decay_t<decltype(initValue)>; + using DataPointer = QArrayDataPointer<Type>; + + auto oldDataPointer = createDataPointer(capacity, initValue); + oldDataPointer->insert(0, 1, initValue); + oldDataPointer->insert(0, 1, initValue); // trigger prepend + QVERIFY(!oldDataPointer.needsDetach()); + + auto newDataPointer = DataPointer::allocateGrow(oldDataPointer, newSize, GrowthPosition); + const auto newAlloc = newDataPointer.constAllocatedCapacity(); + const auto freeAtBegin = newDataPointer.freeSpaceAtBegin(); + const auto freeAtEnd = newDataPointer.freeSpaceAtEnd(); + + QVERIFY(newAlloc >= oldDataPointer.constAllocatedCapacity()); + QCOMPARE(freeAtBegin + freeAtEnd, newAlloc); + if (GrowthPosition == QArrayData::GrowsAtBeginning) { + QVERIFY(freeAtBegin > 0); + } else if (GrowthPosition & QArrayData::GrowsAtEnd) { + QCOMPARE(freeAtBegin, oldDataPointer.freeSpaceAtBegin()); + QVERIFY(freeAtEnd > 0); + } + }; + + for (size_t n : {10, 512, 1000}) { + RUN_TEST_FUNC(testRealloc, n, n + 1, int(0)); + RUN_TEST_FUNC(testRealloc, n, n + 1, char('a')); + RUN_TEST_FUNC(testRealloc, n, n + 1, char16_t(u'a')); + RUN_TEST_FUNC(testRealloc, n, n + 1, QString("hello, world!")); + RUN_TEST_FUNC(testRealloc, n, n + 1, CountedObject()); + } + + const auto testDetachRealloc = [&] (qsizetype capacity, qsizetype newSize, auto initValue) { + using Type = std::decay_t<decltype(initValue)>; + using DataPointer = QArrayDataPointer<Type>; + + auto oldDataPointer = createDataPointer(capacity, initValue); + oldDataPointer->insert(0, 1, initValue); // trigger prepend + auto oldDataPointerCopy = oldDataPointer; // force detach later + QVERIFY(oldDataPointer.needsDetach()); + + auto newDataPointer = DataPointer::allocateGrow(oldDataPointer, oldDataPointer->detachCapacity(newSize), GrowthPosition); + const auto newAlloc = newDataPointer.constAllocatedCapacity(); + const auto freeAtBegin = newDataPointer.freeSpaceAtBegin(); + const auto freeAtEnd = newDataPointer.freeSpaceAtEnd(); + + QVERIFY(newAlloc > oldDataPointer.constAllocatedCapacity()); + QCOMPARE(freeAtBegin + freeAtEnd, newAlloc); + if (GrowthPosition == QArrayData::GrowsAtBeginning) { + QVERIFY(freeAtBegin > 0); + } else if (GrowthPosition & QArrayData::GrowsAtEnd) { + QCOMPARE(freeAtBegin, oldDataPointer.freeSpaceAtBegin()); + QVERIFY(freeAtEnd > 0); + } + }; + + for (size_t n : {10, 512, 1000}) { + RUN_TEST_FUNC(testDetachRealloc, n, n + 1, int(0)); + RUN_TEST_FUNC(testDetachRealloc, n, n + 1, char('a')); + RUN_TEST_FUNC(testDetachRealloc, n, n + 1, char16_t(u'a')); + RUN_TEST_FUNC(testDetachRealloc, n, n + 1, QString("hello, world!")); + RUN_TEST_FUNC(testDetachRealloc, n, n + 1, CountedObject()); + } +} + +struct MyQStringWrapper : public QString +{ + bool movedTo = false; + bool movedFrom = false; + MyQStringWrapper() = default; + MyQStringWrapper(QChar c) : QString(c) { } + MyQStringWrapper(MyQStringWrapper &&other) : QString(std::move(static_cast<QString &>(other))) + { + movedTo = true; + movedFrom = other.movedFrom; + other.movedFrom = true; + } + MyQStringWrapper &operator=(MyQStringWrapper &&other) + { + QString::operator=(std::move(static_cast<QString &>(other))); + movedTo = true; + movedFrom = other.movedFrom; + other.movedFrom = true; + return *this; + } + MyQStringWrapper(const MyQStringWrapper &) = default; + MyQStringWrapper &operator=(const MyQStringWrapper &) = default; + ~MyQStringWrapper() = default; +}; + +struct MyMovableQString : public MyQStringWrapper +{ + MyMovableQString() = default; + MyMovableQString(QChar c) : MyQStringWrapper(c) { } + +private: + friend bool operator==(const MyMovableQString &a, QChar c) + { + return static_cast<QString>(a) == QString(c); + } + + friend bool operator==(const MyMovableQString &a, const MyMovableQString &b) + { + return static_cast<QString>(a) == static_cast<QString>(b); + } +}; + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(MyMovableQString, Q_RELOCATABLE_TYPE); +QT_END_NAMESPACE +static_assert(QTypeInfo<MyMovableQString>::isComplex); +static_assert(QTypeInfo<MyMovableQString>::isRelocatable); + +struct MyComplexQString : public MyQStringWrapper +{ + MyComplexQString() = default; + MyComplexQString(QChar c) : MyQStringWrapper(c) { } + +private: + friend bool operator==(const MyComplexQString &a, QChar c) + { + return static_cast<QString>(a) == QString(c); + } + + friend bool operator==(const MyComplexQString &a, const MyComplexQString &b) + { + return static_cast<QString>(a) == static_cast<QString>(b); + } +}; +static_assert(QTypeInfo<MyComplexQString>::isComplex); +static_assert(!QTypeInfo<MyComplexQString>::isRelocatable); + +void tst_QArrayData::selfEmplaceBackwards() +{ + const auto createDataPointer = [](qsizetype capacity, int spaceAtEnd, auto dummy) { + using Type = std::decay_t<decltype(dummy)>; + Q_UNUSED(dummy); + auto [header, ptr] = QTypedArrayData<Type>::allocate(capacity, QArrayData::Grow); + // do custom adjustments to make sure there's free space at end + ptr += header->alloc - spaceAtEnd; + return QArrayDataPointer(header, ptr); + }; + + const auto testSelfEmplace = [&](auto dummy, int spaceAtEnd, auto initValues) { + auto adp = createDataPointer(100, spaceAtEnd, dummy); + for (auto v : initValues) { + adp->emplace(adp.size, v); + } + QVERIFY(!adp.freeSpaceAtEnd()); + QVERIFY(adp.freeSpaceAtBegin()); + + adp->emplace(adp.size, adp.data()[0]); + for (qsizetype i = 0; i < adp.size - 1; ++i) { + QCOMPARE(adp.data()[i], initValues[i]); + } + QCOMPARE(adp.data()[adp.size - 1], initValues[0]); + + adp->emplace(adp.size, std::move(adp.data()[0])); + for (qsizetype i = 1; i < adp.size - 2; ++i) { + QCOMPARE(adp.data()[i], initValues[i]); + } + QCOMPARE(adp.data()[adp.size - 2], initValues[0]); + QCOMPARE(adp.data()[0].movedFrom, true); + QCOMPARE(adp.data()[adp.size - 1], initValues[0]); + QCOMPARE(adp.data()[adp.size - 1].movedTo, true); + }; + + QList<QChar> movableObjs { u'a', u'b', u'c', u'd' }; + RUN_TEST_FUNC(testSelfEmplace, MyMovableQString(), 4, movableObjs); + QList<QChar> complexObjs { u'a', u'b', u'c', u'd' }; + RUN_TEST_FUNC(testSelfEmplace, MyComplexQString(), 4, complexObjs); +} + +void tst_QArrayData::selfEmplaceForward() +{ + const auto createDataPointer = [](qsizetype capacity, int spaceAtBegin, auto dummy) { + using Type = std::decay_t<decltype(dummy)>; + Q_UNUSED(dummy); + auto [header, ptr] = QTypedArrayData<Type>::allocate(capacity, QArrayData::Grow); + // do custom adjustments to make sure there's free space at end + ptr += spaceAtBegin; + return QArrayDataPointer(header, ptr); + }; + + const auto testSelfEmplace = [&](auto dummy, int spaceAtBegin, auto initValues) { + // need a -1 below as the first emplace will go towards the end (as the array is still empty) + auto adp = createDataPointer(100, spaceAtBegin - 1, dummy); + auto reversedInitValues = initValues; + std::reverse(reversedInitValues.begin(), reversedInitValues.end()); + for (auto v : reversedInitValues) { + adp->emplace(0, v); + } + QVERIFY(!adp.freeSpaceAtBegin()); + QVERIFY(adp.freeSpaceAtEnd()); + + adp->emplace(0, adp.data()[adp.size - 1]); + for (qsizetype i = 1; i < adp.size; ++i) { + QCOMPARE(adp.data()[i], initValues[i - 1]); + } + QCOMPARE(adp.data()[0], initValues[spaceAtBegin - 1]); + + adp->emplace(0, std::move(adp.data()[adp.size - 1])); + for (qsizetype i = 2; i < adp.size - 1; ++i) { + QCOMPARE(adp.data()[i], initValues[i - 2]); + } + QCOMPARE(adp.data()[1], initValues[spaceAtBegin - 1]); + QCOMPARE(adp.data()[adp.size - 1].movedFrom, true); + QCOMPARE(adp.data()[0], initValues[spaceAtBegin - 1]); + QCOMPARE(adp.data()[0].movedTo, true); + }; + + QList<QChar> movableObjs { u'a', u'b', u'c', u'd' }; + RUN_TEST_FUNC(testSelfEmplace, MyMovableQString(), 4, movableObjs); + QList<QChar> complexObjs { u'a', u'b', u'c', u'd' }; + RUN_TEST_FUNC(testSelfEmplace, MyComplexQString(), 4, complexObjs); +} + +#ifndef QT_NO_EXCEPTIONS +struct ThrowingTypeWatcher +{ + std::vector<void *> destroyedAddrs; + bool watch = false; + + void destroyed(void *addr) + { + if (watch) + destroyedAddrs.push_back(addr); + } +}; + +ThrowingTypeWatcher &throwingTypeWatcher() +{ + static ThrowingTypeWatcher global; + return global; +} + +struct ThrowingType +{ + static unsigned int throwOnce; + static constexpr char throwString[] = "Requested to throw"; + enum MoveCase { + MoveRightNoOverlap, + MoveRightOverlap, + MoveLeftNoOverlap, + MoveLeftOverlap, + }; + enum ThrowCase { + NoThrow, + ThrowInUninitializedRegion, + ThrowInOverlapRegion, + }; + + // reinforce basic checkers with std::shared_ptr which happens to signal + // very explicitly about use-after-free and so on under ASan + std::shared_ptr<int> doubleFreeHelper = std::shared_ptr<int>(new int(42)); + int id = 0; + + void checkThrow() + { + // deferred throw + if (throwOnce > 0) { + --throwOnce; + if (throwOnce == 0) { + throw std::runtime_error(throwString); + } + } + return; + } + + void copy(const ThrowingType &other) noexcept(false) + { + doubleFreeHelper = other.doubleFreeHelper; + id = other.id; + checkThrow(); + } + + ThrowingType(int val = 0) noexcept(false) : id(val) { checkThrow(); } + ThrowingType(const ThrowingType &other) noexcept(false) { copy(other); } + ThrowingType &operator=(const ThrowingType &other) noexcept(false) + { + copy(other); + return *this; + } + ThrowingType(ThrowingType &&other) noexcept(false) { copy(other); } + ThrowingType &operator=(ThrowingType &&other) noexcept(false) + { + copy(other); + return *this; + } + ~ThrowingType() noexcept(true) + { + throwingTypeWatcher().destroyed(this); // notify global watcher + id = -1; + // if we're in dtor but use_count is 0, it's double free + QVERIFY(doubleFreeHelper.use_count() > 0); + } + + friend bool operator==(const ThrowingType &a, const ThrowingType &b) { return a.id == b.id; } +}; + +unsigned int ThrowingType::throwOnce = 0; +static_assert(!QTypeInfo<ThrowingType>::isRelocatable); + +void tst_QArrayData::relocateWithExceptions_data() +{ + QTest::addColumn<ThrowingType::MoveCase>("moveCase"); + QTest::addColumn<ThrowingType::ThrowCase>("throwCase"); + // Not throwing + QTest::newRow("no-throw-move-right-no-overlap") + << ThrowingType::MoveRightNoOverlap << ThrowingType::NoThrow; + QTest::newRow("no-throw-move-right-overlap") + << ThrowingType::MoveRightOverlap << ThrowingType::NoThrow; + QTest::newRow("no-throw-move-left-no-overlap") + << ThrowingType::MoveLeftNoOverlap << ThrowingType::NoThrow; + QTest::newRow("no-throw-move-left-overlap") + << ThrowingType::MoveLeftOverlap << ThrowingType::NoThrow; + // Throwing in uninitialized region + QTest::newRow("throw-in-uninit-region-move-right-no-overlap") + << ThrowingType::MoveRightNoOverlap << ThrowingType::ThrowInUninitializedRegion; + QTest::newRow("throw-in-uninit-region-move-right-overlap") + << ThrowingType::MoveRightOverlap << ThrowingType::ThrowInUninitializedRegion; + QTest::newRow("throw-in-uninit-region-move-left-no-overlap") + << ThrowingType::MoveLeftNoOverlap << ThrowingType::ThrowInUninitializedRegion; + QTest::newRow("throw-in-uninit-region-move-left-overlap") + << ThrowingType::MoveLeftOverlap << ThrowingType::ThrowInUninitializedRegion; + // Throwing in overlap region + QTest::newRow("throw-in-overlap-region-move-right-overlap") + << ThrowingType::MoveRightOverlap << ThrowingType::ThrowInOverlapRegion; + QTest::newRow("throw-in-overlap-region-move-left-overlap") + << ThrowingType::MoveLeftOverlap << ThrowingType::ThrowInOverlapRegion; +} + +void tst_QArrayData::relocateWithExceptions() +{ + // Assume that non-throwing moves perform correctly. Otherwise, all previous + // tests would've failed. Test only what happens when exceptions are thrown. + QFETCH(ThrowingType::MoveCase, moveCase); + QFETCH(ThrowingType::ThrowCase, throwCase); + + struct ThrowingTypeLeakChecker + { + ThrowingType::MoveCase moveCase; + ThrowingType::ThrowCase throwCase; + size_t containerSize = 0; + + ThrowingTypeLeakChecker(ThrowingType::MoveCase mc, ThrowingType::ThrowCase tc) + : moveCase(mc), throwCase(tc) + { + } + + void start(qsizetype size) + { + containerSize = size_t(size); + throwingTypeWatcher().watch = true; + } + + ~ThrowingTypeLeakChecker() + { + const size_t destroyedElementsCount = throwingTypeWatcher().destroyedAddrs.size(); + const size_t destroyedElementsUniqueCount = + std::set<void *>(throwingTypeWatcher().destroyedAddrs.begin(), + throwingTypeWatcher().destroyedAddrs.end()) + .size(); + + // reset the global watcher first and only then verify things + throwingTypeWatcher().watch = false; + throwingTypeWatcher().destroyedAddrs.clear(); + + size_t deletedByRelocate = 0; + switch (throwCase) { + case ThrowingType::NoThrow: + // if no overlap, N elements from old range. otherwise, N - 1 + // elements from old range + if (moveCase == ThrowingType::MoveLeftNoOverlap + || moveCase == ThrowingType::MoveRightNoOverlap) { + deletedByRelocate = containerSize; + } else { + deletedByRelocate = containerSize - 1; + } + break; + case ThrowingType::ThrowInUninitializedRegion: + // 1 relocated element from uninitialized region + deletedByRelocate = 1u; + break; + case ThrowingType::ThrowInOverlapRegion: + // 2 relocated elements from uninitialized region + deletedByRelocate = 2u; + break; + default: + QFAIL("Unknown throwCase"); + } + + QCOMPARE(destroyedElementsCount, deletedByRelocate + containerSize); + QCOMPARE(destroyedElementsUniqueCount, destroyedElementsCount); + } + }; + + const auto setDeferredThrow = [throwCase]() { + switch (throwCase) { + case ThrowingType::NoThrow: + break; // do nothing + case ThrowingType::ThrowInUninitializedRegion: + ThrowingType::throwOnce = 2; + break; + case ThrowingType::ThrowInOverlapRegion: + ThrowingType::throwOnce = 3; + break; + default: + QFAIL("Unknown throwCase"); + } + }; + + const auto createDataPointer = [](qsizetype capacity, qsizetype initSize) { + QArrayDataPointer<ThrowingType> qadp(capacity); + qadp->appendInitialize(initSize); + int i = 0; + std::generate(qadp.begin(), qadp.end(), [&i]() { return ThrowingType(i++); }); + return qadp; + }; + + switch (moveCase) { + case ThrowingType::MoveRightNoOverlap: { + ThrowingTypeLeakChecker watch(moveCase, throwCase); + auto storage = createDataPointer(20, 3); + QVERIFY(storage.freeSpaceAtEnd() > 3); + + watch.start(storage.size); + try { + setDeferredThrow(); + storage->relocate(4); + if (throwCase != ThrowingType::NoThrow) + QFAIL("Unreachable line!"); + } catch (const std::runtime_error &e) { + QCOMPARE(std::string(e.what()), ThrowingType::throwString); + } + break; + } + case ThrowingType::MoveRightOverlap: { + ThrowingTypeLeakChecker watch(moveCase, throwCase); + auto storage = createDataPointer(20, 3); + QVERIFY(storage.freeSpaceAtEnd() > 3); + + watch.start(storage.size); + try { + setDeferredThrow(); + storage->relocate(2); + if (throwCase != ThrowingType::NoThrow) + QFAIL("Unreachable line!"); + } catch (const std::runtime_error &e) { + QCOMPARE(std::string(e.what()), ThrowingType::throwString); + } + break; + } + case ThrowingType::MoveLeftNoOverlap: { + ThrowingTypeLeakChecker watch(moveCase, throwCase); + auto storage = createDataPointer(20, 2); + storage->insert(0, 1, ThrowingType(42)); + QVERIFY(storage.freeSpaceAtBegin() > 3); + + watch.start(storage.size); + try { + setDeferredThrow(); + storage->relocate(-4); + if (throwCase != ThrowingType::NoThrow) + QFAIL("Unreachable line!"); + } catch (const std::runtime_error &e) { + QCOMPARE(std::string(e.what()), ThrowingType::throwString); + } + break; + } + case ThrowingType::MoveLeftOverlap: { + ThrowingTypeLeakChecker watch(moveCase, throwCase); + auto storage = createDataPointer(20, 2); + storage->insert(0, 1, ThrowingType(42)); + QVERIFY(storage.freeSpaceAtBegin() > 3); + + watch.start(storage.size); + try { + setDeferredThrow(); + storage->relocate(-2); + if (throwCase != ThrowingType::NoThrow) + QFAIL("Unreachable line!"); + } catch (const std::runtime_error &e) { + QCOMPARE(std::string(e.what()), ThrowingType::throwString); + } + break; + } + default: + QFAIL("Unknown ThrowingType::MoveCase"); + }; +} +#endif // QT_NO_EXCEPTIONS + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" diff --git a/tests/auto/corelib/tools/qatomicscopedvaluerollback/CMakeLists.txt b/tests/auto/corelib/tools/qatomicscopedvaluerollback/CMakeLists.txt new file mode 100644 index 0000000000..b20e56421f --- /dev/null +++ b/tests/auto/corelib/tools/qatomicscopedvaluerollback/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicscopedvaluerollback LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qatomicscopedvaluerollback + SOURCES + tst_qatomicscopedvaluerollback.cpp +) diff --git a/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp b/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp new file mode 100644 index 0000000000..89bd1d7ff6 --- /dev/null +++ b/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp @@ -0,0 +1,164 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore/qatomicscopedvaluerollback.h> + +#include <QTest> + +class tst_QAtomicScopedValueRollback : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void leavingScope(); + void leavingScopeAfterCommit(); + void rollbackToPreviousCommit(); + void exceptions(); + void earlyExitScope(); +private: + void earlyExitScope_helper(int exitpoint, std::atomic<int> &member); +}; + +void tst_QAtomicScopedValueRollback::leavingScope() +{ + QAtomicInt i = 0; + QBasicAtomicInteger<bool> b = false; + std::atomic<bool> b2 = false; + int x = 0, y = 42; + QBasicAtomicPointer<int> p = &x; + + //test rollback on going out of scope + { + QAtomicScopedValueRollback ri(i); + QAtomicScopedValueRollback rb(b); + QAtomicScopedValueRollback rb2(b2, true); + QAtomicScopedValueRollback rp(p); + QCOMPARE(b.loadRelaxed(), false); + QCOMPARE(b2, true); + QCOMPARE(i.loadRelaxed(), 0); + QCOMPARE(p.loadRelaxed(), &x); + b.storeRelaxed(true); + i.storeRelaxed(1); + p.storeRelaxed(&y); + QCOMPARE(b.loadRelaxed(), true); + QCOMPARE(i.loadRelaxed(), 1); + QCOMPARE(p.loadRelaxed(), &y); + } + QCOMPARE(b.loadRelaxed(), false); + QCOMPARE(b2, false); + QCOMPARE(i.loadRelaxed(), 0); + QCOMPARE(p.loadRelaxed(), &x); +} + +void tst_QAtomicScopedValueRollback::leavingScopeAfterCommit() +{ + std::atomic<int> i = 0; + QAtomicInteger<bool> b = false; + + //test rollback on going out of scope + { + QAtomicScopedValueRollback ri(i); + QAtomicScopedValueRollback rb(b); + QCOMPARE(b.loadRelaxed(), false); + QCOMPARE(i, 0); + b.storeRelaxed(true); + i = 1; + QCOMPARE(b.loadRelaxed(), true); + QCOMPARE(i, 1); + ri.commit(); + rb.commit(); + } + QCOMPARE(b.loadRelaxed(), true); + QCOMPARE(i, 1); +} + +void tst_QAtomicScopedValueRollback::rollbackToPreviousCommit() +{ + QBasicAtomicInt i = 0; + { + QAtomicScopedValueRollback ri(i); + i++; + ri.commit(); + i++; + } + QCOMPARE(i.loadRelaxed(), 1); + { + QAtomicScopedValueRollback ri1(i); + i++; + ri1.commit(); + i++; + ri1.commit(); + i++; + } + QCOMPARE(i.loadRelaxed(), 3); +} + +void tst_QAtomicScopedValueRollback::exceptions() +{ + std::atomic<bool> b = false; + bool caught = false; + QT_TRY + { + QAtomicScopedValueRollback rb(b); + b = true; + QT_THROW(std::bad_alloc()); //if Qt compiled without exceptions this is noop + rb.commit(); //if Qt compiled without exceptions, true is committed + } + QT_CATCH(...) + { + caught = true; + } + QCOMPARE(b, !caught); //expect false if exception was thrown, true otherwise +} + +void tst_QAtomicScopedValueRollback::earlyExitScope() +{ + QAtomicInt ai = 0; + std::atomic<int> aj = 0; + while (true) { + QAtomicScopedValueRollback ri(ai); + ++ai; + aj = ai.loadRelaxed(); + if (ai.loadRelaxed() > 8) break; + ri.commit(); + } + QCOMPARE(ai.loadRelaxed(), 8); + QCOMPARE(aj.load(), 9); + + for (int i = 0; i < 5; ++i) { + aj = 1; + earlyExitScope_helper(i, aj); + QCOMPARE(aj.load(), 1 << i); + } +} + +static void operator*=(std::atomic<int> &lhs, int rhs) +{ + int expected = lhs.load(); + while (!lhs.compare_exchange_weak(expected, expected * rhs)) + ; +} + +void tst_QAtomicScopedValueRollback::earlyExitScope_helper(int exitpoint, std::atomic<int>& member) +{ + QAtomicScopedValueRollback r(member); + member *= 2; + if (exitpoint == 0) + return; + r.commit(); + member *= 2; + if (exitpoint == 1) + return; + r.commit(); + member *= 2; + if (exitpoint == 2) + return; + r.commit(); + member *= 2; + if (exitpoint == 3) + return; + r.commit(); +} + +QTEST_MAIN(tst_QAtomicScopedValueRollback) +#include "tst_qatomicscopedvaluerollback.moc" diff --git a/tests/auto/corelib/tools/qbitarray/CMakeLists.txt b/tests/auto/corelib/tools/qbitarray/CMakeLists.txt new file mode 100644 index 0000000000..ac3bd24bd5 --- /dev/null +++ b/tests/auto/corelib/tools/qbitarray/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qbitarray Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qbitarray LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qbitarray + SOURCES + tst_qbitarray.cpp + LIBRARIES + Qt::TestPrivate +) diff --git a/tests/auto/corelib/tools/qbitarray/qbitarray.pro b/tests/auto/corelib/tools/qbitarray/qbitarray.pro deleted file mode 100644 index 1e7185b600..0000000000 --- a/tests/auto/corelib/tools/qbitarray/qbitarray.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qbitarray -QT = core testlib -SOURCES = tst_qbitarray.cpp diff --git a/tests/auto/corelib/tools/qbitarray/tst_qbitarray.cpp b/tests/auto/corelib/tools/qbitarray/tst_qbitarray.cpp index 9a7c099228..f52a368aa9 100644 --- a/tests/auto/corelib/tools/qbitarray/tst_qbitarray.cpp +++ b/tests/auto/corelib/tools/qbitarray/tst_qbitarray.cpp @@ -1,47 +1,26 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> #include <QtCore/QBuffer> #include <QtCore/QDataStream> #include "qbitarray.h" +#include <QtCore/qelapsedtimer.h> +#include <QtCore/qscopeguard.h> + /** * Helper function to initialize a bitarray from a string */ static QBitArray QStringToQBitArray(const QString &str) { QBitArray ba; - ba.resize(str.length()); + ba.resize(str.size()); int i; QChar tru('1'); - for (i = 0; i < str.length(); i++) + for (i = 0; i < str.size(); i++) { if (str.at(i) == tru) { @@ -51,10 +30,18 @@ static QBitArray QStringToQBitArray(const QString &str) return ba; } +static QBitArray detached(QBitArray a) +{ + a.detach(); + return a; +} + class tst_QBitArray : public QObject { Q_OBJECT private slots: + void compareCompiles(); + void canHandleIntMaxBits(); void size_data(); void size(); void countBits_data(); @@ -68,12 +55,21 @@ private slots: // operator &= void operator_andeq_data(); void operator_andeq(); + // operator & + void operator_and_data() { operator_andeq_data(); } + void operator_and(); // operator |= void operator_oreq_data(); void operator_oreq(); + // operator | + void operator_or_data() { operator_oreq_data(); } + void operator_or(); // operator ^= void operator_xoreq_data(); void operator_xoreq(); + // operator ^ + void operator_xor_data() { operator_xoreq_data(); } + void operator_xor(); // operator ~ void operator_neg_data(); void operator_neg(); @@ -86,8 +82,64 @@ private slots: void resize(); void fromBits_data(); void fromBits(); + + void toUInt32_data(); + void toUInt32(); }; +void tst_QBitArray::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QBitArray>(); +} + +void tst_QBitArray::canHandleIntMaxBits() +{ + QElapsedTimer timer; + timer.start(); + const auto print = qScopeGuard([&] { + qDebug("Function took %lldms", qlonglong(timer.elapsed())); + }); + + try { + constexpr qsizetype Size1 = sizeof(void*) > sizeof(int) ? qsizetype(INT_MAX) + 2 : + INT_MAX - 2; + constexpr qsizetype Size2 = Size1 + 2; + + QBitArray ba(Size1, true); + QCOMPARE(ba.size(), Size1); + QCOMPARE(ba.at(Size1 - 1), true); + + ba.resize(Size2); + QCOMPARE(ba.size(), Size2); + QCOMPARE(ba.at(Size1 - 1), true); + QCOMPARE(ba.at(Size1), false); + QCOMPARE(ba.at(Size2 - 1), false); + + QByteArray serialized; + if constexpr (sizeof(void*) > sizeof(int)) { + QDataStream ds(&serialized, QIODevice::WriteOnly); + ds.setVersion(QDataStream::Qt_5_15); + ds << ba; + QCOMPARE(ds.status(), QDataStream::Status::SizeLimitExceeded); + serialized.clear(); + } + { + QDataStream ds(&serialized, QIODevice::WriteOnly); + ds << ba; + QCOMPARE(ds.status(), QDataStream::Status::Ok); + } + { + QDataStream ds(serialized); + QBitArray ba2; + ds >> ba2; + QCOMPARE(ds.status(), QDataStream::Status::Ok); + QT_TEST_EQUALITY_OPS(ba, ba2, true); + } + } catch (const std::bad_alloc &) { + QSKIP("Failed to allocate sufficient memory"); + } +} + void tst_QBitArray::size_data() { //create the testtable instance and define the elements @@ -147,7 +199,6 @@ void tst_QBitArray::countBits_data() QTest::newRow("11111111111111111111111111111111") << QString("11111111111111111111111111111111") << 32 << 32; QTest::newRow("11111111111111111111111111111111111111111111111111111111") << QString("11111111111111111111111111111111111111111111111111111111") << 56 << 56; - QTest::newRow("00000000000000000000000000000000000") << QString("00000000000000000000000000000000000") << 35 << 0; QTest::newRow("00000000000000000000000000000000") << QString("00000000000000000000000000000000") << 32 << 0; QTest::newRow("00000000000000000000000000000000000000000000000000000000") << QString("00000000000000000000000000000000000000000000000000000000") << 56 << 0; @@ -165,6 +216,8 @@ void tst_QBitArray::countBits() bits.setBit(i); } + QCOMPARE(bits.size(), numBits); + // NOLINTNEXTLINE(qt-port-to-std-compatible-api): We want to test count() and size() QCOMPARE(bits.count(), numBits); QCOMPARE(bits.count(true), onBits); QCOMPARE(bits.count(false), numBits - onBits); @@ -220,14 +273,20 @@ void tst_QBitArray::isEmpty() QVERIFY(!a1.isEmpty()); QVERIFY(!a1.isNull()); QVERIFY(a1.size() == 2); + + QT_TEST_EQUALITY_OPS(a1, a2, false); + QT_TEST_EQUALITY_OPS(a2, a3, false); + QT_TEST_EQUALITY_OPS(QBitArray(), QBitArray(), true); + a3 = a2; + QT_TEST_EQUALITY_OPS(a2, a3, true); } void tst_QBitArray::swap() { QBitArray b1 = QStringToQBitArray("1"), b2 = QStringToQBitArray("10"); b1.swap(b2); - QCOMPARE(b1,QStringToQBitArray("10")); - QCOMPARE(b2,QStringToQBitArray("1")); + QT_TEST_EQUALITY_OPS(b1,QStringToQBitArray("10"), true); + QT_TEST_EQUALITY_OPS(b2,QStringToQBitArray("1"), true); } void tst_QBitArray::fill() @@ -277,7 +336,7 @@ void tst_QBitArray::toggleBit() input.toggleBit(index); - QCOMPARE(input, res); + QT_TEST_EQUALITY_OPS(input, res, true); } void tst_QBitArray::operator_andeq_data() @@ -322,9 +381,64 @@ void tst_QBitArray::operator_andeq() QFETCH(QBitArray, input2); QFETCH(QBitArray, res); - input1&=input2; + QBitArray result = input1; + result &= input2; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1; + result &= std::move(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1; + result &= detached(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + + // operation is commutative + result = input2; + result &= input1; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2; + result &= std::move(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2; + result &= detached(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + + // operation is idempotent + result &= result; + QT_TEST_EQUALITY_OPS(result, res, true); + result &= std::move(result); + QT_TEST_EQUALITY_OPS(result, res, true); + result &= detached(result); + QT_TEST_EQUALITY_OPS(result, res, true); +} - QCOMPARE(input1, res); +void tst_QBitArray::operator_and() +{ + QFETCH(QBitArray, input1); + QFETCH(QBitArray, input2); + QFETCH(QBitArray, res); + + QBitArray result = input1 & input2; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1 & QBitArray(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1 & detached(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + + // operation is commutative + result = input2 & input1; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2 & QBitArray(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2 & detached(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + + // operation is idempotent + result = result & result; + QT_TEST_EQUALITY_OPS(result, res, true); + result = result & QBitArray(result); + QT_TEST_EQUALITY_OPS(result, res, true); + result = result & detached(result); + QT_TEST_EQUALITY_OPS(result, res, true); } void tst_QBitArray::operator_oreq_data() @@ -373,9 +487,64 @@ void tst_QBitArray::operator_oreq() QFETCH(QBitArray, input2); QFETCH(QBitArray, res); - input1|=input2; + QBitArray result = input1; + result |= input2; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1; + result |= QBitArray(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1; + result |= detached(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + + // operation is commutative + result = input2; + result |= input1; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2; + result |= QBitArray(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2; + result |= detached(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + + // operation is idempotent + result |= result; + QT_TEST_EQUALITY_OPS(result, res, true); + result |= QBitArray(result); + QT_TEST_EQUALITY_OPS(result, res, true); + result |= detached(result); + QT_TEST_EQUALITY_OPS(result, res, true); +} + +void tst_QBitArray::operator_or() +{ + QFETCH(QBitArray, input1); + QFETCH(QBitArray, input2); + QFETCH(QBitArray, res); - QCOMPARE(input1, res); + QBitArray result = input1 | input2; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1 | QBitArray(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1 | detached(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + + // operation is commutative + result = input2 | input1; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2 | QBitArray(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2 | detached(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + + // operation is idempotent + result = result | result; + QT_TEST_EQUALITY_OPS(result, res, true); + result = result | QBitArray(result); + QT_TEST_EQUALITY_OPS(result, res, true); + result = result | detached(result); + QT_TEST_EQUALITY_OPS(result, res, true); } void tst_QBitArray::operator_xoreq_data() @@ -422,11 +591,102 @@ void tst_QBitArray::operator_xoreq() QFETCH(QBitArray, input2); QFETCH(QBitArray, res); - input1^=input2; - - QCOMPARE(input1, res); + QBitArray result = input1; + result ^= input2; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1; + result ^= QBitArray(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1; + result ^= detached(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + + // operation is commutative + result = input2; + result ^= input1; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2; + result ^= QBitArray(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2; + result ^= detached(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + + // XORing with oneself is nilpotent + result = input1; + result ^= input1; + QT_TEST_EQUALITY_OPS(result, QBitArray(input1.size()), true); + result = input1; + result ^= QBitArray(result); + QT_TEST_EQUALITY_OPS(result, QBitArray(input1.size()), true); + result = input1; + result ^= detached(result); + QT_TEST_EQUALITY_OPS(result, QBitArray(input1.size()), true); + + result = input2; + result ^= input2; + QT_TEST_EQUALITY_OPS(result, QBitArray(input2.size()), true); + result = input2; + result ^= QBitArray(input2); + QT_TEST_EQUALITY_OPS(result, QBitArray(input2.size()), true); + result = input2; + result ^= detached(input2); + QT_TEST_EQUALITY_OPS(result, QBitArray(input2.size()), true); + + result = res; + result ^= res; + QT_TEST_EQUALITY_OPS(result, QBitArray(res.size()), true); + result = res; + result ^= QBitArray(res); + QT_TEST_EQUALITY_OPS(result, QBitArray(res.size()), true); + result = res; + result ^= detached(res); + QT_TEST_EQUALITY_OPS(result, QBitArray(res.size()), true); } +void tst_QBitArray::operator_xor() +{ + QFETCH(QBitArray, input1); + QFETCH(QBitArray, input2); + QFETCH(QBitArray, res); + + QBitArray result = input1 ^ input2; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1 ^ QBitArray(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input1 ^ detached(input2); + QT_TEST_EQUALITY_OPS(result, res, true); + + // operation is commutative + result = input2 ^ input1; + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2 ^ QBitArray(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + result = input2 ^ detached(input1); + QT_TEST_EQUALITY_OPS(result, res, true); + + // XORing with oneself is nilpotent + result = input1 ^ input1; + QT_TEST_EQUALITY_OPS(result, QBitArray(input1.size()), true); + result = input1 ^ QBitArray(input1); + QT_TEST_EQUALITY_OPS(result, QBitArray(input1.size()), true); + result = input1 ^ detached(input1); + QT_TEST_EQUALITY_OPS(result, QBitArray(input1.size()), true); + + result = input2 ^ input2; + QT_TEST_EQUALITY_OPS(result, QBitArray(input2.size()), true); + result = input2 ^ QBitArray(input2); + QT_TEST_EQUALITY_OPS(result, QBitArray(input2.size()), true); + result = input2 ^ detached(input2); + QT_TEST_EQUALITY_OPS(result, QBitArray(input2.size()), true); + + result = res ^ res; + QT_TEST_EQUALITY_OPS(result, QBitArray(res.size()), true); + result = res ^ QBitArray(res); + QT_TEST_EQUALITY_OPS(result, QBitArray(res.size()), true); + result = res ^ detached(res); + QT_TEST_EQUALITY_OPS(result, QBitArray(res.size()), true); +} void tst_QBitArray::operator_neg_data() { @@ -474,7 +734,8 @@ void tst_QBitArray::operator_neg() input = ~input; - QCOMPARE(input, res); + QT_TEST_EQUALITY_OPS(input, res, true); + QT_TEST_EQUALITY_OPS(~~input, res, true); // performs two in-place negations } void tst_QBitArray::datastream_data() @@ -494,7 +755,6 @@ void tst_QBitArray::datastream_data() QTest::newRow("11111111111111111111111111111111") << QString("11111111111111111111111111111111") << 32 << 32; QTest::newRow("11111111111111111111111111111111111111111111111111111111") << QString("11111111111111111111111111111111111111111111111111111111") << 56 << 56; - QTest::newRow("00000000000000000000000000000000000") << QString("00000000000000000000000000000000000") << 35 << 0; QTest::newRow("00000000000000000000000000000000") << QString("00000000000000000000000000000000") << 32 << 0; QTest::newRow("00000000000000000000000000000000000000000000000000000000") << QString("00000000000000000000000000000000000000000000000000000000") << 56 << 0; @@ -516,7 +776,7 @@ void tst_QBitArray::datastream() bits.setBit(i); } - QCOMPARE(bits.count(), numBits); + QCOMPARE(bits.size(), numBits); QCOMPARE(bits.count(true), onBits); QCOMPARE(bits.count(false), numBits - onBits); @@ -531,19 +791,19 @@ void tst_QBitArray::datastream() QBitArray array1, array2, array3; stream2 >> array1 >> array2 >> array3; - QCOMPARE(array1.count(), numBits); + QCOMPARE(array1.size(), numBits); QCOMPARE(array1.count(true), onBits); QCOMPARE(array1.count(false), numBits - onBits); - QCOMPARE(array1, bits); - QCOMPARE(array2, bits); - QCOMPARE(array3, bits); + QT_TEST_EQUALITY_OPS(array1, bits, true); + QT_TEST_EQUALITY_OPS(array2, bits, true); + QT_TEST_EQUALITY_OPS(array3, bits, true); } void tst_QBitArray::invertOnNull() const { QBitArray a; - QCOMPARE(a = ~a, QBitArray()); + QT_TEST_EQUALITY_OPS(a = ~a, QBitArray(), true); } void tst_QBitArray::operator_noteq_data() @@ -584,7 +844,7 @@ void tst_QBitArray::operator_noteq() QFETCH(bool, res); bool b = input1 != input2; - QCOMPARE(b, res); + QT_TEST_EQUALITY_OPS(b, res, true); } void tst_QBitArray::resize() @@ -593,22 +853,22 @@ void tst_QBitArray::resize() QBitArray a = QStringToQBitArray(QString("11")); a.resize(10); QVERIFY(a.size() == 10); - QCOMPARE( a, QStringToQBitArray(QString("1100000000")) ); + QT_TEST_EQUALITY_OPS( a, QStringToQBitArray(QString("1100000000")), true); a.setBit(9); a.resize(9); // now the bit in a should have been gone: - QCOMPARE( a, QStringToQBitArray(QString("110000000")) ); + QT_TEST_EQUALITY_OPS( a, QStringToQBitArray(QString("110000000")), true); // grow the array back and check the new bit a.resize(10); - QCOMPARE( a, QStringToQBitArray(QString("1100000000")) ); + QT_TEST_EQUALITY_OPS( a, QStringToQBitArray(QString("1100000000")), true); // other test with and a.resize(9); QBitArray b = QStringToQBitArray(QString("1111111111")); b &= a; - QCOMPARE( b, QStringToQBitArray(QString("1100000000")) ); + QT_TEST_EQUALITY_OPS( b, QStringToQBitArray(QString("1100000000")), true); } @@ -662,9 +922,99 @@ void tst_QBitArray::fromBits() QFETCH(QBitArray, expected); QBitArray fromBits = QBitArray::fromBits(data, size); - QCOMPARE(fromBits, expected); + QT_TEST_EQUALITY_OPS(fromBits, expected, true); - QCOMPARE(QBitArray::fromBits(fromBits.bits(), fromBits.size()), expected); + QT_TEST_EQUALITY_OPS(QBitArray::fromBits(fromBits.bits(), fromBits.size()), expected, true); +} + +void tst_QBitArray::toUInt32_data() +{ + QTest::addColumn<QBitArray>("data"); + QTest::addColumn<int>("endianness"); + QTest::addColumn<bool>("check"); + QTest::addColumn<quint32>("result"); + + QTest::newRow("ctor") << QBitArray() + << static_cast<int>(QSysInfo::Endian::LittleEndian) + << true + << quint32(0); + + QTest::newRow("empty") << QBitArray(0) + << static_cast<int>(QSysInfo::Endian::LittleEndian) + << true + << quint32(0); + + QTest::newRow("LittleEndian4") << QStringToQBitArray(QString("0111")) + << static_cast<int>(QSysInfo::Endian::LittleEndian) + << true + << quint32(14); + + QTest::newRow("BigEndian4") << QStringToQBitArray(QString("0111")) + << static_cast<int>(QSysInfo::Endian::BigEndian) + << true + << quint32(7); + + QTest::newRow("LittleEndian8") << QStringToQBitArray(QString("01111111")) + << static_cast<int>(QSysInfo::Endian::LittleEndian) + << true + << quint32(254); + + QTest::newRow("BigEndian8") << QStringToQBitArray(QString("01111111")) + << static_cast<int>(QSysInfo::Endian::BigEndian) + << true + << quint32(127); + + QTest::newRow("LittleEndian16") << QStringToQBitArray(QString("0111111111111111")) + << static_cast<int>(QSysInfo::Endian::LittleEndian) + << true + << quint32(65534); + + QTest::newRow("BigEndian16") << QStringToQBitArray(QString("0111111111111111")) + << static_cast<int>(QSysInfo::Endian::BigEndian) + << true + << quint32(32767); + + QTest::newRow("LittleEndian31") << QBitArray(31, true) + << static_cast<int>(QSysInfo::Endian::LittleEndian) + << true + << quint32(2147483647); + + QTest::newRow("BigEndian31") << QBitArray(31, true) + << static_cast<int>(QSysInfo::Endian::BigEndian) + << true + << quint32(2147483647); + + QTest::newRow("LittleEndian32") << QBitArray(32, true) + << static_cast<int>(QSysInfo::Endian::LittleEndian) + << true + << quint32(4294967295); + + QTest::newRow("BigEndian32") << QBitArray(32, true) + << static_cast<int>(QSysInfo::Endian::BigEndian) + << true + << quint32(4294967295); + + QTest::newRow("LittleEndian33") << QBitArray(33, true) + << static_cast<int>(QSysInfo::Endian::LittleEndian) + << false + << quint32(0); + + QTest::newRow("BigEndian33") << QBitArray(33, true) + << static_cast<int>(QSysInfo::Endian::BigEndian) + << false + << quint32(0); +} + +void tst_QBitArray::toUInt32() +{ + QFETCH(QBitArray, data); + QFETCH(int, endianness); + QFETCH(bool, check); + QFETCH(quint32, result); + bool ok = false; + + QCOMPARE(data.toUInt32(static_cast<QSysInfo::Endian>(endianness), &ok), result); + QCOMPARE(ok, check); } QTEST_APPLESS_MAIN(tst_QBitArray) diff --git a/tests/auto/corelib/tools/qcache/CMakeLists.txt b/tests/auto/corelib/tools/qcache/CMakeLists.txt new file mode 100644 index 0000000000..8ffe942d70 --- /dev/null +++ b/tests/auto/corelib/tools/qcache/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qcache Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcache LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qcache + SOURCES + tst_qcache.cpp +) diff --git a/tests/auto/corelib/tools/qcache/qcache.pro b/tests/auto/corelib/tools/qcache/qcache.pro deleted file mode 100644 index 07488ef40f..0000000000 --- a/tests/auto/corelib/tools/qcache/qcache.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qcache -QT = core testlib -SOURCES = tst_qcache.cpp diff --git a/tests/auto/corelib/tools/qcache/tst_qcache.cpp b/tests/auto/corelib/tools/qcache/tst_qcache.cpp index d880953c1c..5fccb8f1d0 100644 --- a/tests/auto/corelib/tools/qcache/tst_qcache.cpp +++ b/tests/auto/corelib/tools/qcache/tst_qcache.cpp @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <qcache.h> @@ -37,6 +12,7 @@ public slots: void initTestCase(); void cleanupTestCase(); private slots: + void empty(); void maxCost(); void setMaxCost(); void totalCost(); @@ -47,6 +23,10 @@ private slots: void remove(); void take(); void axioms_on_key_type(); + void largeCache(); + void internalChainOrderAfterEntryUpdate(); + void emplaceLowerCost(); + void trimWithMovingAcrossSpans(); }; @@ -72,6 +52,21 @@ void tst_QCache::cleanupTestCase() QCOMPARE(Foo::count, 0); } +void tst_QCache::empty() +{ + QCache<int, int> cache; + QCOMPARE(cache.size(), 0); + QCOMPARE(cache.count(), 0); + QVERIFY(cache.isEmpty()); + QVERIFY(!cache.contains(1)); + QCOMPARE(cache.keys().size(), 0); + QCOMPARE(cache.take(1), nullptr); + QVERIFY(!cache.remove(1)); + QCOMPARE(cache.object(1), nullptr); + QCOMPARE(cache[1], nullptr); + QCOMPARE(cache.totalCost(), 0); +} + void tst_QCache::maxCost() { QCache<QString, int> cache1, cache2(100), cache3(200), cache4(-50); @@ -356,6 +351,7 @@ struct KeyType int foo; KeyType(int x) : foo(x) {} + constexpr KeyType(const KeyType &o) noexcept : foo(o.foo) {} private: KeyType &operator=(const KeyType &); @@ -377,7 +373,7 @@ bool operator==(const KeyType &key1, const KeyType &key2) return key1.foo == key2.foo; } -uint qHash(const KeyType &key) +size_t qHash(const KeyType &key) { return qHash(key.foo); } @@ -398,5 +394,115 @@ void tst_QCache::axioms_on_key_type() QVERIFY(sizeof(QHash<int, int>) == sizeof(void *)); } +void tst_QCache::largeCache() +{ + QCache<int, int> cache; + cache.setMaxCost(500); + for (int i = 0; i < 1000; ++i) { + for (int j = 0; j < qMax(0, i - 500); ++j) + QVERIFY(!cache.contains(j)); + for (int j = qMax(0, i - 500); j < i; ++j) + QVERIFY(cache.contains(j)); + cache.insert(i, new int); + } + cache.clear(); + QVERIFY(cache.size() == 0); +} + +// The internal chain could lose track of some objects. +// Make sure it doesn't happen again. +void tst_QCache::internalChainOrderAfterEntryUpdate() +{ + QCache<QString, int> cache; + cache.setMaxCost(20); + cache.insert(QString::number(1), new int, 1); + cache.insert(QString::number(2), new int, 1); + cache.insert(QString::number(1), new int, 1); + // If the chain is still 'in order' then setting maxCost == 0 should + // a. not crash, and + // b. remove all the elements in the QHash + cache.setMaxCost(0); + QCOMPARE(cache.size(), 0); +} + +void tst_QCache::emplaceLowerCost() +{ + QCache<QString, int> cache; + cache.setMaxCost(5); + cache.insert("a", new int, 3); // insert high cost + cache.insert("a", new int, 1); // and then exchange it with a lower-cost object + QCOMPARE(cache.totalCost(), 1); + cache.remove("a"); // then remove the object + // The cache should now have a cost == 0 and be empty. + QCOMPARE(cache.totalCost(), 0); + QVERIFY(cache.isEmpty()); +} + +struct TrivialHashType { + int i = -1; + size_t hash = 0; + + TrivialHashType(int i, size_t hash) : i(i), hash(hash) {} + TrivialHashType(const TrivialHashType &o) noexcept = default; + TrivialHashType &operator=(const TrivialHashType &o) noexcept = default; + TrivialHashType(TrivialHashType &&o) noexcept : i(o.i), hash(o.hash) { + o.i = -1; + o.hash = 0; + } + TrivialHashType &operator=(TrivialHashType &&o) noexcept { + i = o.i; + hash = o.hash; + o.i = -1; + o.hash = 0; + return *this; + } + + + friend bool operator==(const TrivialHashType &lhs, const TrivialHashType &rhs) + { + return lhs.i == rhs.i; + } +}; +quint64 qHash(TrivialHashType t, size_t seed = 0) +{ + Q_UNUSED(seed); + return t.hash; +} + +// During trim(), if the Node we have a pointer to in the function is moved +// to another span in the hash table, our pointer would end up pointing to +// garbage memory. Test that this no longer happens +void tst_QCache::trimWithMovingAcrossSpans() +{ + qsizetype numBuckets = [](){ + QHash<int, int> h; + h.reserve(1); + // Beholden to QHash internals: + return h.capacity() << 1; + }(); + + QCache<TrivialHashType, int> cache; + cache.setMaxCost(1000); + + auto lastBucketInSpan = size_t(numBuckets - 1); + // If this fails then the test is no longer valid + QCOMPARE(QHashPrivate::GrowthPolicy::bucketForHash(numBuckets, lastBucketInSpan), + lastBucketInSpan); + + // Pad some space so we have two spans: + for (int i = 2; i < numBuckets; ++i) + cache.insert({i, 0}, nullptr); + + // These two are vying for the last bucket in the first span, + // when '0' is deleted, '1' is moved across the span boundary, + // invalidating any pointer to its Node. + cache.insert({0, lastBucketInSpan}, nullptr); + cache.insert({1, lastBucketInSpan}, nullptr); + + QCOMPARE(cache.size(), numBuckets); + cache.setMaxCost(0); + QCOMPARE(cache.size(), 0); +} + QTEST_APPLESS_MAIN(tst_QCache) #include "tst_qcache.moc" diff --git a/tests/auto/corelib/tools/qcommandlineparser/CMakeLists.txt b/tests/auto/corelib/tools/qcommandlineparser/CMakeLists.txt new file mode 100644 index 0000000000..5aa8bd2500 --- /dev/null +++ b/tests/auto/corelib/tools/qcommandlineparser/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qcommandlineparser Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcommandlineparser LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qcommandlineparser + SOURCES + tst_qcommandlineparser.cpp +) +add_subdirectory(testhelper) +if(QT_FEATURE_process AND NOT ANDROID) + add_dependencies(tst_qcommandlineparser qcommandlineparser_test_helper) +endif() diff --git a/tests/auto/corelib/tools/qcommandlineparser/qcommandlineparser.pro b/tests/auto/corelib/tools/qcommandlineparser/qcommandlineparser.pro deleted file mode 100644 index a9aedc4c0d..0000000000 --- a/tests/auto/corelib/tools/qcommandlineparser/qcommandlineparser.pro +++ /dev/null @@ -1,3 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS += tst_qcommandlineparser.pro testhelper/qcommandlineparser_test_helper.pro diff --git a/tests/auto/corelib/tools/qcommandlineparser/testhelper/CMakeLists.txt b/tests/auto/corelib/tools/qcommandlineparser/testhelper/CMakeLists.txt new file mode 100644 index 0000000000..20cec30a9c --- /dev/null +++ b/tests/auto/corelib/tools/qcommandlineparser/testhelper/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## qcommandlineparser_test_helper Binary: +##################################################################### + +qt_internal_add_executable(qcommandlineparser_test_helper + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + SOURCES + qcommandlineparser_test_helper.cpp +) diff --git a/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp index 513c811788..b5f178a3d1 100644 --- a/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp +++ b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2013 David Faure <faure@kde.org> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2013 David Faure <faure@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QDebug> #include <QCoreApplication> @@ -74,12 +49,6 @@ int main(int argc, char *argv[]) hiddenOption.setDescription(QStringLiteral("THIS SHOULD NEVER APPEAR")); hiddenOption.setFlags(QCommandLineOption::HiddenFromHelp); parser.addOption(hiddenOption); -#if QT_DEPRECATED_SINCE(5, 8) - QCommandLineOption hiddenOption2(QStringList() << QStringLiteral("hidden2")); - hiddenOption2.setDescription(QStringLiteral("NEITHER SHOULD THIS")); - hiddenOption2.setHidden(true); - parser.addOption(hiddenOption2); -#endif // This program supports different options depending on the "command" (first argument). // Call parse() to find out the positional arguments. @@ -97,6 +66,13 @@ int main(int argc, char *argv[]) parser.process(app); const QString size = parser.value("size"); printf("Resizing %s to %s and saving to %s\n", qPrintable(parser.value("load")), qPrintable(size), qPrintable(parser.value("o"))); + } else if (command == "long") { + // A very long option (QTBUG-79926) + QCommandLineOption longOption(QStringList{QStringLiteral("looooooooooooong-option"), QStringLiteral("looooong-opt-alias")}); + longOption.setDescription(QStringLiteral("Short description")); + longOption.setValueName(QStringLiteral("looooooooooooong-value-name")); + parser.addOption(longOption); + parser.process(app); } else { // Call process again, to handle unknown options this time. parser.process(app); diff --git a/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.pro b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.pro deleted file mode 100644 index 5020658835..0000000000 --- a/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += cmdline -QT = core -DESTDIR = ./ - -SOURCES += qcommandlineparser_test_helper.cpp diff --git a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp index 1e87c76d2f..812cf2d1b3 100644 --- a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp +++ b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp @@ -1,32 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2013 David Faure <faure@kde.org> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2021 David Faure <faure@kde.org> +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#if QT_CONFIG(process) +#include <QProcess> +#endif #include <QtCore/QCommandLineParser> Q_DECLARE_METATYPE(char**) @@ -63,6 +42,7 @@ private slots: void testDefaultValue(); void testProcessNotCalled(); void testEmptyArgsList(); + void testNoApplication(); void testMissingOptionValue(); void testStdinArgument_data(); void testStdinArgument(); @@ -78,6 +58,7 @@ private slots: void testUnknownOption(); void testHelpAll_data(); void testHelpAll(); + void testVeryLongOptionNames(); }; static char *empty_argv[] = { 0 }; @@ -145,6 +126,7 @@ void tst_QCommandLineParser::testBooleanOption() QVERIFY(parser.parse(args)); QCOMPARE(parser.optionNames(), expectedOptionNames); QCOMPARE(parser.isSet("b"), expectedIsSet); + QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: option not expecting values: \"b\""); QCOMPARE(parser.values("b"), QStringList()); QCOMPARE(parser.positionalArguments(), QStringList()); // Should warn on typos @@ -182,6 +164,7 @@ void tst_QCommandLineParser::testOptionsAndPositional() QVERIFY(parser.parse(args)); QCOMPARE(parser.optionNames(), expectedOptionNames); QCOMPARE(parser.isSet("b"), expectedIsSet); + QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: option not expecting values: \"b\""); QCOMPARE(parser.values("b"), QStringList()); QCOMPARE(parser.positionalArguments(), expectedPositionalArguments); } @@ -380,6 +363,7 @@ void tst_QCommandLineParser::testProcessNotCalled() QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: call process() or parse() before isSet"); QVERIFY(!parser.isSet("b")); QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: call process() or parse() before values"); + QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: option not expecting values: \"b\""); QCOMPARE(parser.values("b"), QStringList()); } @@ -391,6 +375,34 @@ void tst_QCommandLineParser::testEmptyArgsList() QVERIFY(!parser.parse(QStringList())); // invalid call, argv[0] is missing } +void tst_QCommandLineParser::testNoApplication() +{ + QCommandLineOption option(QStringLiteral("param"), QStringLiteral("Pass parameter to the backend.")); + option.setValueName("key=value"); + QCommandLineParser parser; + QVERIFY(parser.addOption(option)); + { + QVERIFY(parser.parse(QStringList() << "tst" << "--param" << "key1=value1")); + QVERIFY(parser.isSet("param")); + QCOMPARE(parser.values("param"), QStringList() << "key1=value1"); + QCOMPARE(parser.value("param"), QString("key1=value1")); + } + { + QVERIFY(parser.parse(QStringList() << "tst" << "--param" << "key1=value1" << "--param" << "key2=value2")); + QVERIFY(parser.isSet("param")); + QCOMPARE(parser.values("param"), QStringList() << "key1=value1" << "key2=value2"); + QCOMPARE(parser.value("param"), QString("key2=value2")); + } + + const QString expected = + "Usage: <executable_name> [options]\n" + "\n" + "Options:\n" + " --param <key=value> Pass parameter to the backend.\n"; + + QCOMPARE(parser.helpText(), expected); +} + void tst_QCommandLineParser::testMissingOptionValue() { QCoreApplication app(empty_argc, empty_argv); @@ -439,37 +451,40 @@ void tst_QCommandLineParser::testSingleDashWordOptionModes_data() QTest::addColumn<QStringList>("commandLine"); QTest::addColumn<QStringList>("expectedOptionNames"); QTest::addColumn<QStringList>("expectedOptionValues"); + QTest::addColumn<QStringList>("invalidOptionValues"); QTest::newRow("collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-abc" << "val") - << (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val"); + << (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val") + << (QStringList() << "a" << "b"); QTest::newRow("collapsed_with_equalsign_value") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-abc=val") - << (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val"); + << (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val") + << (QStringList() << "a" << "b"); QTest::newRow("collapsed_explicit_longoption") << QCommandLineParser::ParseAsCompactedShortOptions << QStringList("--nn") - << QStringList("nn") << QStringList(); + << QStringList("nn") << QStringList() << QStringList(); QTest::newRow("collapsed_longoption_value") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "--abc" << "val") - << QStringList("abc") << QStringList("val"); + << QStringList("abc") << QStringList("val") << QStringList(); QTest::newRow("compiler") << QCommandLineParser::ParseAsCompactedShortOptions << QStringList("-cab") - << QStringList("c") << QStringList("ab"); + << QStringList("c") << QStringList("ab") << QStringList(); QTest::newRow("compiler_with_space") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-c" << "val") - << QStringList("c") << QStringList("val"); + << QStringList("c") << QStringList("val") << QStringList(); QTest::newRow("implicitlylong") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-abc" << "val") - << QStringList("abc") << QStringList("val"); + << QStringList("abc") << QStringList("val") << QStringList(); QTest::newRow("implicitlylong_equal") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-abc=val") - << QStringList("abc") << QStringList("val"); + << QStringList("abc") << QStringList("val") << QStringList(); QTest::newRow("implicitlylong_longoption") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "--nn") - << QStringList("nn") << QStringList(); + << QStringList("nn") << QStringList() << QStringList(); QTest::newRow("implicitlylong_longoption_value") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "--abc" << "val") - << QStringList("abc") << QStringList("val"); + << QStringList("abc") << QStringList("val") << QStringList(); QTest::newRow("implicitlylong_with_space") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-c" << "val") - << QStringList("c") << QStringList("val"); + << QStringList("c") << QStringList("val") << QStringList(); QTest::newRow("forceshort_detached") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-I" << "45") - << QStringList("I") << QStringList("45"); + << QStringList("I") << QStringList("45") << QStringList(); QTest::newRow("forceshort_attached") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-I46") - << QStringList("I") << QStringList("46"); + << QStringList("I") << QStringList("46") << QStringList(); QTest::newRow("forceshort_mixed") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-I45" << "-nn") - << (QStringList() << "I" << "nn") << QStringList("45"); + << (QStringList() << "I" << "nn") << QStringList("45") << QStringList(); } void tst_QCommandLineParser::testSingleDashWordOptionModes() @@ -478,6 +493,7 @@ void tst_QCommandLineParser::testSingleDashWordOptionModes() QFETCH(QStringList, commandLine); QFETCH(QStringList, expectedOptionNames); QFETCH(QStringList, expectedOptionValues); + QFETCH(QStringList, invalidOptionValues); commandLine.prepend("tst_QCommandLineParser"); @@ -494,14 +510,19 @@ void tst_QCommandLineParser::testSingleDashWordOptionModes() QVERIFY(parser.addOption(forceShort)); QVERIFY(parser.parse(commandLine)); QCOMPARE(parser.optionNames(), expectedOptionNames); - for (int i = 0; i < expectedOptionValues.count(); ++i) - QCOMPARE(parser.value(parser.optionNames().at(i)), expectedOptionValues.at(i)); + for (int i = 0; i < expectedOptionValues.size(); ++i) { + const QString option = parser.optionNames().at(i); + if (invalidOptionValues.contains(option)) { + QByteArray msg = QLatin1String("QCommandLineParser: option not expecting values: \"%1\"").arg(option).toLatin1(); + QTest::ignoreMessage(QtWarningMsg, msg.data()); + } + QCOMPARE(parser.value(option), expectedOptionValues.at(i)); + } QCOMPARE(parser.unknownOptionNames(), QStringList()); } void tst_QCommandLineParser::testCpp11StyleInitialization() { -#if defined(Q_COMPILER_UNIFORM_INIT) QCoreApplication app(empty_argc, empty_argv); QCommandLineParser parser; @@ -515,19 +536,15 @@ void tst_QCommandLineParser::testCpp11StyleInitialization() QVERIFY(parser.parse({"tst_QCommandLineParser", "-a", "-vvv", "--infile=in.txt"})); QCOMPARE(parser.optionNames(), (QStringList{"a", "v", "v", "v", "infile"})); QCOMPARE(parser.value("infile"), QString("in.txt")); -#else - QSKIP("This test requires C++11 uniform initialization support in the compiler."); -#endif } void tst_QCommandLineParser::testVersionOption() { #if !QT_CONFIG(process) QSKIP("This test requires QProcess support"); -#else -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#elif defined(Q_OS_ANDROID) QSKIP("Deploying executable applications to file system on Android not supported."); -#endif +#else QCoreApplication app(empty_argc, empty_argv); QProcess process; @@ -545,7 +562,7 @@ void tst_QCommandLineParser::testVersionOption() static const char expectedOptionsHelp[] = "Options:\n" " -h, --help Displays help on commandline options.\n" - " --help-all Displays help including Qt specific options.\n" + " --help-all Displays help, including generic Qt options.\n" " -v, --version Displays version information.\n" " --load <url> Load file from URL.\n" " -o, --output <file> Set output file.\n" @@ -592,10 +609,9 @@ void tst_QCommandLineParser::testHelpOption() { #if !QT_CONFIG(process) QSKIP("This test requires QProcess support"); -#else -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#elif defined(Q_OS_ANDROID) QSKIP("Deploying executable applications to file system on Android not supported."); -#endif +#else QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode); QFETCH(QString, expectedHelpOutput); @@ -640,7 +656,7 @@ void tst_QCommandLineParser::testQuoteEscaping() { #if !QT_CONFIG(process) QSKIP("This test requires QProcess support"); -#elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#elif defined(Q_OS_ANDROID) QSKIP("Deploying executable applications to file system on Android not supported."); #else QCoreApplication app(empty_argc, empty_argv); @@ -666,7 +682,7 @@ void tst_QCommandLineParser::testUnknownOption() { #if !QT_CONFIG(process) QSKIP("This test requires QProcess support"); -#elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#elif defined(Q_OS_ANDROID) QSKIP("Deploying executable applications to file system on Android not supported."); #else QCoreApplication app(empty_argc, empty_argv); @@ -717,7 +733,7 @@ void tst_QCommandLineParser::testHelpAll() #if !QT_CONFIG(process) QSKIP("This test requires QProcess support"); #else -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#ifdef Q_OS_ANDROID QSKIP("Deploying executable applications to file system on Android not supported."); #endif @@ -737,6 +753,37 @@ void tst_QCommandLineParser::testHelpAll() #endif // QT_CONFIG(process) } +void tst_QCommandLineParser::testVeryLongOptionNames() +{ +#if !QT_CONFIG(process) + QSKIP("This test requires QProcess support"); +#elif defined(Q_OS_ANDROID) + QSKIP("Deploying executable applications to file system on Android not supported."); +#else + + QCoreApplication app(empty_argc, empty_argv); + QProcess process; + process.start("testhelper/qcommandlineparser_test_helper", QStringList() << "0" << "long" << "--help"); + QVERIFY(process.waitForFinished(5000)); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); + QString output = process.readAll(); +#ifdef Q_OS_WIN + output.replace(QStringLiteral("\r\n"), QStringLiteral("\n")); +#endif + const QStringList lines = output.split('\n'); + const int last = lines.size() - 1; + // Let's not compare everything, just the final parts. + QCOMPARE(lines.at(last - 7), " cdefghijklmnopqrstuvwxyz"); + QCOMPARE(lines.at(last - 6), " --looooooooooooong-option, --looooong-opt-alias <l Short description"); + QCOMPARE(lines.at(last - 5), " ooooooooooooong-value-name>"); + QCOMPARE(lines.at(last - 4), ""); + QCOMPARE(lines.at(last - 3), "Arguments:"); + QCOMPARE(lines.at(last - 2), " parsingMode The parsing mode to test."); + QCOMPARE(lines.at(last - 1), " command The command to execute."); + +#endif // QT_CONFIG(process) +} + QTEST_APPLESS_MAIN(tst_QCommandLineParser) #include "tst_qcommandlineparser.moc" diff --git a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.pro b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.pro deleted file mode 100644 index 75b74bbf4d..0000000000 --- a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qcommandlineparser -QT = core testlib -SOURCES = tst_qcommandlineparser.cpp diff --git a/tests/auto/corelib/tools/qcontiguouscache/CMakeLists.txt b/tests/auto/corelib/tools/qcontiguouscache/CMakeLists.txt new file mode 100644 index 0000000000..5c32c34023 --- /dev/null +++ b/tests/auto/corelib/tools/qcontiguouscache/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qcontiguouscache Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcontiguouscache LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qcontiguouscache + SOURCES + tst_qcontiguouscache.cpp +) diff --git a/tests/auto/corelib/tools/qcontiguouscache/qcontiguouscache.pro b/tests/auto/corelib/tools/qcontiguouscache/qcontiguouscache.pro deleted file mode 100644 index d79bd16c76..0000000000 --- a/tests/auto/corelib/tools/qcontiguouscache/qcontiguouscache.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qcontiguouscache -QT = core testlib -SOURCES = tst_qcontiguouscache.cpp diff --git a/tests/auto/corelib/tools/qcontiguouscache/tst_qcontiguouscache.cpp b/tests/auto/corelib/tools/qcontiguouscache/tst_qcontiguouscache.cpp index f305d63d46..ca110b1240 100644 --- a/tests/auto/corelib/tools/qcontiguouscache/tst_qcontiguouscache.cpp +++ b/tests/auto/corelib/tools/qcontiguouscache/tst_qcontiguouscache.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QObject> #include <QTest> @@ -75,20 +50,24 @@ void tst_QContiguousCache::empty() { QContiguousCache<int> c(10); QCOMPARE(c.capacity(), 10); + QCOMPARE(c.size(), 0); + // NOLINTNEXTLINE(qt-port-to-std-compatible-api): Test both size() and count() QCOMPARE(c.count(), 0); QVERIFY(c.isEmpty()); c.append(1); + // NOLINTNEXTLINE(qt-port-to-std-compatible-api): Test both size() and count() QCOMPARE(c.count(), 1); + QCOMPARE(c.size(), 1); QVERIFY(!c.isEmpty()); c.clear(); QCOMPARE(c.capacity(), 10); - QCOMPARE(c.count(), 0); + QCOMPARE(c.size(), 0); QVERIFY(c.isEmpty()); c.prepend(1); - QCOMPARE(c.count(), 1); + QCOMPARE(c.size(), 1); QVERIFY(!c.isEmpty()); c.clear(); - QCOMPARE(c.count(), 0); + QCOMPARE(c.size(), 0); QVERIFY(c.isEmpty()); QCOMPARE(c.capacity(), 10); } @@ -99,32 +78,32 @@ void tst_QContiguousCache::swap() c1.append(1); c1.swap(c2); QCOMPARE(c1.capacity(), 100); - QCOMPARE(c1.count(), 0 ); + QCOMPARE(c1.size(), 0 ); QCOMPARE(c2.capacity(), 10 ); - QCOMPARE(c2.count(), 1 ); + QCOMPARE(c2.size(), 1 ); } void tst_QContiguousCache::append_data() { - QTest::addColumn<int>("start"); - QTest::addColumn<int>("count"); - QTest::addColumn<int>("cacheSize"); + QTest::addColumn<qsizetype>("start"); + QTest::addColumn<qsizetype>("count"); + QTest::addColumn<qsizetype>("cacheSize"); QTest::addColumn<bool>("invalidIndexes"); - QTest::newRow("0+30[10]") << 0 << 30 << 10 << false; - QTest::newRow("300+30[10]") << 300 << 30 << 10 << false; - QTest::newRow("MAX-10+30[10]") << INT_MAX-10 << 30 << 10 << true; + QTest::newRow("0+30[10]") << qsizetype(0) << qsizetype(30) << qsizetype(10) << false; + QTest::newRow("300+30[10]") << qsizetype(300) << qsizetype(30) << qsizetype(10) << false; + QTest::newRow("MAX-10+30[10]") << std::numeric_limits<qsizetype>::max()-10 << qsizetype(30) << qsizetype(10) << true; } void tst_QContiguousCache::append() { - QFETCH(int, start); - QFETCH(int, count); - QFETCH(int, cacheSize); + QFETCH(qsizetype, start); + QFETCH(qsizetype, count); + QFETCH(qsizetype, cacheSize); QFETCH(bool, invalidIndexes); - int i, j; - QContiguousCache<int> c(cacheSize); + qsizetype i, j; + QContiguousCache<qsizetype> c(cacheSize); i = 1; QCOMPARE(c.available(), cacheSize); @@ -134,10 +113,10 @@ void tst_QContiguousCache::append() c.insert(start, i++); while (i < count) { c.append(i); - QCOMPARE(c.available(), qMax(0, cacheSize - i)); - QCOMPARE(c.first(), qMax(1, i-cacheSize+1)); + QCOMPARE(c.available(), qMax(qsizetype(0), cacheSize - i)); + QCOMPARE(c.first(), qMax(qsizetype(1), i-cacheSize+1)); QCOMPARE(c.last(), i); - QCOMPARE(c.count(), qMin(i, cacheSize)); + QCOMPARE(c.size(), qMin(i, cacheSize)); QCOMPARE(c.isFull(), i >= cacheSize); i++; } @@ -150,7 +129,7 @@ void tst_QContiguousCache::append() // test taking from end until empty. for (j = 0; j < cacheSize; j++, i--) { QCOMPARE(c.takeLast(), i-1); - QCOMPARE(c.count(), cacheSize-j-1); + QCOMPARE(c.size(), cacheSize-j-1); QCOMPARE(c.available(), j+1); QVERIFY(!c.isFull()); QCOMPARE(c.isEmpty(), j==cacheSize-1); @@ -188,7 +167,7 @@ void tst_QContiguousCache::prepend() QCOMPARE(c.available(), qMax(0, cacheSize - i)); QCOMPARE(c.last(), qMax(1, i-cacheSize+1)); QCOMPARE(c.first(), i); - QCOMPARE(c.count(), qMin(i, cacheSize)); + QCOMPARE(c.size(), qMin(i, cacheSize)); QCOMPARE(c.isFull(), i >= cacheSize); i++; } @@ -201,7 +180,7 @@ void tst_QContiguousCache::prepend() // test taking from start until empty. for (j = 0; j < cacheSize; j++, i--) { QCOMPARE(c.takeFirst(), i-1); - QCOMPARE(c.count(), cacheSize-j-1); + QCOMPARE(c.size(), cacheSize-j-1); QCOMPARE(c.available(), j+1); QVERIFY(!c.isFull()); QCOMPARE(c.isEmpty(), j==cacheSize-1); @@ -321,7 +300,7 @@ void tst_QContiguousCache::setCapacity() for (i = 280; i < 310; ++i) contiguousCache.insert(i, i); QCOMPARE(contiguousCache.capacity(), 100); - QCOMPARE(contiguousCache.count(), 30); + QCOMPARE(contiguousCache.size(), 30); QCOMPARE(contiguousCache.firstIndex(), 280); QCOMPARE(contiguousCache.lastIndex(), 309); @@ -333,7 +312,7 @@ void tst_QContiguousCache::setCapacity() contiguousCache.setCapacity(150); QCOMPARE(contiguousCache.capacity(), 150); - QCOMPARE(contiguousCache.count(), 30); + QCOMPARE(contiguousCache.size(), 30); QCOMPARE(contiguousCache.firstIndex(), 280); QCOMPARE(contiguousCache.lastIndex(), 309); @@ -345,7 +324,7 @@ void tst_QContiguousCache::setCapacity() contiguousCache.setCapacity(20); QCOMPARE(contiguousCache.capacity(), 20); - QCOMPARE(contiguousCache.count(), 20); + QCOMPARE(contiguousCache.size(), 20); QCOMPARE(contiguousCache.firstIndex(), 290); QCOMPARE(contiguousCache.lastIndex(), 309); diff --git a/tests/auto/corelib/tools/qcryptographichash/CMakeLists.txt b/tests/auto/corelib/tools/qcryptographichash/CMakeLists.txt new file mode 100644 index 0000000000..8a0c08fcad --- /dev/null +++ b/tests/auto/corelib/tools/qcryptographichash/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qcryptographichash Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcryptographichash LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +# Collect test data +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_qcryptographichash + SOURCES + tst_qcryptographichash.cpp + TESTDATA ${test_data} +) + +if(QT_FEATURE_sanitize_address) + set_property(TEST tst_qcryptographichash APPEND PROPERTY ENVIRONMENT "QTEST_FUNCTION_TIMEOUT=900000") +endif() diff --git a/tests/auto/corelib/tools/qcryptographichash/qcryptographichash.pro b/tests/auto/corelib/tools/qcryptographichash/qcryptographichash.pro deleted file mode 100644 index 8d3957a524..0000000000 --- a/tests/auto/corelib/tools/qcryptographichash/qcryptographichash.pro +++ /dev/null @@ -1,11 +0,0 @@ -CONFIG += testcase -TARGET = tst_qcryptographichash -QT = core testlib -SOURCES = tst_qcryptographichash.cpp - -TESTDATA += data/* - -android:!android-embedded { - RESOURCES += \ - testdata.qrc -} diff --git a/tests/auto/corelib/tools/qcryptographichash/testdata.qrc b/tests/auto/corelib/tools/qcryptographichash/testdata.qrc deleted file mode 100644 index 8f7bcea63c..0000000000 --- a/tests/auto/corelib/tools/qcryptographichash/testdata.qrc +++ /dev/null @@ -1,6 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>data/2c1517dad3678f03917f15849b052fd5.md5</file> - <file>data/d41d8cd98f00b204e9800998ecf8427e.md5</file> - </qresource> -</RCC> diff --git a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp index 3eef7631c8..c08afd67c4 100644 --- a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp +++ b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp @@ -1,36 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QCoreApplication> -#include <QtTest/QtTest> +#include <QTest> +#include <QScopeGuard> +#include <QCryptographicHash> #include <QtCore/QMetaEnum> +#include <thread> + Q_DECLARE_METATYPE(QCryptographicHash::Algorithm) class tst_QCryptographicHash : public QObject @@ -44,9 +23,25 @@ private slots: void sha1(); void sha3_data(); void sha3(); + void keccak(); + void keccak_data(); + void blake2_data(); + void blake2(); void files_data(); void files(); + void hashLength_data(); void hashLength(); + void addDataAcceptsNullByteArrayView_data() { hashLength_data(); } + void addDataAcceptsNullByteArrayView(); + void move(); + void swap(); + // keep last + void moreThan4GiBOfData_data(); + void moreThan4GiBOfData(); + void keccakBufferOverflow(); +private: + void ensureLargeData(); + std::vector<char> large; }; void tst_QCryptographicHash::repeated_result_data() @@ -60,19 +55,23 @@ void tst_QCryptographicHash::repeated_result() QCryptographicHash::Algorithm _algo = QCryptographicHash::Algorithm(algo); QCryptographicHash hash(_algo); + QCOMPARE_EQ(hash.algorithm(), _algo); + QFETCH(QByteArray, first); hash.addData(first); QFETCH(QByteArray, hash_first); - QByteArray result = hash.result(); + QByteArrayView result = hash.resultView(); QCOMPARE(result, hash_first); + QCOMPARE(result, hash.resultView()); QCOMPARE(result, hash.result()); hash.reset(); hash.addData(first); - result = hash.result(); + result = hash.resultView(); QCOMPARE(result, hash_first); QCOMPARE(result, hash.result()); + QCOMPARE(result, hash.resultView()); } void tst_QCryptographicHash::intermediary_result_data() @@ -153,6 +152,27 @@ void tst_QCryptographicHash::intermediary_result_data() << QByteArray("abc") << QByteArray("abc") << QByteArray::fromHex("B751850B1A57168A5693CD924B6B096E08F621827444F70D884F5D0240D2712E10E116E9192AF3C91A7EC57647E3934057340B4CF408D5A56592F8274EEC53F0") << QByteArray::fromHex("BB582DA40D15399ACF62AFCBBD6CFC9EE1DD5129B1EF9935DD3B21668F1A73D7841018BE3B13F281C3A8E9DA7EDB60F57B9F9F1C04033DF4CE3654B7B2ADB310"); + + QTest::newRow("keccak_224_abc_abc") + << int(QCryptographicHash::Keccak_224) + << QByteArray("abc") << QByteArray("abc") + << QByteArray::fromHex("c30411768506ebe1c2871b1ee2e87d38df342317300a9b97a95ec6a8") + << QByteArray::fromHex("048330e7c7c8b4a41ab713b3a6f958d77b8cf3ee969930f1584dd550"); + QTest::newRow("keccak_256_abc_abc") + << int(QCryptographicHash::Keccak_256) + << QByteArray("abc") << QByteArray("abc") + << QByteArray::fromHex("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45") + << QByteArray::fromHex("9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21"); + QTest::newRow("keccak_384_abc_abc") + << int(QCryptographicHash::Keccak_384) + << QByteArray("abc") << QByteArray("abc") + << QByteArray::fromHex("f7df1165f033337be098e7d288ad6a2f74409d7a60b49c36642218de161b1f99f8c681e4afaf31a34db29fb763e3c28e") + << QByteArray::fromHex("d733b87d392d270889d3da23ae113f349e25574b445f319cde4cd3f877c753e9e3c65980421339b3a131457ff393939f"); + QTest::newRow("keccak_512_abc_abc") + << int(QCryptographicHash::Keccak_512) + << QByteArray("abc") << QByteArray("abc") + << QByteArray::fromHex("18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96") + << QByteArray::fromHex("a7c392d2a42155761ca76bddde1c47d55486b007edf465397bfb9dfa74d11c8f0d7c86cd29415283f1b5e7f655cec25b869c9e9c33a8986f0b38542fb12bfb93"); } void tst_QCryptographicHash::intermediary_result() @@ -165,16 +185,14 @@ void tst_QCryptographicHash::intermediary_result() hash.addData(first); QFETCH(QByteArray, hash_first); - QByteArray result = hash.result(); - QCOMPARE(result, hash_first); + QCOMPARE(hash.resultView(), hash_first); // don't reset QFETCH(QByteArray, second); QFETCH(QByteArray, hash_firstsecond); hash.addData(second); - result = hash.result(); - QCOMPARE(result, hash_firstsecond); + QCOMPARE(hash.resultView(), hash_firstsecond); hash.reset(); } @@ -195,10 +213,7 @@ void tst_QCryptographicHash::sha1() // SHA1(A million repetitions of "a") = // 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F - QByteArray as; - for (int i = 0; i < 1000000; ++i) - as += 'a'; - QCOMPARE(QCryptographicHash::hash(as, QCryptographicHash::Sha1).toHex().toUpper(), + QCOMPARE(QCryptographicHash::hash(QByteArray(1'000'000, 'a'), QCryptographicHash::Sha1).toHex().toUpper(), QByteArray("34AA973CD4C4DAA4F61EEB2BDBAD27316534016F")); } @@ -264,6 +279,172 @@ void tst_QCryptographicHash::sha3() QCOMPARE(result, expectedResult); } +void tst_QCryptographicHash::keccak_data() +{ + QTest::addColumn<QCryptographicHash::Algorithm>("algorithm"); + QTest::addColumn<QByteArray>("data"); + QTest::addColumn<QByteArray>("expectedResult"); + +#define ROW(Tag, Algorithm, Input, Result) \ + QTest::newRow(Tag) << Algorithm << QByteArrayLiteral(Input) << QByteArray::fromHex(Result) + + ROW("keccak_224_pangram", + QCryptographicHash::Keccak_224, + "The quick brown fox jumps over the lazy dog", + "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe"); + + ROW("keccak_224_pangram_dot", + QCryptographicHash::Keccak_224, + "The quick brown fox jumps over the lazy dog.", + "c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab"); + + ROW("keccak_256_pangram", + QCryptographicHash::Keccak_256, + "The quick brown fox jumps over the lazy dog", + "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15"); + + ROW("keccak_256_pangram_dot", + QCryptographicHash::Keccak_256, + "The quick brown fox jumps over the lazy dog.", + "578951e24efd62a3d63a86f7cd19aaa53c898fe287d2552133220370240b572d"); + + ROW("keccak_384_pangram", + QCryptographicHash::Keccak_384, + "The quick brown fox jumps over the lazy dog", + "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3"); + + ROW("keccak_384_pangram_dot", + QCryptographicHash::Keccak_384, + "The quick brown fox jumps over the lazy dog.", + "9ad8e17325408eddb6edee6147f13856ad819bb7532668b605a24a2d958f88bd5c169e56dc4b2f89ffd325f6006d820b"); + + ROW("skeccak_512_pangram", + QCryptographicHash::Keccak_512, + "The quick brown fox jumps over the lazy dog", + "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609"); + + ROW("keccak_512_pangram_dot", + QCryptographicHash::Keccak_512, + "The quick brown fox jumps over the lazy dog.", + "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded6387889064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760"); + +#undef ROW +} + +void tst_QCryptographicHash::keccak() +{ + QFETCH(QCryptographicHash::Algorithm, algorithm); + QFETCH(QByteArray, data); + QFETCH(QByteArray, expectedResult); + + const auto result = QCryptographicHash::hash(data, algorithm); + QCOMPARE(result, expectedResult); +} + +void tst_QCryptographicHash::blake2_data() +{ + QTest::addColumn<QCryptographicHash::Algorithm>("algorithm"); + QTest::addColumn<QByteArray>("data"); + QTest::addColumn<QByteArray>("expectedResult"); + +#define ROW(Tag, Algorithm, Input, Result) \ + QTest::newRow(Tag) << Algorithm << QByteArrayLiteral(Input) << QByteArray::fromHex(Result) + + // BLAKE2b + ROW("blake2b_160_pangram", + QCryptographicHash::Blake2b_160, + "The quick brown fox jumps over the lazy dog", + "3c523ed102ab45a37d54f5610d5a983162fde84f"); + + ROW("blake2b_160_pangram_dot", + QCryptographicHash::Blake2b_160, + "The quick brown fox jumps over the lazy dog.", + "d0c8bb0bdd830296d1d4f4348176699ccccc16bb"); + + ROW("blake2b_256_pangram", + QCryptographicHash::Blake2b_256, + "The quick brown fox jumps over the lazy dog", + "01718cec35cd3d796dd00020e0bfecb473ad23457d063b75eff29c0ffa2e58a9"); + + ROW("blake2b_256_pangram_dot", + QCryptographicHash::Blake2b_256, + "The quick brown fox jumps over the lazy dog.", + "69d7d3b0afba81826d27024c17f7f183659ed0812cf27b382eaef9fdc29b5712"); + + ROW("blake2b_384_pangram", + QCryptographicHash::Blake2b_384, + "The quick brown fox jumps over the lazy dog", + "b7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d"); + + ROW("blake2b_384_pangram_dot", + QCryptographicHash::Blake2b_384, + "The quick brown fox jumps over the lazy dog.", + "16d65de1a3caf1c26247234c39af636284c7e19ca448c0de788272081410778852c94d9cef6b939968d4f872c7f78337"); + + ROW("blake2b_512_pangram", + QCryptographicHash::Blake2b_512, + "The quick brown fox jumps over the lazy dog", + "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918"); + + ROW("blake2b_512_pangram_dot", + QCryptographicHash::Blake2b_512, + "The quick brown fox jumps over the lazy dog.", + "87af9dc4afe5651b7aa89124b905fd214bf17c79af58610db86a0fb1e0194622a4e9d8e395b352223a8183b0d421c0994b98286cbf8c68a495902e0fe6e2bda2"); + + // BLAKE2s + ROW("blake2s_128_pangram", + QCryptographicHash::Blake2s_128, + "The quick brown fox jumps over the lazy dog", + "96fd07258925748a0d2fb1c8a1167a73"); + + ROW("blake2s_128_pangram_dot", + QCryptographicHash::Blake2s_128, + "The quick brown fox jumps over the lazy dog.", + "1f298f2e1f9c2490e506c2308f64e7c0"); + + ROW("blake2s_160_pangram", + QCryptographicHash::Blake2s_160, + "The quick brown fox jumps over the lazy dog", + "5a604fec9713c369e84b0ed68daed7d7504ef240"); + + ROW("blake2s_160_pangram_dot", + QCryptographicHash::Blake2s_160, + "The quick brown fox jumps over the lazy dog.", + "cd4a863226463aac852662d16275d399966e3ffe"); + + ROW("blake2s_224_pangram", + QCryptographicHash::Blake2s_224, + "The quick brown fox jumps over the lazy dog", + "e4e5cb6c7cae41982b397bf7b7d2d9d1949823ae78435326e8db4912"); + + ROW("blake2s_224_pangram_dot", + QCryptographicHash::Blake2s_224, + "The quick brown fox jumps over the lazy dog.", + "fd1557500ef49f308882969507acd18a13e155c26f8fcd82f9bf2ff7"); + + ROW("blake2s_256_pangram", + QCryptographicHash::Blake2s_256, + "The quick brown fox jumps over the lazy dog", + "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812"); + + ROW("blake2s_256_pangram_dot", + QCryptographicHash::Blake2s_256, + "The quick brown fox jumps over the lazy dog.", + "95bca6e1b761dca1323505cc629949a0e03edf11633cc7935bd8b56f393afcf2"); + +#undef ROW +} + +void tst_QCryptographicHash::blake2() +{ + QFETCH(QCryptographicHash::Algorithm, algorithm); + QFETCH(QByteArray, data); + QFETCH(QByteArray, expectedResult); + + const auto result = QCryptographicHash::hash(data, algorithm); + QCOMPARE(result, expectedResult); +} + void tst_QCryptographicHash::files_data() { QTest::addColumn<QString>("filename"); QTest::addColumn<QCryptographicHash::Algorithm>("algorithm"); @@ -293,14 +474,192 @@ void tst_QCryptographicHash::files() } } -void tst_QCryptographicHash::hashLength() +void tst_QCryptographicHash::hashLength_data() { + QTest::addColumn<QCryptographicHash::Algorithm>("algorithm"); auto metaEnum = QMetaEnum::fromType<QCryptographicHash::Algorithm>(); for (int i = 0, value = metaEnum.value(i); value != -1; value = metaEnum.value(++i)) { auto algorithm = QCryptographicHash::Algorithm(value); - QByteArray output = QCryptographicHash::hash(QByteArrayLiteral("test"), algorithm); - QCOMPARE(QCryptographicHash::hashLength(algorithm), output.length()); + QTest::addRow("%s", metaEnum.key(i)) << algorithm; + } +} + +void tst_QCryptographicHash::hashLength() +{ + QFETCH(const QCryptographicHash::Algorithm, algorithm); + + qsizetype expectedSize; + if (algorithm == QCryptographicHash::NumAlgorithms) { + // It's UB to call ::hash() with NumAlgorithms, but hashLength() is + // fine and returns 0 for invalid values: + expectedSize = 0; + } else { + expectedSize = QCryptographicHash::hash("test", algorithm).size(); + } + QCOMPARE(QCryptographicHash::hashLength(algorithm), expectedSize); +} + +void tst_QCryptographicHash::addDataAcceptsNullByteArrayView() +{ + QFETCH(const QCryptographicHash::Algorithm, algorithm); + + if (!QCryptographicHash::supportsAlgorithm(algorithm)) + QSKIP("QCryptographicHash doesn't support this algorithm"); + + QCryptographicHash hash1(algorithm); + hash1.addData("meep"); + hash1.addData(QByteArrayView{}); // after other data + + QCryptographicHash hash2(algorithm); + hash2.addData(QByteArrayView{}); // before any other data + hash2.addData("meep"); + + const auto expected = QCryptographicHash::hash("meep", algorithm); + + QCOMPARE(hash1.resultView(), expected); + QCOMPARE(hash2.resultView(), expected); +} + +void tst_QCryptographicHash::move() +{ + QCryptographicHash hash1(QCryptographicHash::Sha1); + hash1.addData("a"); + + // move constructor + auto hash2(std::move(hash1)); + hash2.addData("b"); + + // move assign operator + QCryptographicHash hash3(QCryptographicHash::Sha256); + hash3.addData("no effect on the end result"); + hash3 = std::move(hash2); + hash3.addData("c"); + + QCOMPARE(hash3.resultView(), QByteArray::fromHex("A9993E364706816ABA3E25717850C26C9CD0D89D")); +} + +void tst_QCryptographicHash::swap() +{ + QCryptographicHash hash1(QCryptographicHash::Sha1); + QCryptographicHash hash2(QCryptographicHash::Sha256); + + hash1.addData("da"); + hash2.addData("te"); + + hash1.swap(hash2); + + hash2.addData("ta"); + hash1.addData("st"); + + QCOMPARE(hash2.result(), QCryptographicHash::hash("data", QCryptographicHash::Sha1)); + QCOMPARE(hash1.result(), QCryptographicHash::hash("test", QCryptographicHash::Sha256)); +} + +void tst_QCryptographicHash::ensureLargeData() +{ +#if QT_POINTER_SIZE > 4 + QElapsedTimer timer; + timer.start(); + const size_t GiB = 1024 * 1024 * 1024; + if (large.size() == 4 * GiB + 1) + return; + try { + large.resize(4 * GiB + 1, '\0'); + } catch (const std::bad_alloc &) { + QSKIP("Could not allocate 4GiB plus one byte of RAM."); + } + QCOMPARE(large.size(), 4 * GiB + 1); + large.back() = '\1'; + qDebug("created dataset in %lld ms", timer.elapsed()); +#endif +} + +void tst_QCryptographicHash::moreThan4GiBOfData_data() +{ +#if QT_POINTER_SIZE > 4 + if (ensureLargeData(); large.empty()) + return; + QTest::addColumn<QCryptographicHash::Algorithm>("algorithm"); + auto me = QMetaEnum::fromType<QCryptographicHash::Algorithm>(); + auto row = [me] (QCryptographicHash::Algorithm algo) { + QTest::addRow("%s", me.valueToKey(int(algo))) << algo; + }; + // these are reasonably fast (O(secs)) + row(QCryptographicHash::Md4); + row(QCryptographicHash::Md5); + row(QCryptographicHash::Sha1); + if (!qgetenv("QTEST_ENVIRONMENT").split(' ').contains("ci")) { + // This is important but so slow (O(minute)) that, on CI, it tends to time out. + // Retain it for manual runs, all the same, as most dev machines will be fast enough. + row(QCryptographicHash::Sha512); + } + // the rest is just too slow +#else + QSKIP("This test is 64-bit only."); +#endif +} + +void tst_QCryptographicHash::moreThan4GiBOfData() +{ + QFETCH(const QCryptographicHash::Algorithm, algorithm); + + using MaybeThread = std::thread; + + QElapsedTimer timer; + timer.start(); + const auto sg = qScopeGuard([&] { + qDebug() << algorithm << "test finished in" << timer.restart() << "ms"; + }); + + const auto view = QByteArrayView{large}; + const auto first = view.first(view.size() / 2); + const auto last = view.sliced(view.size() / 2); + + QByteArray single; + QByteArray chunked; + + auto t = MaybeThread{[&] { + QCryptographicHash h(algorithm); + h.addData(view); + single = h.result(); + }}; + { + QCryptographicHash h(algorithm); + h.addData(first); + h.addData(last); + chunked = h.result(); } + t.join(); + + QCOMPARE(single, chunked); +} + +void tst_QCryptographicHash::keccakBufferOverflow() +{ +#if QT_POINTER_SIZE == 4 + QSKIP("This is a 64-bit-only test"); +#else + + if (ensureLargeData(); large.empty()) + return; + + QElapsedTimer timer; + timer.start(); + const auto sg = qScopeGuard([&] { + qDebug() << "test finished in" << timer.restart() << "ms"; + }); + + constexpr qsizetype magic = INT_MAX/4; + QCOMPARE_GE(large.size(), size_t(magic + 1)); + + QCryptographicHash hash(QCryptographicHash::Algorithm::Keccak_224); + const auto first = QByteArrayView{large}.first(1); + const auto second = QByteArrayView{large}.sliced(1, magic); + hash.addData(first); + hash.addData(second); + (void)hash.resultView(); + QVERIFY(true); // didn't crash +#endif } QTEST_MAIN(tst_QCryptographicHash) diff --git a/tests/auto/corelib/tools/qduplicatetracker/CMakeLists.txt b/tests/auto/corelib/tools/qduplicatetracker/CMakeLists.txt new file mode 100644 index 0000000000..13645c50b8 --- /dev/null +++ b/tests/auto/corelib/tools/qduplicatetracker/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qduplicatetracker Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qduplicatetracker LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qduplicatetracker + SOURCES + tst_qduplicatetracker.cpp + LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp b/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp new file mode 100644 index 0000000000..ad0b6abbc7 --- /dev/null +++ b/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp @@ -0,0 +1,230 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtTest/QtTest> + +#include <QtCore/private/qduplicatetracker_p.h> + +#include <QObject> + +#include <string> +#include <utility> + +class tst_QDuplicateTracker : public QObject +{ + Q_OBJECT +private slots: + void hasSeen(); + void clear(); + void appendTo(); + void appendTo_special(); +}; + +void tst_QDuplicateTracker::hasSeen() +{ + { + QDuplicateTracker<int, 2> tracker; + QVERIFY(!tracker.hasSeen(0)); + QVERIFY(tracker.hasSeen(0)); + QVERIFY(!tracker.hasSeen(1)); + QVERIFY(tracker.hasSeen(1)); + // past the prealloc amount + QVERIFY(!tracker.hasSeen(2)); + QVERIFY(tracker.hasSeen(2)); + } + + { + QDuplicateTracker<QString, 2> tracker; + QString string1("string1"); + QString string2("string2"); + QString string2_2("string2"); + QString string3("string3"); + + // Move when seen + QVERIFY(!tracker.hasSeen(string1)); + QVERIFY(tracker.hasSeen(std::move(string1))); + + // Move when unseen + QVERIFY(!tracker.hasSeen(std::move(string2))); + QVERIFY(tracker.hasSeen(string2_2)); + + // Past the prealloc amount + QVERIFY(!tracker.hasSeen(string3)); + QVERIFY(tracker.hasSeen(string3)); + } + + { + QDuplicateTracker<std::string, 2> tracker; + std::string string1("string1"); + std::string string2("string2"); + std::string string2_2("string2"); + std::string string3("string3"); + + // Move when seen + QVERIFY(!tracker.hasSeen(string1)); + QVERIFY(tracker.hasSeen(std::move(string1))); + + // Move when unseen + QVERIFY(!tracker.hasSeen(std::move(string2))); + QVERIFY(tracker.hasSeen(string2_2)); + + // Past the prealloc amount + QVERIFY(!tracker.hasSeen(string3)); + QVERIFY(tracker.hasSeen(string3)); + } + +} + +void tst_QDuplicateTracker::clear() +{ + QDuplicateTracker<int, 2> tracker; + QVERIFY(!tracker.hasSeen(0)); + QVERIFY(tracker.hasSeen(0)); + QVERIFY(!tracker.hasSeen(1)); + QVERIFY(tracker.hasSeen(1)); + + tracker.clear(); + QVERIFY(!tracker.hasSeen(0)); + QVERIFY(tracker.hasSeen(0)); + QVERIFY(!tracker.hasSeen(1)); + QVERIFY(tracker.hasSeen(1)); +} + +void tst_QDuplicateTracker::appendTo() +{ + QDuplicateTracker<int, 2> tracker; + QVERIFY(!tracker.hasSeen(0)); + QVERIFY(!tracker.hasSeen(1)); + QList<int> a; + a.append(-1); + tracker.appendTo(a); + std::sort(a.begin(), a.end()); + QCOMPARE(a, QList<int>({ -1, 0, 1 })); + + QList<int> b; + tracker.appendTo(b); + std::sort(b.begin(), b.end()); + QCOMPARE(b, QList<int>({ 0, 1 })); + + QVERIFY(!tracker.hasSeen(2)); + QList<int> c; + std::move(tracker).appendTo(c); + std::sort(c.begin(), c.end()); + QCOMPARE(c, QList<int>({ 0, 1, 2 })); + if (QDuplicateTracker<int, 2>::uses_pmr) { + // the following is only true if we use the std container + QVERIFY(!tracker.hasSeen(0)); + QVERIFY(!tracker.hasSeen(1)); + QVERIFY(!tracker.hasSeen(2)); + } +} + +struct ConstructionCounted +{ + ConstructionCounted(int i) : i(i) { } + ConstructionCounted(ConstructionCounted &&other) noexcept + : i(other.i), copies(other.copies), moves(other.moves + 1) + { + // set to some easily noticeable values + other.i = -64; + other.copies = -64; + other.moves = -64; + } + ConstructionCounted &operator=(ConstructionCounted &&other) noexcept + { + ConstructionCounted moved = std::move(other); + swap(moved); + // set to some easily noticeable values + other.i = -64; + other.copies = -64; + other.moves = -64; + return *this; + } + ConstructionCounted(const ConstructionCounted &other) noexcept + : i(other.i), copies(other.copies + 1), moves(other.moves) + { + } + ConstructionCounted &operator=(const ConstructionCounted &other) noexcept + { + ConstructionCounted copy = other; + swap(copy); + return *this; + } + ~ConstructionCounted() = default; + + friend bool operator==(const ConstructionCounted &lhs, const ConstructionCounted &rhs) + { + return lhs.i == rhs.i; + } + + QString toString() { return QString::number(i); } + + void swap(ConstructionCounted &other) + { + std::swap(copies, other.copies); + std::swap(i, other.i); + std::swap(moves, other.moves); + } + + int i; + int copies = 0; + int moves = 0; +}; + +// for std::unordered_set +namespace std { +template<> +struct hash<ConstructionCounted> +{ + std::size_t operator()(const ConstructionCounted &c) const noexcept { return c.i; } +}; +} + +// for QSet +size_t qHash(const ConstructionCounted &c, std::size_t seed = 0) +{ + return qHash(c.i, seed); +} + +void tst_QDuplicateTracker::appendTo_special() +{ + QDuplicateTracker<ConstructionCounted> tracker(3); + QVERIFY(!tracker.hasSeen(1)); + QVERIFY(!tracker.hasSeen(2)); + QVERIFY(!tracker.hasSeen(3)); + + QVERIFY(tracker.hasSeen(1)); + QVERIFY(tracker.hasSeen(2)); + QVERIFY(tracker.hasSeen(3)); + { + QList<ConstructionCounted> a; + a.reserve(3); + tracker.appendTo(a); + for (const auto &counter : a) { + QCOMPARE(counter.moves, 1); + QCOMPARE(counter.copies, 1); + } + } + QVERIFY(tracker.hasSeen(1)); + QVERIFY(tracker.hasSeen(2)); + QVERIFY(tracker.hasSeen(3)); + { + QList<ConstructionCounted> a; + a.reserve(3); + std::move(tracker).appendTo(a); + if (QDuplicateTracker<ConstructionCounted>::uses_pmr) { + // the following is only true if we use the std container + for (const auto &counter : a) { + QCOMPARE(counter.moves, 2); + QCOMPARE(counter.copies, 0); + } + QVERIFY(!tracker.hasSeen(1)); + QVERIFY(!tracker.hasSeen(2)); + QVERIFY(!tracker.hasSeen(3)); + } + } +} + +QTEST_MAIN(tst_QDuplicateTracker) + +#include "tst_qduplicatetracker.moc" diff --git a/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt b/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt new file mode 100644 index 0000000000..3f76f8a38f --- /dev/null +++ b/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qeasingcurve Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qeasingcurve LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qeasingcurve + SOURCES + tst_qeasingcurve.cpp +) diff --git a/tests/auto/corelib/tools/qeasingcurve/qeasingcurve.pro b/tests/auto/corelib/tools/qeasingcurve/qeasingcurve.pro deleted file mode 100644 index 80c5a94a83..0000000000 --- a/tests/auto/corelib/tools/qeasingcurve/qeasingcurve.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qeasingcurve -QT = core testlib -SOURCES = tst_qeasingcurve.cpp diff --git a/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp index 2a9c1e1e41..fc8c1a3e5c 100644 --- a/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp +++ b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <qeasingcurve.h> @@ -399,7 +374,7 @@ void tst_QEasingCurve::valueForProgress() // in theory the baseline should't have an error of more than 0.00005 due to how its rounded, // but due to FP imprecision, we have to adjust the error a bit more. const qreal errorBound = 0.00006; - for (int i = 0; i < at.count(); ++i) { + for (int i = 0; i < at.size(); ++i) { const qreal ex = expected.at(i); const qreal error = qAbs(ex - curve.valueForProgress(at.at(i)/qreal(100))); QVERIFY(error <= errorBound); @@ -428,9 +403,9 @@ void tst_QEasingCurve::setCustomType() QCOMPARE(curve.valueForProgress(0.15), 0.1); QCOMPARE(curve.valueForProgress(0.20), 0.2); QCOMPARE(curve.valueForProgress(0.25), 0.2); - // QTBUG-69947, MinGW 7.3 returns 0.2 + // QTBUG-69947, MinGW 7.3, 8.1 x86 returns 0.2 #if defined(Q_CC_MINGW) -#if !defined(__GNUC__) || __GNUC__ != 7 || __GNUC_MINOR__ < 3 +#if !defined(__GNUC__) || defined(__MINGW64__) QCOMPARE(curve.valueForProgress(0.30), 0.3); #endif #endif @@ -501,7 +476,7 @@ class tst_QEasingProperties : public QObject Q_OBJECT Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing) public: - tst_QEasingProperties(QObject *parent = 0) : QObject(parent) {} + tst_QEasingProperties(QObject *parent = nullptr) : QObject(parent) {} QEasingCurve easing() const { return e; } void setEasing(const QEasingCurve& value) { e = value; } @@ -546,10 +521,9 @@ void tst_QEasingCurve::properties() void tst_QEasingCurve::metaTypes() { - QVERIFY(QMetaType::type("QEasingCurve") == QMetaType::QEasingCurve); + QVERIFY(QMetaType::fromName("QEasingCurve").id() == QMetaType::QEasingCurve); - QCOMPARE(QByteArray(QMetaType::typeName(QMetaType::QEasingCurve)), - QByteArray("QEasingCurve")); + QCOMPARE(QByteArray(QMetaType(QMetaType::QEasingCurve).name()), QByteArray("QEasingCurve")); QVERIFY(QMetaType::isRegistered(QMetaType::QEasingCurve)); @@ -600,18 +574,18 @@ void tst_QEasingCurve::bezierSpline_data() static inline void setupBezierSpline(QEasingCurve *easingCurve, const QString &string) { - QStringList pointStr = string.split(QLatin1Char(' ')); + const QStringList pointStr = string.split(QLatin1Char(' ')); - QVector<QPointF> points; - foreach (const QString &str, pointStr) { + QList<QPointF> points; + for (const QString &str : pointStr) { QStringList coordStr = str.split(QLatin1Char(',')); QPointF point(coordStr.first().toDouble(), coordStr.last().toDouble()); points.append(point); } - QVERIFY(points.count() % 3 == 0); + QVERIFY(points.size() % 3 == 0); - for (int i = 0; i < points.count() / 3; i++) { + for (int i = 0; i < points.size() / 3; i++) { QPointF c1 = points.at(i * 3); QPointF c2 = points.at(i * 3 + 1); QPointF p1 = points.at(i * 3 + 2); @@ -629,7 +603,7 @@ void tst_QEasingCurve::bezierSpline() setupBezierSpline(&bezierEasingCurve, definition); const qreal errorBound = 0.002; - for (int i = 0; i < at.count(); ++i) { + for (int i = 0; i < at.size(); ++i) { const qreal ex = expected.at(i); const qreal value = bezierEasingCurve.valueForProgress(at.at(i)/qreal(100)); const qreal error = qAbs(ex - value); @@ -668,11 +642,11 @@ void tst_QEasingCurve::tcbSpline_data() static inline void setupTCBSpline(QEasingCurve *easingCurve, const QString &string) { - QStringList pointStr = string.split(QLatin1Char(' ')); + const QStringList pointStr = string.split(QLatin1Char(' ')); - foreach (const QString &str, pointStr) { + for (const QString &str : pointStr) { QStringList coordStr = str.split(QLatin1Char(',')); - Q_ASSERT(coordStr.count() == 5); + Q_ASSERT(coordStr.size() == 5); QPointF point(coordStr.first().toDouble(), coordStr.at(1).toDouble()); qreal t = coordStr.at(2).toDouble(); qreal c = coordStr.at(3).toDouble(); @@ -691,7 +665,7 @@ void tst_QEasingCurve::tcbSpline() setupTCBSpline(&tcbEasingCurve, definition); const qreal errorBound = 0.002; - for (int i = 0; i < at.count(); ++i) { + for (int i = 0; i < at.size(); ++i) { const qreal ex = expected.at(i); const qreal value = tcbEasingCurve.valueForProgress(at.at(i)/qreal(100)); const qreal error = qAbs(ex - value); diff --git a/tests/auto/corelib/tools/qexplicitlyshareddatapointer/CMakeLists.txt b/tests/auto/corelib/tools/qexplicitlyshareddatapointer/CMakeLists.txt new file mode 100644 index 0000000000..280918e302 --- /dev/null +++ b/tests/auto/corelib/tools/qexplicitlyshareddatapointer/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qexplicitlyshareddatapointer Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qexplicitlyshareddatapointer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qexplicitlyshareddatapointer + SOURCES + tst_qexplicitlyshareddatapointer.cpp +) diff --git a/tests/auto/corelib/tools/qexplicitlyshareddatapointer/qexplicitlyshareddatapointer.pro b/tests/auto/corelib/tools/qexplicitlyshareddatapointer/qexplicitlyshareddatapointer.pro deleted file mode 100644 index 45fe1f60fe..0000000000 --- a/tests/auto/corelib/tools/qexplicitlyshareddatapointer/qexplicitlyshareddatapointer.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qexplicitlyshareddatapointer -QT = core testlib -SOURCES = tst_qexplicitlyshareddatapointer.cpp diff --git a/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.cpp b/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.cpp index e89e634841..5e105a090a 100644 --- a/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.cpp +++ b/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.cpp @@ -1,33 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + + +#include <QTest> #include <QtCore/QSharedData> /*! @@ -79,8 +54,8 @@ public: class Derived : public Base { public: - virtual Base *clone() { return new Derived(*this); } - virtual bool isBase() const { return false; } + virtual Base *clone() override { return new Derived(*this); } + virtual bool isBase() const override { return false; } }; QT_BEGIN_NAMESPACE @@ -169,7 +144,7 @@ void tst_QExplicitlySharedDataPointer::data() const pointer.data(); /* Check that this cast is possible. */ - static_cast<const MyClass *>(pointer.data()); + Q_UNUSED(static_cast<const MyClass *>(pointer.data())); QVERIFY(! (pointer == nullptr)); QVERIFY(! (nullptr == pointer)); @@ -181,7 +156,7 @@ void tst_QExplicitlySharedDataPointer::data() const pointer.data(); /* Check that this cast is possible. */ - static_cast<const MyClass *>(pointer.data()); + Q_UNUSED(static_cast<const MyClass *>(pointer.data())); } /* Must not mutate the pointer. */ @@ -190,8 +165,8 @@ void tst_QExplicitlySharedDataPointer::data() const pointer.data(); /* Check that these casts are possible. */ - static_cast<MyClass *>(pointer.data()); - static_cast<const MyClass *>(pointer.data()); + Q_UNUSED(static_cast<MyClass *>(pointer.data())); + Q_UNUSED(static_cast<const MyClass *>(pointer.data())); } /* Must not mutate the pointer. */ @@ -200,8 +175,8 @@ void tst_QExplicitlySharedDataPointer::data() const pointer.data(); /* Check that these casts are possible. */ - static_cast<MyClass *>(pointer.data()); - static_cast<const MyClass *>(pointer.data()); + Q_UNUSED(static_cast<MyClass *>(pointer.data())); + Q_UNUSED(static_cast<const MyClass *>(pointer.data())); } } @@ -248,4 +223,3 @@ void tst_QExplicitlySharedDataPointer::swap() const QTEST_MAIN(tst_QExplicitlySharedDataPointer) #include "tst_qexplicitlyshareddatapointer.moc" -// vim: et:ts=4:sw=4:sts=4 diff --git a/tests/auto/corelib/tools/qflatmap/CMakeLists.txt b/tests/auto/corelib/tools/qflatmap/CMakeLists.txt new file mode 100644 index 0000000000..bc98c669fc --- /dev/null +++ b/tests/auto/corelib/tools/qflatmap/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qflatmap Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qflatmap LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qflatmap + SOURCES + tst_qflatmap.cpp + LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp b/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp new file mode 100644 index 0000000000..986cf2407b --- /dev/null +++ b/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp @@ -0,0 +1,732 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#define QT_USE_QSTRINGBUILDER +#define QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT + +#include <QTest> + +#include <private/qflatmap_p.h> +#include <qbytearray.h> +#include <qstring.h> +#include <qstringview.h> +#include <qvarlengtharray.h> + +#include <algorithm> +#include <list> +#include <tuple> + +static constexpr bool is_even(int n) { return n % 2 == 0; } +static constexpr bool is_empty(QAnyStringView v) { return v.isEmpty(); } + +namespace { +template <typename P> +constexpr inline bool is_pair_impl_v = false; +template <typename T, typename S> +constexpr inline bool is_pair_impl_v<std::pair<T,S>> = true; +template <typename P> +constexpr inline bool is_pair_v = is_pair_impl_v<std::decay_t<P>>; +template <typename P> +using if_pair = std::enable_if_t<is_pair_v<P>, bool>; +} + +class tst_QFlatMap : public QObject +{ + Q_OBJECT +private slots: + void constructing(); + void constAccess(); + void insertion(); + void insertRValuesAndLValues(); + void removal(); + void extraction(); + void iterators(); + void remove_if_pair() { remove_if_impl([](const auto &p) -> if_pair<decltype(p)> { return is_even(p.first) && is_empty(p.second); }); } + void remove_if_key_value() { remove_if_impl([](const auto &k, const auto &v) { return is_even(k) && is_empty(v); }); } + void remove_if_key() { remove_if_impl([](int k) { return is_even(k); }, true); } + void statefulComparator(); + void transparency_using(); + void transparency_struct(); + void try_emplace_and_insert_or_assign(); + void viewIterators(); + void varLengthArray(); + +private: + template <typename Compare> + void transparency_impl(); + template <typename Predicate> + void remove_if_impl(Predicate p, bool removeNonEmptyValues = false); +}; + +void tst_QFlatMap::constructing() +{ + using Map = QFlatMap<int, QByteArray>; + Map fmDefault; + QVERIFY(fmDefault.isEmpty()); + QCOMPARE(fmDefault.size(), Map::size_type(0)); + QCOMPARE(fmDefault.size(), fmDefault.count()); + + auto key_compare = fmDefault.key_comp(); + auto selfbuilt_value_compare + = [&key_compare](const Map::value_type &a, const Map::value_type &b) + { + return key_compare(a.first, b.first); + }; + auto value_compare = fmDefault.value_comp(); + + Map::key_container_type kv = { 6, 2, 1 }; + Map::mapped_container_type mv = { "foo", "bar", "baz" }; + Map fmCopy{kv, mv}; + QCOMPARE(fmCopy.size(), Map::size_type(3)); + QVERIFY(std::is_sorted(fmCopy.begin(), fmCopy.end(), selfbuilt_value_compare)); + QVERIFY(std::is_sorted(fmCopy.begin(), fmCopy.end(), value_compare)); + + Map fmMove{ + Map::key_container_type{ 6, 2, 1 }, + Map::mapped_container_type{ "foo", "bar", "baz" } + }; + QCOMPARE(fmMove.size(), Map::size_type(3)); + QVERIFY(std::is_sorted(fmMove.begin(), fmMove.end(), value_compare)); + + auto fmInitList = Map{ { 1, 2 }, { "foo", "bar" } }; + QVERIFY(std::is_sorted(fmInitList.begin(), fmInitList.end(), value_compare)); + + auto fmRange = Map(fmCopy.begin(), fmCopy.end()); + QVERIFY(std::is_sorted(fmRange.begin(), fmRange.end(), value_compare)); + + kv.clear(); + mv.clear(); + std::vector<Map::value_type> sv; + for (auto it = fmRange.begin(); it != fmRange.end(); ++it) { + kv.push_back(it->first); + mv.push_back(it->second); + sv.push_back(*it); + } + auto fmFromSortedVectorCopy = Map(Qt::OrderedUniqueRange, kv, mv); + auto fmFromSortedVectorMove = Map(Qt::OrderedUniqueRange, Map::key_container_type(kv), + Map::mapped_container_type(mv)); + auto fmFromSortedInitList = Map(Qt::OrderedUniqueRange, { { 1, "foo" }, { 2, "bar" } }); + auto fmFromSortedRange = Map(Qt::OrderedUniqueRange, sv.begin(), sv.end()); +} + +void tst_QFlatMap::constAccess() +{ + using Map = QFlatMap<QByteArray, QByteArray>; + const Map m{ { { "foo", "FOO" }, { "bar", "BAR" } } }; + + const std::vector<Map::value_type> v{ { "foo", "FOO" }, { "bar", "BAR" } }; + + QCOMPARE(m.value("foo").data(), "FOO"); + QCOMPARE(m.value("bar").data(), "BAR"); + QCOMPARE(m.value("nix"), QByteArray()); + QCOMPARE(m.value("nix", "NIX").data(), "NIX"); + QCOMPARE(m["foo"].data(), "FOO"); + QCOMPARE(m["bar"].data(), "BAR"); + QCOMPARE(m["nix"], QByteArray()); + QVERIFY(m.contains("foo")); + QVERIFY(!m.contains("nix")); +} + +void tst_QFlatMap::insertion() +{ + using Map = QFlatMap<QByteArray, QByteArray>; + Map m; + QByteArray foo = "foo"; + m[foo] = foo.toUpper(); + m["bar"] = "BAR"; + m["baz"] = "BAZ"; + QVERIFY(m.insert("oof", "eek").second); + QVERIFY(!m.insert("oof", "OOF").second); + const std::vector<Map::value_type> container = { { "bla", "BLA" }, { "blubb", "BLUBB" } }; + m.insert(container.begin(), container.end()); + QCOMPARE(m.value("foo").data(), "FOO"); + QCOMPARE(m.value("bar").data(), "BAR"); + QCOMPARE(m.value("baz").data(), "BAZ"); + QCOMPARE(m.value("oof").data(), "eek"); + QCOMPARE(m.value("bla").data(), "BLA"); + QCOMPARE(m.value("blubb").data(), "BLUBB"); + + Map::value_type a1[] = { { "narf", "NARF" }, + { "zort", "ZORT" }, + { "troz", "TROZ" } }; + Map::value_type a2[] = { { "gnampf", "GNAMPF" }, + { "narf", "NARFFFF" }, + { "narf", "NARFFFFF" }, + { "narf", "NARFFFFFF" } }; + m.insert(std::begin(a1), std::end(a1)); + m.insert(Qt::OrderedUniqueRange, std::begin(a2), std::end(a2)); + QCOMPARE(m.size(), 10); + QCOMPARE(m.value("narf").data(), "NARF"); + QCOMPARE(m.value("gnampf").data(), "GNAMPF"); +} + +void tst_QFlatMap::insertRValuesAndLValues() +{ + using Map = QFlatMap<QByteArray, QByteArray>; + const QByteArray foo = QByteArrayLiteral("foo"); + const QByteArray bar = QByteArrayLiteral("bar"); + + auto rvalue = [](const QByteArray &ba) { return ba; }; +#define lvalue(x) x + + { + Map m; + QVERIFY( m.insert(lvalue(foo), lvalue(bar)).second); + QVERIFY(!m.insert(lvalue(foo), lvalue(bar)).second); + } + + { + Map m; + QVERIFY( m.insert(lvalue(foo), rvalue(bar)).second); + QVERIFY(!m.insert(lvalue(foo), rvalue(bar)).second); + } + + { + Map m; + QVERIFY( m.insert(rvalue(foo), lvalue(bar)).second); + QVERIFY(!m.insert(rvalue(foo), lvalue(bar)).second); + } + + { + Map m; + QVERIFY( m.insert(rvalue(foo), rvalue(bar)).second); + QVERIFY(!m.insert(rvalue(foo), rvalue(bar)).second); + } + +#undef lvalue +} + +void tst_QFlatMap::extraction() +{ + using Map = QFlatMap<int, QByteArray>; + Map::key_container_type expectedKeys = { 1, 2, 3 }; + Map::mapped_container_type expectedValues = { "een", "twee", "dree" }; + Map m(Qt::OrderedUniqueRange, expectedKeys, expectedValues); + auto keys = m.keys(); + auto values = m.values(); + QCOMPARE(keys, expectedKeys); + QCOMPARE(values, expectedValues); + Map::containers c = std::move(m).extract(); + QCOMPARE(c.keys, expectedKeys); + QCOMPARE(c.values, expectedValues); +} + +void tst_QFlatMap::iterators() +{ + using Map = QFlatMap<int, QByteArray>; + auto m = Map{ Qt::OrderedUniqueRange, { { 1, "foo" }, { 2, "bar" }, { 3, "baz" } } }; + { + // forward / backward + Map::iterator a = m.begin(); + QVERIFY(a != m.end()); + QCOMPARE(a.key(), 1); + QCOMPARE(a.value(), "foo"); + ++a; + QCOMPARE(a.key(), 2); + QCOMPARE(a.value(), "bar"); + Map::iterator b = a++; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(3, "baz")); + QCOMPARE(std::tie(b.key(), b.value()), std::make_tuple(2, "bar")); + QCOMPARE(++a, m.end()); + --a; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(3, "baz")); + a.value() = "buzz"; + b = a--; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(2, "bar")); + QCOMPARE(std::tie(b.key(), b.value()), std::make_tuple(3, "buzz")); + b.value() = "baz"; + + // random access + a = m.begin(); + a += 2; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(3, "baz")); + a = m.begin() + 1; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(2, "bar")); + a = 1 + m.begin(); + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(2, "bar")); + a = m.end() - 1; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(3, "baz")); + b = m.end(); + b -= 1; + QCOMPARE(std::tie(b.key(), b.value()), std::make_tuple(3, "baz")); + QCOMPARE(m.end() - m.begin(), m.size()); + + // comparison + a = m.begin() + m.size() - 1; + b = m.end() - 1; + QVERIFY(a == b); + a = m.begin(); + b = m.end(); + QVERIFY(a < b); + QVERIFY(a <= b); + QVERIFY(b > a); + QVERIFY(b >= a); + a = b; + QVERIFY(!(a < b)); + QVERIFY(a <= b); + QVERIFY(!(b > a)); + QVERIFY(b >= a); + + // de-referencing + a = m.begin(); + auto ref0 = *a; + QCOMPARE(ref0.first, 1); + QCOMPARE(ref0.second, "foo"); + auto ref1 = a[1]; + QCOMPARE(ref1.first, 2); + QCOMPARE(ref1.second, "bar"); + } + { + // forward / backward + Map::const_iterator a = m.cbegin(); + QVERIFY(a != m.cend()); + QCOMPARE(a.key(), 1); + QCOMPARE(a.value(), "foo"); + ++a; + QCOMPARE(a.key(), 2); + QCOMPARE(a.value(), "bar"); + Map::const_iterator b = a++; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(3, "baz")); + QCOMPARE(std::tie(b.key(), b.value()), std::make_tuple(2, "bar")); + QCOMPARE(++a, m.cend()); + --a; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(3, "baz")); + b = a--; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(2, "bar")); + + // random access + a = m.cbegin(); + a += 2; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(3, "baz")); + a = m.cbegin() + 1; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(2, "bar")); + a = 1 + m.cbegin(); + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(2, "bar")); + a = m.cend() - 1; + QCOMPARE(std::tie(a.key(), a.value()), std::make_tuple(3, "baz")); + b = m.cend(); + b -= 1; + QCOMPARE(std::tie(b.key(), b.value()), std::make_tuple(3, "baz")); + QCOMPARE(m.cend() - m.cbegin(), m.size()); + + // comparison + a = m.cbegin() + m.size() - 1; + b = m.cend() - 1; + QVERIFY(a == b); + a = m.cbegin(); + b = m.cend(); + QVERIFY(a < b); + QVERIFY(a <= b); + QVERIFY(b > a); + QVERIFY(b >= a); + a = b; + QVERIFY(!(a < b)); + QVERIFY(a <= b); + QVERIFY(!(b > a)); + QVERIFY(b >= a); + + // de-referencing + a = m.cbegin(); + auto ref0 = *a; + QCOMPARE(ref0.first, 1); + QCOMPARE(ref0.second, "foo"); + auto ref1 = a[1]; + QCOMPARE(ref1.first, 2); + QCOMPARE(ref1.second, "bar"); + } + { + Map::iterator it = m.begin(); + Map::const_iterator cit = it; + Q_UNUSED(it); + Q_UNUSED(cit); + } + { + std::list<Map::value_type> revlst; + std::copy(m.begin(), m.end(), std::front_inserter(revlst)); + std::vector<Map::value_type> v0; + std::copy(revlst.begin(), revlst.end(), std::back_inserter(v0)); + std::vector<Map::value_type> v1; + std::copy(m.rbegin(), m.rend(), std::back_inserter(v1)); + const Map cm = m; + std::vector<Map::value_type> v2; + std::copy(cm.rbegin(), cm.rend(), std::back_inserter(v2)); + std::vector<Map::value_type> v3; + std::copy(m.crbegin(), m.crend(), std::back_inserter(v3)); + QCOMPARE(v0, v1); + QCOMPARE(v1, v2); + QCOMPARE(v2, v3); + } +} + +template <typename Pred> +void tst_QFlatMap::remove_if_impl(Pred p, bool removeNonEmptyValues) +{ + // empty stays empty: + { + QFlatMap<int, QString> m; + QCOMPARE(m.remove_if(p), 0); + QVERIFY(m.isEmpty()); + } + // a matching element is removed: + { + { + QFlatMap<int, QString> m; + m.insert_or_assign(0, ""); + QCOMPARE(m.remove_if(p), 1); + QVERIFY(m.isEmpty()); + } + if (removeNonEmptyValues) { + QFlatMap<int, QString> m; + m.insert_or_assign(0, "x"); + QCOMPARE(m.remove_if(p), 1); + QVERIFY(m.isEmpty()); + } + } + // a non-matching element is not removed: + { + { + QFlatMap<int, QString> m; + m.insert_or_assign(1, ""); + QCOMPARE(m.remove_if(p), 0); + QVERIFY(m.contains(1)); + QVERIFY(m[1].isEmpty()); + } + if (removeNonEmptyValues) { + QFlatMap<int, QString> m; + m.insert_or_assign(1, "x"); + QCOMPARE(m.remove_if(p), 0); + QVERIFY(m.contains(1)); + QCOMPARE(m[1], "x"); + } + } + // of matching and non-matching elements, only matching ones are removed: + { + { + QFlatMap<int, QString> m; + m.insert_or_assign(0, ""); + m.insert_or_assign(1, ""); + const auto copy = m; + QCOMPARE(m.remove_if(p), 1); + QCOMPARE(copy.size(), 2); + QCOMPARE(copy[0], ""); + QCOMPARE(copy[1], ""); + QCOMPARE(m.size(), 1); + QVERIFY(m.contains(1)); + QVERIFY(m[1].isEmpty()); + } + { + QFlatMap<int, QString> m; + m.insert_or_assign(1, ""); + m.insert_or_assign(2, ""); + QCOMPARE(m.remove_if(p), 1); + QCOMPARE(m.size(), 1); + QVERIFY(m.contains(1)); + QVERIFY(m[1].isEmpty()); + } + } +} + +void tst_QFlatMap::removal() +{ + using Map = QFlatMap<int, QByteArray>; + Map m({ { 2, "bar" }, { 3, "baz" }, { 1, "foo" } }); + QCOMPARE(m.value(2).data(), "bar"); + QCOMPARE(m.take(2).data(), "bar"); + QVERIFY(!m.contains(2)); + QCOMPARE(m.size(), Map::size_type(2)); + QVERIFY(m.remove(1)); + QVERIFY(!m.contains(1)); + QVERIFY(!m.remove(1)); + QCOMPARE(m.size(), Map::size_type(1)); + m.clear(); + QVERIFY(m.isEmpty()); + QVERIFY(m.empty()); + + m[1] = "een"; + m[2] = "twee"; + m[3] = "dree"; + auto it = m.lower_bound(1); + QCOMPARE(it.key(), 1); + it = m.erase(it); + QCOMPARE(it.key(), 2); + QVERIFY(!m.contains(1)); +} + +void tst_QFlatMap::statefulComparator() +{ + struct CountingCompare { + mutable int count = 0; + + bool operator()(const QString &lhs, const QString &rhs) const + { + ++count; + return lhs < rhs; + } + }; + + using Map = QFlatMap<QString, QString, CountingCompare>; + auto m1 = Map{ { "en", "een"}, { "to", "twee" }, { "tre", "dree" } }; + QVERIFY(m1.key_comp().count > 0); + auto m2 = Map(m1.key_comp()); + QCOMPARE(m2.key_comp().count, m1.key_comp().count); + m2.insert(m1.begin(), m1.end()); + QVERIFY(m2.key_comp().count > m1.key_comp().count); +} + +void tst_QFlatMap::transparency_using() +{ + struct StringViewCompare + { + using is_transparent [[maybe_unused]] = void; + bool operator()(QAnyStringView lhs, QAnyStringView rhs) const + { + return lhs < rhs; + } + }; + transparency_impl<StringViewCompare>(); +} + +void tst_QFlatMap::transparency_struct() +{ + struct StringViewCompare + { + struct is_transparent {}; + bool operator()(QAnyStringView lhs, QAnyStringView rhs) const + { + return lhs < rhs; + } + }; + transparency_impl<StringViewCompare>(); +} + +template <typename StringViewCompare> +void tst_QFlatMap::transparency_impl() +{ + using Map = QFlatMap<QString, QString, StringViewCompare>; + auto m = Map{ { "one", "een" }, { "two", "twee" }, { "three", "dree" } }; + + const QString numbers = "one two three"; + const QStringView sv1{numbers.constData(), 3}; + const QStringView sv2{numbers.constData() + 4, 3}; + const QStringView sv3{numbers.constData() + 8, 5}; + QCOMPARE(m.lower_bound(sv1).value(), "een"); + QCOMPARE(m.value(sv1), "een"); + QCOMPARE(m.lower_bound(sv2).value(), "twee"); + QCOMPARE(m.value(sv2), "twee"); + QCOMPARE(m.lower_bound(sv3).value(), "dree"); + QCOMPARE(m.value(sv3), "dree"); + + QVERIFY(m.contains(sv2)); + auto twee = m.take(sv2); + static_assert(std::is_same_v<decltype(twee), QString>); + QCOMPARE(twee, "twee"); + QVERIFY(!m.contains(sv2)); + + QVERIFY(m.contains(QLatin1String("one"))); + QVERIFY(m.remove(QAnyStringView(u8"one"))); + QVERIFY(!m.contains(QLatin1String("one"))); +} + +void tst_QFlatMap::try_emplace_and_insert_or_assign() +{ + using Map = QFlatMap<QByteArray, QByteArray>; + + const QByteArray foo = QByteArrayLiteral("foo"); + const qsizetype qqq_1 = 3; + const char qqq_2 = 'q'; + const QByteArray qqq = QByteArray(qqq_1, qqq_2); + + auto sb = [] (const auto &str) { return str % ""; }; + auto rvalue = [](const auto &x) { return x; }; +#define lvalue(x) x +#define CHECKS() \ + do { \ + QVERIFY(!m.try_emplace(rvalue(foo), lvalue(foo)).second); \ + QCOMPARE(m.value(foo), qqq); \ + QVERIFY(!m.try_emplace(lvalue(foo), lvalue(foo)).second); \ + QCOMPARE(m.value(foo), qqq); \ + QVERIFY(!m.try_emplace(lvalue(foo), sb(foo)).second); \ + QCOMPARE(m.value(foo), qqq); \ + QVERIFY(!m.try_emplace(rvalue(foo), sb(foo)).second); \ + QCOMPARE(m.value(foo), qqq); \ + } while (0) \ + /* end */ + + { + Map m; + QVERIFY(m.try_emplace(lvalue(foo), lvalue(qqq)).second); + CHECKS(); + QVERIFY(!m.insert_or_assign(lvalue(foo), lvalue(foo)).second); + QCOMPARE(m.value(foo), foo); + } + + { + Map m; + QVERIFY(m.insert_or_assign(lvalue(foo), lvalue(qqq)).second); + CHECKS(); + QVERIFY(!m.try_emplace(lvalue(foo), lvalue(foo)).second); + QCOMPARE(m.value(foo), qqq); + } + + { + Map m; + QVERIFY(m.try_emplace(lvalue(foo), rvalue(qqq)).second); + CHECKS(); + QVERIFY(!m.insert_or_assign(lvalue(foo), rvalue(foo)).second); + QCOMPARE(m.value(foo), foo); + } + + { + Map m; + QVERIFY(m.insert_or_assign(lvalue(foo), rvalue(qqq)).second); + CHECKS(); + QVERIFY(!m.try_emplace(lvalue(foo), rvalue(foo)).second); + QCOMPARE(m.value(foo), qqq); + } + + { + Map m; + QVERIFY(m.try_emplace(lvalue(foo), qqq_1, qqq_2).second); + QCOMPARE(m.value(foo), qqq); + CHECKS(); + } + + { + Map m; + QVERIFY(m.try_emplace(lvalue(foo), sb(qqq)).second); + QCOMPARE(m.value(foo), qqq); + CHECKS(); + QVERIFY(!m.insert_or_assign(lvalue(foo), sb(foo)).second); + QCOMPARE(m.value(foo), foo); + } + + { + Map m; + QVERIFY(m.insert_or_assign(lvalue(foo), sb(qqq)).second); + QCOMPARE(m.value(foo), qqq); + CHECKS(); + QVERIFY(!m.try_emplace(lvalue(foo), sb(foo)).second); + QCOMPARE(m.value(foo), qqq); + } + + { + Map m; + QVERIFY(m.try_emplace(rvalue(foo), lvalue(qqq)).second); + CHECKS(); + QVERIFY(!m.insert_or_assign(rvalue(foo), lvalue(foo)).second); + QCOMPARE(m.value(foo), foo); + } + + { + Map m; + QVERIFY(m.insert_or_assign(rvalue(foo), lvalue(qqq)).second); + CHECKS(); + QVERIFY(!m.try_emplace(rvalue(foo), lvalue(foo)).second); + QCOMPARE(m.value(foo), qqq); + } + + { + Map m; + QVERIFY(m.try_emplace(rvalue(foo), rvalue(qqq)).second); + CHECKS(); + QVERIFY(!m.insert_or_assign(rvalue(foo), rvalue(foo)).second); + QCOMPARE(m.value(foo), foo); + } + + { + Map m; + QVERIFY(m.insert_or_assign(rvalue(foo), rvalue(qqq)).second); + CHECKS(); + QVERIFY(!m.try_emplace(rvalue(foo), rvalue(foo)).second); + QCOMPARE(m.value(foo), qqq); + } + + { + Map m; + QVERIFY(m.try_emplace(rvalue(foo), qqq_1, qqq_2).second); + QCOMPARE(m.value(foo), qqq); + CHECKS(); + } + + { + Map m; + QVERIFY(m.try_emplace(rvalue(foo), sb(qqq)).second); + QCOMPARE(m.value(foo), qqq); + CHECKS(); + QVERIFY(!m.insert_or_assign(rvalue(foo), sb(foo)).second); + QCOMPARE(m.value(foo), foo); + } + + { + Map m; + QVERIFY(m.insert_or_assign(rvalue(foo), sb(qqq)).second); + QCOMPARE(m.value(foo), qqq); + CHECKS(); + QVERIFY(!m.try_emplace(rvalue(foo), sb(foo)).second); + QCOMPARE(m.value(foo), qqq); + } +#undef CHECKS +#undef lvalue +} + +void tst_QFlatMap::viewIterators() +{ + using Map = QFlatMap<QByteArray, QByteArray>; + Map m({ { "yksi", "een"}, { "kaksi", "twee" }, { "kolme", "dree" } }); + { + std::vector<QByteArray> keys; + std::transform(m.begin(), m.end(), std::back_inserter(keys), + [](const Map::value_type &v) + { + return v.first; + }); + auto it = keys.begin(); + QCOMPARE(*it, "kaksi"); + QCOMPARE(it->size(), 5); + ++it; + QCOMPARE(*it, "kolme"); + it++; + QCOMPARE(*it, "yksi"); + ++it; + QCOMPARE(it, keys.end()); + --it; + QCOMPARE(*it, "yksi"); + it--; + QCOMPARE(*it, "kolme"); + } + { + std::vector<QByteArray> values; + std::transform(m.begin(), m.end(), std::back_inserter(values), + [](const Map::value_type &v) + { + return v.second; + }); + auto it = values.begin(); + QCOMPARE(*it, "twee"); + QCOMPARE(it->size(), 4); + ++it; + QCOMPARE(*it, "dree"); + it++; + QCOMPARE(*it, "een"); + ++it; + QCOMPARE(it, values.end()); + --it; + QCOMPARE(*it, "een"); + it--; + QCOMPARE(*it, "dree"); + } +} + +void tst_QFlatMap::varLengthArray() +{ + using Map = QVarLengthFlatMap<int, QByteArray, 1024>; + Map m(Qt::OrderedUniqueRange, { { 2, "twee" } }); + m.insert_or_assign(1, "een"); + m.remove(1); + QVERIFY(!m.isEmpty()); + m.remove(2); + QVERIFY(m.isEmpty()); +} + +QTEST_APPLESS_MAIN(tst_QFlatMap) +#include "tst_qflatmap.moc" diff --git a/tests/auto/corelib/tools/qfreelist/CMakeLists.txt b/tests/auto/corelib/tools/qfreelist/CMakeLists.txt new file mode 100644 index 0000000000..a37d3131f5 --- /dev/null +++ b/tests/auto/corelib/tools/qfreelist/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qfreelist Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfreelist LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qfreelist + SOURCES + tst_qfreelist.cpp + LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/tools/qfreelist/qfreelist.pro b/tests/auto/corelib/tools/qfreelist/qfreelist.pro deleted file mode 100644 index 4825987bcf..0000000000 --- a/tests/auto/corelib/tools/qfreelist/qfreelist.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qfreelist -QT = core-private testlib -SOURCES = tst_qfreelist.cpp -!qtConfig(private_tests): SOURCES += $$QT_SOURCE_TREE/src/corelib/tools/qfreelist.cpp diff --git a/tests/auto/corelib/tools/qfreelist/tst_qfreelist.cpp b/tests/auto/corelib/tools/qfreelist/tst_qfreelist.cpp index a09bcd5121..a45fa6d400 100644 --- a/tests/auto/corelib/tools/qfreelist/tst_qfreelist.cpp +++ b/tests/auto/corelib/tools/qfreelist/tst_qfreelist.cpp @@ -1,38 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ - +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QCoreApplication> #include <QtCore/QElapsedTimer> #include <QtCore/QList> #include <QtCore/QThread> #include <private/qfreelist_p.h> -#include <QtTest/QtTest> +#include <QTest> class tst_QFreeList : public QObject { @@ -120,7 +94,7 @@ class FreeListThread : public QThread public: inline FreeListThread() : QThread() { } - inline void run() + inline void run() override { QElapsedTimer t; t.start(); @@ -141,7 +115,7 @@ public: needToRelease << i; } while (t.elapsed() < TimeLimit); - foreach (int x, needToRelease) + for (int x : std::as_const(needToRelease)) freelist.release(x); } }; diff --git a/tests/auto/corelib/tools/qhash/CMakeLists.txt b/tests/auto/corelib/tools/qhash/CMakeLists.txt new file mode 100644 index 0000000000..8702b8bf23 --- /dev/null +++ b/tests/auto/corelib/tools/qhash/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qhash Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qhash LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qhash + SOURCES + tst_qhash.cpp +) + +qt_internal_undefine_global_definition(tst_qhash QT_NO_JAVA_STYLE_ITERATORS) diff --git a/tests/auto/corelib/tools/qhash/qhash.pro b/tests/auto/corelib/tools/qhash/qhash.pro deleted file mode 100644 index e96c0d1bf1..0000000000 --- a/tests/auto/corelib/tools/qhash/qhash.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qhash -QT = core testlib -SOURCES = $$PWD/tst_qhash.cpp - -DEFINES -= QT_NO_JAVA_STYLE_ITERATORS diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index 91cd3eb0bd..b3dbdfa40c 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -1,38 +1,22 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include <QTest> + +#include <qdebug.h> #include <qhash.h> #include <qmap.h> +#include <qscopeguard.h> +#include <qset.h> #include <algorithm> #include <vector> +#include <unordered_set> +#include <string> + +#include <qsemaphore.h> + +using namespace Qt::StringLiterals; class tst_QHash : public QObject { @@ -40,7 +24,9 @@ class tst_QHash : public QObject private slots: void insert1(); void erase(); + void erase_edge_case(); void key(); + void keys(); void swap(); void count(); // copied from tst_QMap @@ -52,17 +38,36 @@ private slots: void qhash(); void take(); // copied from tst_QMap void operator_eq(); // slightly modified from tst_QMap + void heterogeneousSearch(); + void heterogeneousSearchConstKey(); + void heterogeneousSearchByteArray(); + void heterogeneousSearchString(); + void heterogeneousSearchLatin1String(); + void rehash_isnt_quadratic(); void dont_need_default_constructor(); void qmultihash_specific(); + void qmultihash_qhash_rvalue_ref_ctor(); + void qmultihash_qhash_rvalue_ref_unite(); + void qmultihashUnite(); + void qmultihashSize(); + void qmultihashHeterogeneousSearch(); + void qmultihashHeterogeneousSearchConstKey(); + void qmultihashHeterogeneousSearchByteArray(); + void qmultihashHeterogeneousSearchString(); + void qmultihashHeterogeneousSearchLatin1String(); void compare(); void compare2(); void iterators(); // sligthly modified from tst_QMap + void multihashIterators(); + void iteratorsInEmptyHash(); void keyIterator(); + void multihashKeyIterator(); void keyValueIterator(); + void multihashKeyValueIterator(); + void keyValueIteratorInEmptyHash(); void keys_values_uniqueKeys(); // slightly modified from tst_QMap - void noNeedlessRehashes(); void const_shared_null(); void twoArguments_qHash(); @@ -70,13 +75,40 @@ private slots: void eraseValidIteratorOnSharedHash(); void equal_range(); void insert_hash(); + void multiHashStoresInReverseInsertionOrder(); + + void emplace(); + + void badHashFunction(); + void hashOfHash(); + + void stdHash(); + + void countInEmptyHash(); + void removeInEmptyHash(); + void valueInEmptyHash(); + void fineTuningInEmptyHash(); + + void reserveShared(); + void reserveLessThanCurrentAmount(); + void reserveKeepCapacity_data(); + void reserveKeepCapacity(); + + void QTBUG98265(); + + void detachAndReferences(); + + void lookupUsingKeyIterator(); + + void squeeze(); + void squeezeShared(); }; struct IdentityTracker { int value, id; }; -inline uint qHash(IdentityTracker key) { return qHash(key.value); } +inline size_t qHash(IdentityTracker key) { return qHash(key.value); } inline bool operator==(IdentityTracker lhs, IdentityTracker rhs) { return lhs.value == rhs.value; } @@ -85,6 +117,8 @@ struct Foo { Foo():c(count) { ++count; } Foo(const Foo& o):c(o.c) { ++count; } ~Foo() { --count; } + constexpr Foo &operator=(const Foo &o) noexcept { c = o.c; return *this; } + int c; int data[8]; }; @@ -95,26 +129,53 @@ int Foo::count = 0; class MyClass { public: - MyClass() { ++count; + MyClass() + { + ++count; } - MyClass( const QString& c) { - count++; str = c; + MyClass( const QString& c) + { + count++; + str = c; + } + MyClass(const QString &a, const QString &b) + { + count++; + str = a + b; } ~MyClass() { count--; } MyClass( const MyClass& c ) { - count++; str = c.str; + count++; + ++copies; + str = c.str; } MyClass &operator =(const MyClass &o) { - str = o.str; return *this; + str = o.str; + ++copies; + return *this; + } + MyClass(MyClass &&c) { + count++; + ++moves; + str = c.str; + } + MyClass &operator =(MyClass &&o) { + str = o.str; + ++moves; + return *this; } QString str; static int count; + static int copies; + static int moves; }; -int MyClass::count = 0; +int MyClass::count = 0; +int MyClass::copies = 0; +int MyClass::moves = 0; typedef QHash<QString, MyClass> MyMap; @@ -124,13 +185,13 @@ void tst_QHash::count() { MyMap map; MyMap map2( map ); - QCOMPARE( map.count(), 0 ); - QCOMPARE( map2.count(), 0 ); + QCOMPARE( map.size(), 0 ); + QCOMPARE( map2.size(), 0 ); QCOMPARE( MyClass::count, 0 ); // detach map2["Hallo"] = MyClass( "Fritz" ); - QCOMPARE( map.count(), 0 ); - QCOMPARE( map2.count(), 1 ); + QCOMPARE( map.size(), 0 ); + QCOMPARE( map2.size(), 1 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 1 ); #endif @@ -140,11 +201,11 @@ void tst_QHash::count() { typedef QHash<QString, MyClass> Map; Map map; - QCOMPARE( map.count(), 0); + QCOMPARE( map.size(), 0); map.insert( "Torben", MyClass("Weis") ); - QCOMPARE( map.count(), 1 ); + QCOMPARE( map.size(), 1 ); map.insert( "Claudia", MyClass("Sorg") ); - QCOMPARE( map.count(), 2 ); + QCOMPARE( map.size(), 2 ); map.insert( "Lars", MyClass("Linzbach") ); map.insert( "Matthias", MyClass("Ettrich") ); map.insert( "Sue", MyClass("Paludo") ); @@ -152,7 +213,7 @@ void tst_QHash::count() map.insert( "Haavard", MyClass("Nord") ); map.insert( "Arnt", MyClass("Gulbrandsen") ); map.insert( "Paul", MyClass("Tvete") ); - QCOMPARE( map.count(), 9 ); + QCOMPARE( map.size(), 9 ); map.insert( "Paul", MyClass("Tvete 1") ); map.insert( "Paul", MyClass("Tvete 2") ); map.insert( "Paul", MyClass("Tvete 3") ); @@ -160,68 +221,68 @@ void tst_QHash::count() map.insert( "Paul", MyClass("Tvete 5") ); map.insert( "Paul", MyClass("Tvete 6") ); - QCOMPARE( map.count(), 9 ); + QCOMPARE( map.size(), 9 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif Map map2( map ); - QVERIFY( map2.count() == 9 ); + QVERIFY( map2.size() == 9 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif map2.insert( "Kay", MyClass("Roemer") ); - QVERIFY( map2.count() == 10 ); - QVERIFY( map.count() == 9 ); + QVERIFY( map2.size() == 10 ); + QVERIFY( map.size() == 9 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 19 ); #endif map2 = map; - QVERIFY( map.count() == 9 ); - QVERIFY( map2.count() == 9 ); + QVERIFY( map.size() == 9 ); + QVERIFY( map2.size() == 9 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif map2.insert( "Kay", MyClass("Roemer") ); - QVERIFY( map2.count() == 10 ); + QVERIFY( map2.size() == 10 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 19 ); #endif map2.clear(); - QVERIFY( map.count() == 9 ); - QVERIFY( map2.count() == 0 ); + QVERIFY( map.size() == 9 ); + QVERIFY( map2.size() == 0 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif map2 = map; - QVERIFY( map.count() == 9 ); - QVERIFY( map2.count() == 9 ); + QVERIFY( map.size() == 9 ); + QVERIFY( map2.size() == 9 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif map2.clear(); - QVERIFY( map.count() == 9 ); - QVERIFY( map2.count() == 0 ); + QVERIFY( map.size() == 9 ); + QVERIFY( map2.size() == 0 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif map.remove( "Lars" ); - QVERIFY( map.count() == 8 ); - QVERIFY( map2.count() == 0 ); + QVERIFY( map.size() == 8 ); + QVERIFY( map2.size() == 0 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 8 ); #endif map.remove( "Mist" ); - QVERIFY( map.count() == 8 ); - QVERIFY( map2.count() == 0 ); + QVERIFY( map.size() == 8 ); + QVERIFY( map2.size() == 0 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 8 ); #endif @@ -235,22 +296,22 @@ void tst_QHash::count() #ifndef Q_CC_SUN QVERIFY( MyClass::count == 1 ); #endif - QVERIFY( map.count() == 1 ); + QVERIFY( map.size() == 1 ); (void)map["Torben"].str; (void)map["Lars"].str; #ifndef Q_CC_SUN QVERIFY( MyClass::count == 2 ); #endif - QVERIFY( map.count() == 2 ); + QVERIFY( map.size() == 2 ); const Map& cmap = map; (void)cmap["Depp"].str; #ifndef Q_CC_SUN QVERIFY( MyClass::count == 2 ); #endif - QVERIFY( map.count() == 2 ); - QVERIFY( cmap.count() == 2 ); + QVERIFY( map.size() == 2 ); + QVERIFY( cmap.size() == 2 ); } QCOMPARE( MyClass::count, 0 ); { @@ -276,17 +337,17 @@ void tst_QHash::insert1() Hash hash; QString key = QLatin1String(" "); for (int i = 0; i < 10; ++i) { - key[0] = i + '0'; + key[0] = QChar(i + '0'); for (int j = 0; j < 10; ++j) { - key[1] = j + '0'; + key[1] = QChar(j + '0'); hash.insert(key, "V" + key); } } for (int i = 0; i < 10; ++i) { - key[0] = i + '0'; + key[0] = QChar(i + '0'); for (int j = 0; j < 10; ++j) { - key[1] = j + '0'; + key[1] = QChar(j + '0'); hash.remove(key); } } @@ -301,6 +362,8 @@ void tst_QHash::insert1() QVERIFY(hash.size() == 2); QVERIFY(!hash.isEmpty()); +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded") { Hash hash2 = hash; hash2 = hash; @@ -313,6 +376,7 @@ void tst_QHash::insert1() QVERIFY(hash2.isEmpty()); } QVERIFY(hash.size() == 2); +QT_WARNING_POP { Hash hash2 = hash; @@ -442,6 +506,7 @@ void tst_QHash::insert1() { QHash<IdentityTracker, int> hash; QCOMPARE(hash.size(), 0); + QVERIFY(!hash.isDetached()); const int dummy = -1; IdentityTracker id00 = {0, 0}, id01 = {0, 1}, searchKey = {0, dummy}; QCOMPARE(hash.insert(id00, id00.id).key().id, id00.id); @@ -451,20 +516,6 @@ void tst_QHash::insert1() QCOMPARE(hash.find(searchKey).value(), id01.id); // last-inserted value QCOMPARE(hash.find(searchKey).key().id, id00.id); // but first-inserted key } - { - QMultiHash<IdentityTracker, int> hash; - QCOMPARE(hash.size(), 0); - const int dummy = -1; - IdentityTracker id00 = {0, 0}, id01 = {0, 1}, searchKey = {0, dummy}; - QCOMPARE(hash.insert(id00, id00.id).key().id, id00.id); - QCOMPARE(hash.size(), 1); - QCOMPARE(hash.insert(id01, id01.id).key().id, id01.id); - QCOMPARE(hash.size(), 2); - QMultiHash<IdentityTracker, int>::const_iterator pos = hash.constFind(searchKey); - QCOMPARE(pos.value(), pos.key().id); // key fits to value it was inserted with - ++pos; - QCOMPARE(pos.value(), pos.key().id); // key fits to value it was inserted with - } } void tst_QHash::erase() @@ -491,13 +542,75 @@ void tst_QHash::erase() ++n; } QVERIFY(n == 3); - QHash<int, int> h2; - h2.insertMulti(20, 41); - h2.insertMulti(20, 42); + + QMultiHash<int, int> h2; + h2.insert(20, 41); + h2.insert(20, 42); QVERIFY(h2.size() == 2); - it1 = h2.erase(h2.begin()); - it1 = h2.erase(h2.begin()); - QVERIFY(it1 == h2.end()); + auto bit = h2.begin(); + auto mit = h2.erase(bit); + mit = h2.erase(h2.begin()); + QVERIFY(mit == h2.end()); + + h2 = QMultiHash<int, int>(); + h2.emplace(1, 1); + h2.emplace(1, 2); + h2.emplace(3, 1); + h2.emplace(3, 4); + QMultiHash<int, int> h3 = h2; + auto it = h3.constFind(3); + ++it; + QVERIFY(h3.isSharedWith(h2)); + it = h3.erase(it); + QVERIFY(!h3.isSharedWith(h2)); + if (it != h3.cend()) { + auto it2 = h3.constFind(it.key()); + QCOMPARE(it, it2); + } +} + +/* + With a specific seed we could end up in a situation where, upon deleting the + last entry in a QHash, the returned iterator would not point to the end() + iterator. +*/ +void tst_QHash::erase_edge_case() +{ + QHashSeed::setDeterministicGlobalSeed(); + auto resetSeed = qScopeGuard([&]() { + QHashSeed::resetRandomGlobalSeed(); + }); + + QHash<int, int> h1; + h1.reserve(2); + qsizetype capacity = h1.capacity(); + // Beholden to QHash internals: + qsizetype numBuckets = capacity << 1; + + // Find some keys which will both be slotted into the last bucket: + int keys[2]; + int index = 0; + for (qsizetype i = 0; i < numBuckets * 4 && index < 2; ++i) { + const size_t hash = qHash(i, QHashSeed::globalSeed()); + const size_t bucketForHash = QHashPrivate::GrowthPolicy::bucketForHash(numBuckets, hash); + if (qsizetype(bucketForHash) == numBuckets - 1) + keys[index++] = i; + } + QCOMPARE(index, 2); // Sanity check. If this fails then the test needs an update! + + // As mentioned earlier these are both calculated to be in the last bucket: + h1.insert(keys[0], 4); + h1.insert(keys[1], 6); + // As a sanity-check, make sure that the key we inserted last is the first one (because its + // allocation to the last bucket would make it wrap around): + // NOTE: If this fails this then this test may need an update!!! + QCOMPARE(h1.constBegin().key(), keys[1]); + // Then we delete the last entry: + QHash<int, int>::iterator it1 = h1.begin(); + ++it1; + it1 = h1.erase(it1); + // Now, since we deleted the last entry, the iterator should be at the end(): + QVERIFY(it1 == h1.end()); } void tst_QHash::key() @@ -508,6 +621,7 @@ void tst_QHash::key() QHash<QString, int> hash1; QCOMPARE(hash1.key(1), QString()); QCOMPARE(hash1.key(1, def), def); + QVERIFY(!hash1.isDetached()); hash1.insert("one", 1); QCOMPARE(hash1.key(1), QLatin1String("one")); @@ -538,6 +652,7 @@ void tst_QHash::key() QHash<int, QString> hash2; QCOMPARE(hash2.key("one"), 0); QCOMPARE(hash2.key("one", def), def); + QVERIFY(!hash2.isDetached()); hash2.insert(1, "one"); QCOMPARE(hash2.key("one"), 1); @@ -569,6 +684,62 @@ void tst_QHash::key() QCOMPARE(hash2.key("zero"), 0); QCOMPARE(hash2.key("zero", def), 0); } + + { + const int def = -1; + QMultiHash<int, QString> hash; + QCOMPARE(hash.key("val"), 0); + QCOMPARE(hash.key("val", def), def); + QVERIFY(!hash.isDetached()); + + hash.insert(1, "value1"); + hash.insert(1, "value2"); + hash.insert(2, "value1"); + + QCOMPARE(hash.key("value2"), 1); + const auto key = hash.key("value1"); + QVERIFY(key == 1 || key == 2); + QCOMPARE(hash.key("value"), 0); + QCOMPARE(hash.key("value", def), def); + } +} + +template <typename T> +QList<T> sorted(const QList<T> &list) +{ + QList<T> res = list; + std::sort(res.begin(), res.end()); + return res; +} + +void tst_QHash::keys() +{ + { + QHash<QString, int> hash; + QVERIFY(hash.keys().isEmpty()); + QVERIFY(hash.keys(1).isEmpty()); + QVERIFY(!hash.isDetached()); + + hash.insert("key1", 1); + hash.insert("key2", 2); + hash.insert("key3", 1); + + QCOMPARE(sorted(hash.keys()), QStringList({ "key1", "key2", "key3" })); + QCOMPARE(sorted(hash.keys(1)), QStringList({ "key1", "key3" })); + } + { + QMultiHash<QString, int> hash; + QVERIFY(hash.keys().isEmpty()); + QVERIFY(hash.keys(1).isEmpty()); + QVERIFY(!hash.isDetached()); + + hash.insert("key1", 1); + hash.insert("key2", 1); + hash.insert("key1", 2); + + QCOMPARE(sorted(hash.keys()), QStringList({ "key1", "key1", "key2" })); + QCOMPARE(sorted(hash.keys(1)), QStringList({ "key1", "key2" })); + } } void tst_QHash::swap() @@ -598,6 +769,25 @@ void tst_QHash::clear() QVERIFY( map.isEmpty() ); } QCOMPARE( MyClass::count, int(0) ); + + { + QMultiHash<QString, MyClass> multiHash; + multiHash.clear(); + QVERIFY(multiHash.isEmpty()); + + multiHash.insert("key", MyClass("value0")); + QVERIFY(!multiHash.isEmpty()); + multiHash.clear(); + QVERIFY(multiHash.isEmpty()); + + multiHash.insert("key0", MyClass("value0")); + multiHash.insert("key0", MyClass("value1")); + multiHash.insert("key1", MyClass("value2")); + QVERIFY(!multiHash.isEmpty()); + multiHash.clear(); + QVERIFY(multiHash.isEmpty()); + } + QCOMPARE(MyClass::count, int(0)); } //copied from tst_QMap void tst_QHash::empty() @@ -605,24 +795,31 @@ void tst_QHash::empty() QHash<int, QString> map1; QVERIFY(map1.isEmpty()); + QVERIFY(map1.empty()); map1.insert(1, "one"); QVERIFY(!map1.isEmpty()); + QVERIFY(!map1.empty()); map1.clear(); QVERIFY(map1.isEmpty()); - + QVERIFY(map1.empty()); } //copied from tst_QMap void tst_QHash::find() { + const QHash<int, QString> constEmptyHash; + QVERIFY(constEmptyHash.find(1) == constEmptyHash.end()); + QVERIFY(!constEmptyHash.isDetached()); + QHash<int, QString> map1; QString testString="Teststring %0"; QString compareString; int i,count=0; QVERIFY(map1.find(1) == map1.end()); + QVERIFY(!map1.isDetached()); map1.insert(1,"Mensch"); map1.insert(1,"Mayer"); @@ -631,14 +828,25 @@ void tst_QHash::find() QCOMPARE(map1.find(1).value(), QLatin1String("Mayer")); QCOMPARE(map1.find(2).value(), QLatin1String("Hej")); - for(i = 3; i < 10; ++i) { + const QMultiHash<int, QString> constEmptyMultiHash; + QVERIFY(constEmptyMultiHash.find(1) == constEmptyMultiHash.cend()); + QVERIFY(constEmptyMultiHash.find(1, "value") == constEmptyMultiHash.cend()); + QVERIFY(!constEmptyMultiHash.isDetached()); + + QMultiHash<int, QString> emptyMultiHash; + QVERIFY(emptyMultiHash.find(1) == emptyMultiHash.end()); + QVERIFY(emptyMultiHash.find(1, "value") == emptyMultiHash.end()); + QVERIFY(!emptyMultiHash.isDetached()); + + QMultiHash<int, QString> multiMap(map1); + for (i = 3; i < 10; ++i) { compareString = testString.arg(i); - map1.insertMulti(4, compareString); + multiMap.insert(4, compareString); } - QHash<int, QString>::const_iterator it=map1.constFind(4); + auto it = multiMap.constFind(4); - for(i = 9; i > 2 && it != map1.constEnd() && it.key() == 4; --i) { + for (i = 9; i > 2 && it != multiMap.constEnd() && it.key() == 4; --i) { compareString = testString.arg(i); QVERIFY(it.value() == compareString); ++it; @@ -656,6 +864,7 @@ void tst_QHash::constFind() int i,count=0; QVERIFY(map1.constFind(1) == map1.constEnd()); + QVERIFY(!map1.isDetached()); map1.insert(1,"Mensch"); map1.insert(1,"Mayer"); @@ -664,14 +873,19 @@ void tst_QHash::constFind() QCOMPARE(map1.constFind(1).value(), QLatin1String("Mayer")); QCOMPARE(map1.constFind(2).value(), QLatin1String("Hej")); - for(i = 3; i < 10; ++i) { + QMultiHash<int, QString> emptyMultiHash; + QVERIFY(emptyMultiHash.constFind(1) == emptyMultiHash.constEnd()); + QVERIFY(!emptyMultiHash.isDetached()); + + QMultiHash<int, QString> multiMap(map1); + for (i = 3; i < 10; ++i) { compareString = testString.arg(i); - map1.insertMulti(4, compareString); + multiMap.insert(4, compareString); } - QHash<int, QString>::const_iterator it=map1.constFind(4); + auto it = multiMap.constFind(4); - for(i = 9; i > 2 && it != map1.constEnd() && it.key() == 4; --i) { + for (i = 9; i > 2 && it != multiMap.constEnd() && it.key() == 4; --i) { compareString = testString.arg(i); QVERIFY(it.value() == compareString); ++it; @@ -686,12 +900,15 @@ void tst_QHash::contains() QHash<int, QString> map1; int i; + QVERIFY(!map1.contains(1)); + QVERIFY(!map1.isDetached()); + map1.insert(1, "one"); QVERIFY(map1.contains(1)); - for(i=2; i < 100; ++i) + for (i=2; i < 100; ++i) map1.insert(i, "teststring"); - for(i=99; i > 1; --i) + for (i=99; i > 1; --i) QVERIFY(map1.contains(i)); map1.remove(43); @@ -704,16 +921,31 @@ class QGlobalQHashSeedResetter int oldSeed; public: // not entirely correct (may lost changes made by another thread between the query - // of the old and the setting of the new seed), but qSetGlobalQHashSeed doesn't + // of the old and the setting of the new seed), but setHashSeed() can't // return the old value, so this is the best we can do: explicit QGlobalQHashSeedResetter(int newSeed) - : oldSeed(qGlobalQHashSeed()) + : oldSeed(getHashSeed()) { - qSetGlobalQHashSeed(newSeed); + setHashSeed(newSeed); } ~QGlobalQHashSeedResetter() { - qSetGlobalQHashSeed(oldSeed); + setHashSeed(oldSeed); + } + +private: + // The functions are implemented to replace the deprecated + // qGlobalQHashSeed() and qSetGlobalQHashSeed() + static int getHashSeed() + { + return int(QHashSeed::globalSeed() & INT_MAX); + } + static void setHashSeed(int seed) + { + if (seed == 0) + QHashSeed::setDeterministicGlobalSeed(); + else + QHashSeed::resetRandomGlobalSeed(); } }; @@ -764,13 +996,33 @@ void tst_QHash::qhash() //copied from tst_QMap void tst_QHash::take() { - QHash<int, QString> map; + { + QHash<int, QString> map; + QCOMPARE(map.take(1), QString()); + QVERIFY(!map.isDetached()); + + map.insert(2, "zwei"); + map.insert(3, "drei"); - map.insert(2, "zwei"); - map.insert(3, "drei"); + QCOMPARE(map.take(3), QLatin1String("drei")); + QVERIFY(!map.contains(3)); + } + { + QMultiHash<int, QString> hash; + QCOMPARE(hash.take(1), QString()); + QVERIFY(!hash.isDetached()); - QCOMPARE(map.take(3), QLatin1String("drei")); - QVERIFY(!map.contains(3)); + hash.insert(1, "value1"); + hash.insert(2, "value2"); + hash.insert(1, "value3"); + + // The docs tell that if there are multiple values for a key, then the + // most recent is returned. + QCOMPARE(hash.take(1), "value3"); + QCOMPARE(hash.take(1), "value1"); + QCOMPARE(hash.take(1), QString()); + QCOMPARE(hash.take(2), "value2"); + } } // slightly modified from tst_QMap @@ -855,33 +1107,33 @@ void tst_QHash::operator_eq() // regardless of insertion or iteration order { - QHash<int, int> a; - QHash<int, int> b; + QMultiHash<int, int> a; + QMultiHash<int, int> b; - a.insertMulti(0, 0); - a.insertMulti(0, 1); + a.insert(0, 0); + a.insert(0, 1); - b.insertMulti(0, 1); - b.insertMulti(0, 0); + b.insert(0, 1); + b.insert(0, 0); QVERIFY(a == b); QVERIFY(!(a != b)); } { - QHash<int, int> a; - QHash<int, int> b; + QMultiHash<int, int> a; + QMultiHash<int, int> b; enum { Count = 100 }; for (int key = 0; key < Count; ++key) { for (int value = 0; value < Count; ++value) - a.insertMulti(key, value); + a.insert(key, value); } for (int key = Count - 1; key >= 0; --key) { for (int value = 0; value < Count; ++value) - b.insertMulti(key, value); + b.insert(key, value); } QVERIFY(a == b); @@ -889,8 +1141,8 @@ void tst_QHash::operator_eq() } { - QHash<int, int> a; - QHash<int, int> b; + QMultiHash<int, int> a; + QMultiHash<int, int> b; enum { Count = 100, @@ -900,7 +1152,7 @@ void tst_QHash::operator_eq() for (int key = 0; key < Count; ++key) { for (int value = 0; value < Count; ++value) - a.insertMulti(key, value); + a.insert(key, value); } // Generates two permutations of [0, Count) for the keys and values, @@ -909,7 +1161,7 @@ void tst_QHash::operator_eq() for (int k = 0; k < Count; ++k) { const int key = (k * KeyStep) % Count; for (int v = 0; v < Count; ++v) - b.insertMulti(key, (v * ValueStep) % Count); + b.insert(key, (v * ValueStep) % Count); } QVERIFY(a == b); @@ -917,16 +1169,232 @@ void tst_QHash::operator_eq() } } +#ifdef __cpp_concepts +struct HeterogeneousHashingType +{ + inline static int conversionCount = 0; + QString s; + + Q_IMPLICIT operator QString() const + { + ++conversionCount; + return s; + } + + // std::equality_comparable_with requires we be self-comparable too + friend bool operator==(const HeterogeneousHashingType &t1, const HeterogeneousHashingType &t2) = default; + + friend bool operator==(const QString &string, const HeterogeneousHashingType &tester) + { return tester.s == string; } + friend bool operator!=(const QString &string, const HeterogeneousHashingType &tester) + { return !(tester.s == string); } + + friend size_t qHash(const HeterogeneousHashingType &tester, size_t seed) + { return qHash(tester.s, seed); } +}; +QT_BEGIN_NAMESPACE +template <> struct QHashHeterogeneousSearch<QString, HeterogeneousHashingType> : std::true_type {}; +template <> struct QHashHeterogeneousSearch<HeterogeneousHashingType, QString> : std::true_type {}; +QT_END_NAMESPACE +static_assert(std::is_same_v<QString, std::common_type_t<QString, HeterogeneousHashingType>>); +static_assert(std::equality_comparable_with<QString, HeterogeneousHashingType>); +static_assert(QHashPrivate::HeterogeneouslySearchableWith<QString, HeterogeneousHashingType>); +static_assert(QHashPrivate::HeterogeneouslySearchableWith<HeterogeneousHashingType, QString>); + +template <typename T> struct HeterogeneousSearchTestHelper +{ + static void resetCounter() {} + static void checkCounter() {} +}; +template <> struct HeterogeneousSearchTestHelper<HeterogeneousHashingType> +{ + static void resetCounter() + { + HeterogeneousHashingType::conversionCount = 0; + } + static void checkCounter() + { + QTest::setThrowOnFail(true); + auto scopeExit = qScopeGuard([] { QTest::setThrowOnFail(false); }); + QCOMPARE(HeterogeneousHashingType::conversionCount, 0); + } +}; +#else +using HeterogeneousHashingType = QString; +#endif + +template <template <typename, typename> class Hash, typename String, typename View, typename Converter> +static void heterogeneousSearchTest(const QList<std::remove_const_t<String>> &keys, Converter conv) +{ +#ifdef __cpp_concepts + using Helper = HeterogeneousSearchTestHelper<View>; + String key = keys.last(); + String otherKey = keys.first(); + auto keyHolder = conv(key); + auto otherKeyHolder = conv(otherKey); + View keyView(keyHolder); + View otherKeyView(otherKeyHolder); + + Hash<String, qsizetype> hash; + static constexpr bool IsMultiHash = !std::is_same_v<decltype(hash.remove(String())), bool>; + hash[key] = keys.size(); + + Helper::resetCounter(); + QVERIFY(hash.contains(keyView)); + QCOMPARE_EQ(hash.count(keyView), 1); + QCOMPARE_EQ(hash.value(keyView), keys.size()); + QCOMPARE_EQ(hash.value(keyView, -1), keys.size()); + QCOMPARE_EQ(std::as_const(hash)[keyView], keys.size()); + QCOMPARE_EQ(hash.find(keyView), hash.begin()); + QCOMPARE_EQ(std::as_const(hash).find(keyView), hash.constBegin()); + QCOMPARE_EQ(hash.constFind(keyView), hash.constBegin()); + QCOMPARE_EQ(hash.equal_range(keyView), std::make_pair(hash.begin(), hash.end())); + QCOMPARE_EQ(std::as_const(hash).equal_range(keyView), + std::make_pair(hash.constBegin(), hash.constEnd())); + Helper::checkCounter(); + + QVERIFY(!hash.contains(otherKeyView)); + QCOMPARE_EQ(hash.count(otherKeyView), 0); + QCOMPARE_EQ(hash.value(otherKeyView), 0); + QCOMPARE_EQ(hash.value(otherKeyView, -1), -1); + QCOMPARE_EQ(std::as_const(hash)[otherKeyView], 0); + QCOMPARE_EQ(hash.find(otherKeyView), hash.end()); + QCOMPARE_EQ(std::as_const(hash).find(otherKeyView), hash.constEnd()); + QCOMPARE_EQ(hash.constFind(otherKeyView), hash.constEnd()); + QCOMPARE_EQ(hash.equal_range(otherKeyView), std::make_pair(hash.end(), hash.end())); + QCOMPARE_EQ(std::as_const(hash).equal_range(otherKeyView), + std::make_pair(hash.constEnd(), hash.constEnd())); + Helper::checkCounter(); + + // non-const versions + QCOMPARE_EQ(hash[keyView], keys.size()); // already there + Helper::checkCounter(); + + QCOMPARE_EQ(hash[otherKeyView], 0); // inserts + Helper::resetCounter(); + hash[otherKeyView] = INT_MAX; + Helper::checkCounter(); + + if constexpr (IsMultiHash) { + hash.insert(key, keys.size()); + QCOMPARE_EQ(hash.count(keyView), 2); + + // not depending on which of the two the current implementation finds + QCOMPARE_NE(hash.value(keyView), 0); + QCOMPARE_NE(hash.value(keyView, -1000), -1000); + QCOMPARE_NE(std::as_const(hash)[keyView], 0); + QCOMPARE_NE(hash.find(keyView), hash.end()); + QCOMPARE_NE(std::as_const(hash).find(keyView), hash.constEnd()); + QCOMPARE_NE(hash.constFind(keyView), hash.constEnd()); + QCOMPARE_NE(hash.equal_range(keyView), std::make_pair(hash.end(), hash.end())); + QCOMPARE_NE(std::as_const(hash).equal_range(keyView), + std::make_pair(hash.constEnd(), hash.constEnd())); + + // QMultiHash-specific functions + QVERIFY(hash.contains(keyView, keys.size())); + QCOMPARE_EQ(hash.count(keyView, 0), 0); + QCOMPARE_EQ(hash.count(keyView, keys.size()), 2); + QCOMPARE_EQ(hash.values(keyView), QList<qsizetype>({ keys.size(), keys.size() })); + + hash.insert(key, -keys.size()); + QCOMPARE_EQ(hash.count(keyView), 3); + QCOMPARE_EQ(hash.find(keyView, 0), hash.end()); + QCOMPARE_NE(hash.find(keyView, keys.size()), hash.end()); + QCOMPARE_NE(hash.find(keyView, -keys.size()), hash.end()); + QCOMPARE_EQ(std::as_const(hash).find(keyView, 0), hash.constEnd()); + QCOMPARE_NE(std::as_const(hash).find(keyView, keys.size()), hash.constEnd()); + QCOMPARE_NE(std::as_const(hash).find(keyView, -keys.size()), hash.constEnd()); + QCOMPARE_EQ(hash.constFind(keyView, 0), hash.constEnd()); + QCOMPARE_NE(hash.constFind(keyView, keys.size()), hash.constEnd()); + QCOMPARE_NE(hash.constFind(keyView, -keys.size()), hash.constEnd()); + + // removals + QCOMPARE_EQ(hash.remove(keyView, -keys.size()), 1); + QCOMPARE_EQ(hash.remove(keyView), 2); + } else { + // removals + QCOMPARE_EQ(hash.remove(keyView), true); + } + + QCOMPARE_EQ(hash.take(otherKeyView), INT_MAX); + QVERIFY(hash.isEmpty()); + Helper::checkCounter(); + + // repeat with more keys + for (qsizetype i = 0; i < keys.size() - 1; ++i) { + hash.insert(keys[i], -(i + 1)); + hash.insert(keys[i], i + 1); + } + + QVERIFY(!hash.contains(keyView)); + QCOMPARE_EQ(hash.count(keyView), 0); + QCOMPARE_EQ(hash.value(keyView), 0); + QCOMPARE_EQ(hash.value(keyView, -1), -1); + QCOMPARE_EQ(std::as_const(hash)[keyView], 0); + QCOMPARE_EQ(hash.find(keyView), hash.end()); + QCOMPARE_EQ(hash.constFind(keyView), hash.constEnd()); + Helper::checkCounter(); +#else + Q_UNUSED(keys); + Q_UNUSED(conv); + QSKIP("This feature requires C++20 (concepts)"); +#endif +} + +template <template <typename, typename> class Hash, typename String, typename View> +static void heterogeneousSearchTest(const QList<std::remove_const_t<String>> &keys) +{ + heterogeneousSearchTest<Hash, String, View>(keys, [](const String &s) { return View(s); }); +} + +template <template <typename, typename> class Hash, typename T> +static void heterogeneousSearchLatin1String(T) +{ + if constexpr (!T::value) { + QSKIP("QLatin1StringView and QString do not have the same hash on this platform"); + } else { + // similar to the above + auto toLatin1 = [](const QString &s) { return s.toLatin1(); }; + heterogeneousSearchTest<Hash, QString, QLatin1StringView>({ "Hello", {}, "World" }, toLatin1); + } +} + +void tst_QHash::heterogeneousSearch() +{ + heterogeneousSearchTest<QHash, QString, HeterogeneousHashingType>({ "Hello", {}, "World" }); +} + +void tst_QHash::heterogeneousSearchConstKey() +{ + // QHash<const QString, X> seen in the wild (e.g. Qt Creator) + heterogeneousSearchTest<QHash, const QString, HeterogeneousHashingType>({ "Hello", {}, "World" }); +} + +void tst_QHash::heterogeneousSearchByteArray() +{ + heterogeneousSearchTest<QHash, QByteArray, QByteArrayView>({ "Hello", {}, "World" }); +} + +void tst_QHash::heterogeneousSearchString() +{ + heterogeneousSearchTest<QHash, QString, QStringView>({ "Hello", {}, "World" }); +} + +void tst_QHash::heterogeneousSearchLatin1String() +{ + ::heterogeneousSearchLatin1String<QHash>(QHashHeterogeneousSearch<QString, QLatin1StringView>{}); +} + void tst_QHash::compare() { QHash<int, QString> hash1,hash2; QString testString = "Teststring %1"; int i; - for(i = 0; i < 1000; ++i) + for (i = 0; i < 1000; ++i) hash1.insert(i,testString.arg(i)); - for(--i; i >= 0; --i) + for (--i; i >= 0; --i) hash2.insert(i,testString.arg(i)); hash1.squeeze(); @@ -947,39 +1415,39 @@ void tst_QHash::compare() void tst_QHash::compare2() { - QHash<int, int> a; - QHash<int, int> b; + QMultiHash<int, int> a; + QMultiHash<int, int> b; - a.insertMulti(17, 1); - a.insertMulti(17 * 2, 1); - b.insertMulti(17 * 2, 1); - b.insertMulti(17, 1); + a.insert(17, 1); + a.insert(17 * 2, 1); + b.insert(17 * 2, 1); + b.insert(17, 1); QVERIFY(a == b); QVERIFY(b == a); - a.insertMulti(17, 2); - a.insertMulti(17 * 2, 3); - b.insertMulti(17 * 2, 3); - b.insertMulti(17, 2); + a.insert(17, 2); + a.insert(17 * 2, 3); + b.insert(17 * 2, 3); + b.insert(17, 2); QVERIFY(a == b); QVERIFY(b == a); - a.insertMulti(17, 4); - a.insertMulti(17 * 2, 5); - b.insertMulti(17 * 2, 4); - b.insertMulti(17, 5); + a.insert(17, 4); + a.insert(17 * 2, 5); + b.insert(17 * 2, 4); + b.insert(17, 5); QVERIFY(!(a == b)); QVERIFY(!(b == a)); a.clear(); b.clear(); - a.insertMulti(1, 1); - a.insertMulti(1, 2); - a.insertMulti(1, 3); - b.insertMulti(1, 1); - b.insertMulti(1, 2); - b.insertMulti(1, 3); - b.insertMulti(1, 4); + a.insert(1, 1); + a.insert(1, 2); + a.insert(1, 3); + b.insert(1, 1); + b.insert(1, 2); + b.insert(1, 3); + b.insert(1, 4); QVERIFY(!(a == b)); QVERIFY(!(b == a)); } @@ -993,7 +1461,7 @@ void tst_QHash::iterators() QString testString1; int i; - for(i = 1; i < 100; ++i) + for (i = 1; i < 100; ++i) hash.insert(i, testString.arg(i)); //to get some chaos in the hash @@ -1010,18 +1478,13 @@ void tst_QHash::iterators() QVERIFY(stlIt.value() == testMap.value(1)); - stlIt+=5; + for (int i = 0; i < 5; ++i) + ++stlIt; QVERIFY(stlIt.value() == testMap.value(6)); stlIt++; QVERIFY(stlIt.value() == testMap.value(7)); - stlIt-=3; - QVERIFY(stlIt.value() == testMap.value(4)); - - stlIt--; - QVERIFY(stlIt.value() == testMap.value(3)); - testMap.clear(); //STL-Style const-iterators @@ -1035,18 +1498,13 @@ void tst_QHash::iterators() QVERIFY(cstlIt.value() == testMap.value(1)); - cstlIt+=5; + for (int i = 0; i < 5; ++i) + ++cstlIt; QVERIFY(cstlIt.value() == testMap.value(6)); cstlIt++; QVERIFY(cstlIt.value() == testMap.value(7)); - cstlIt-=3; - QVERIFY(cstlIt.value() == testMap.value(4)); - - cstlIt--; - QVERIFY(cstlIt.value() == testMap.value(3)); - testMap.clear(); //Java-Style iterators @@ -1068,14 +1526,7 @@ void tst_QHash::iterators() QVERIFY(javaIt.value() == testMap.value(i)); } - ++i; - while(javaIt.hasPrevious()) { - --i; - javaIt.previous(); - QVERIFY(javaIt.value() == testMap.value(i)); - } - - //peekNext() peekPrevious() + //peekNext() javaIt.toFront(); javaIt.next(); while(javaIt.hasNext()) { @@ -1083,25 +1534,147 @@ void tst_QHash::iterators() testString1 = javaIt.peekNext().value(); javaIt.next(); QVERIFY(javaIt.value() == testString1); - QCOMPARE(javaIt.peekPrevious().value(), testString1); } - while(javaIt.hasPrevious()) { - testString = javaIt.value(); - testString1 = javaIt.peekPrevious().value(); - javaIt.previous(); - QVERIFY(javaIt.value() == testString1); - QCOMPARE(javaIt.peekNext().value(), testString1); +} + +void tst_QHash::multihashIterators() +{ + QMultiHash<int, QString> hash; + QMap<int, QString> referenceMap; + QString testString = "Teststring %1-%2"; + int i = 0; + + // Add 5 elements for each key + for (i = 0; i < 10; ++i) { + for (int j = 0; j < 5; ++j) + hash.insert(i, testString.arg(i, j)); } + + hash.squeeze(); + + // Verify that iteration is reproducible. + + // STL iterator + QMultiHash<int, QString>::iterator stlIt; + + for (stlIt = hash.begin(), i = 1; stlIt != hash.end(); ++stlIt, ++i) + referenceMap.insert(i, *stlIt); + + stlIt = hash.begin(); + QCOMPARE(*stlIt, referenceMap[1]); + + for (i = 0; i < 5; ++i) + stlIt++; + QCOMPARE(*stlIt, referenceMap[6]); + + for (i = 0; i < 44; ++i) + stlIt++; + QCOMPARE(*stlIt, referenceMap[50]); + + // const STL iterator + referenceMap.clear(); + QMultiHash<int, QString>::const_iterator cstlIt; + + for (cstlIt = hash.cbegin(), i = 1; cstlIt != hash.cend(); ++cstlIt, ++i) + referenceMap.insert(i, *cstlIt); + + cstlIt = hash.cbegin(); + QCOMPARE(*cstlIt, referenceMap[1]); + + for (i = 0; i < 5; ++i) + cstlIt++; + QCOMPARE(*cstlIt, referenceMap[6]); + + for (i = 0; i < 44; ++i) + cstlIt++; + QCOMPARE(*cstlIt, referenceMap[50]); + + // Java-Style iterator + referenceMap.clear(); + QMultiHashIterator<int, QString> javaIt(hash); + + // walk through + i = 0; + while (javaIt.hasNext()) { + ++i; + javaIt.next(); + referenceMap.insert(i, javaIt.value()); + } + javaIt.toFront(); + i = 0; + while (javaIt.hasNext()) { + ++i; + javaIt.next(); + QCOMPARE(javaIt.value(), referenceMap.value(i)); + } + + // peekNext() + javaIt.toFront(); + javaIt.next(); + QString nextValue; + while (javaIt.hasNext()) { + nextValue = javaIt.peekNext().value(); + javaIt.next(); + QCOMPARE(javaIt.value(), nextValue); + } +} + +template<typename T> +void iteratorsInEmptyHashTestMethod() +{ + T hash; + using ConstIter = typename T::const_iterator; + ConstIter it1 = hash.cbegin(); + ConstIter it2 = hash.constBegin(); + QVERIFY(it1 == it2 && it2 == ConstIter()); + QVERIFY(!hash.isDetached()); + + ConstIter it3 = hash.cend(); + ConstIter it4 = hash.constEnd(); + QVERIFY(it3 == it4 && it4 == ConstIter()); + QVERIFY(!hash.isDetached()); + + // to call const overloads of begin() and end() + const T hash2; + ConstIter it5 = hash2.begin(); + ConstIter it6 = hash2.end(); + QVERIFY(it5 == it6 && it6 == ConstIter()); + QVERIFY(!hash2.isDetached()); + + T hash3; + using Iter = typename T::iterator; + Iter it7 = hash3.end(); + QVERIFY(it7 == Iter()); + QVERIFY(!hash3.isDetached()); + + Iter it8 = hash3.begin(); // calls detach() + QVERIFY(it8 == Iter()); + QVERIFY(hash3.isDetached()); +} + +void tst_QHash::iteratorsInEmptyHash() +{ + iteratorsInEmptyHashTestMethod<QHash<int, QString>>(); + if (QTest::currentTestFailed()) + return; + + iteratorsInEmptyHashTestMethod<QMultiHash<int, QString>>(); } void tst_QHash::keyIterator() { QHash<int, int> hash; + using KeyIterator = QHash<int, int>::key_iterator; + KeyIterator it1 = hash.keyBegin(); + KeyIterator it2 = hash.keyEnd(); + QVERIFY(it1 == it2 && it2 == KeyIterator()); + QVERIFY(!hash.isDetached()); + for (int i = 0; i < 100; ++i) hash.insert(i, i*100); - QHash<int, int>::key_iterator key_it = hash.keyBegin(); + KeyIterator key_it = hash.keyBegin(); QHash<int, int>::const_iterator it = hash.cbegin(); for (int i = 0; i < 100; ++i) { QCOMPARE(*key_it, it.key()); @@ -1115,15 +1688,55 @@ void tst_QHash::keyIterator() QVERIFY(key_it != hash.keyEnd()); QCOMPARE(*key_it, it.key()); QCOMPARE(*(key_it++), (it++).key()); - QCOMPARE(*(key_it--), (it--).key()); - QCOMPARE(*(++key_it), (++it).key()); - QCOMPARE(*(--key_it), (--it).key()); + if (key_it != hash.keyEnd()) { + QVERIFY(it != hash.cend()); + ++key_it; + ++it; + if (key_it != hash.keyEnd()) + QCOMPARE(*key_it, it.key()); + else + QVERIFY(it == hash.cend()); + } QCOMPARE(std::count(hash.keyBegin(), hash.keyEnd(), 99), 1); // DefaultConstructible test - typedef QHash<int, int>::key_iterator keyIterator; - Q_STATIC_ASSERT(std::is_default_constructible<keyIterator>::value); + static_assert(std::is_default_constructible<KeyIterator>::value); +} + +void tst_QHash::multihashKeyIterator() +{ + QMultiHash<int, int> hash; + + using KeyIterator = QMultiHash<int, int>::key_iterator; + KeyIterator it1 = hash.keyBegin(); + KeyIterator it2 = hash.keyEnd(); + QVERIFY(it1 == it2 && it2 == KeyIterator()); + QVERIFY(!hash.isDetached()); + + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 5; ++j) + hash.insert(i, i * 100 + j); + } + + KeyIterator keyIt = hash.keyBegin(); + QMultiHash<int, int>::const_iterator it = hash.cbegin(); + while (keyIt != hash.keyEnd() && it != hash.cend()) { + QCOMPARE(*keyIt, it.key()); + keyIt++; + it++; + } + + keyIt = std::find(hash.keyBegin(), hash.keyEnd(), 5); + it = std::find(hash.cbegin(), hash.cend(), 5 * 100 + 2); + + QVERIFY(keyIt != hash.keyEnd()); + QCOMPARE(*keyIt, it.key()); + + QCOMPARE(std::count(hash.keyBegin(), hash.keyEnd(), 9), 5); + + // DefaultConstructible test + static_assert(std::is_default_constructible<KeyIterator>::value); } void tst_QHash::keyValueIterator() @@ -1144,6 +1757,12 @@ void tst_QHash::keyValueIterator() entry_type pair(it.key(), it.value()); QCOMPARE(*key_value_it, pair); + QCOMPARE(key_value_it->first, pair.first); + QCOMPARE(key_value_it->second, pair.second); + QCOMPARE(&(*key_value_it).first, &it.key()); + QCOMPARE(&key_value_it->first, &it.key()); + QCOMPARE(&(*key_value_it).second, &it.value()); + QCOMPARE(&key_value_it->second, &it.value()); ++key_value_it; ++it; } @@ -1162,31 +1781,105 @@ void tst_QHash::keyValueIterator() ++it; ++key_value_it; - QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + if (it != hash.cend()) + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + else + QVERIFY(key_value_it == hash.constKeyValueEnd()); - --it; - --key_value_it; - QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); - - ++it; - ++key_value_it; - QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); - - --it; - --key_value_it; - QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); key = 99; value = 99 * 100; QCOMPARE(std::count(hash.constKeyValueBegin(), hash.constKeyValueEnd(), entry_type(key, value)), 1); } +void tst_QHash::multihashKeyValueIterator() +{ + QMultiHash<int, int> hash; + using EntryType = QHash<int, int>::const_key_value_iterator::value_type; + + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 5; j++) + hash.insert(i, i * 100 + j); + } + + auto keyValueIt = hash.constKeyValueBegin(); + auto it = hash.cbegin(); + + for (int i = 0; i < hash.size(); ++i) { + QVERIFY(keyValueIt != hash.constKeyValueEnd()); + QVERIFY(it != hash.cend()); + + EntryType pair(it.key(), it.value()); + QCOMPARE(*keyValueIt, pair); + QCOMPARE(keyValueIt->first, pair.first); + QCOMPARE(keyValueIt->second, pair.second); + ++keyValueIt; + ++it; + } + + QVERIFY(keyValueIt == hash.constKeyValueEnd()); + QVERIFY(it == hash.cend()); + + int key = 5; + int value = key * 100 + 3; + EntryType pair(key, value); + keyValueIt = std::find(hash.constKeyValueBegin(), hash.constKeyValueEnd(), pair); + it = std::find(hash.cbegin(), hash.cend(), value); + + QVERIFY(keyValueIt != hash.constKeyValueEnd()); + QCOMPARE(*keyValueIt, EntryType(it.key(), it.value())); + + key = 9; + value = key * 100 + 4; + const auto numItems = + std::count(hash.constKeyValueBegin(), hash.constKeyValueEnd(), EntryType(key, value)); + QCOMPARE(numItems, 1); +} + +template<typename T> +void keyValueIteratorInEmptyHashTestMethod() +{ + T hash; + using ConstKeyValueIter = typename T::const_key_value_iterator; + + ConstKeyValueIter it1 = hash.constKeyValueBegin(); + ConstKeyValueIter it2 = hash.constKeyValueEnd(); + QVERIFY(it1 == it2 && it2 == ConstKeyValueIter()); + QVERIFY(!hash.isDetached()); + + const T hash2; + ConstKeyValueIter it3 = hash2.keyValueBegin(); + ConstKeyValueIter it4 = hash2.keyValueEnd(); + QVERIFY(it3 == it4 && it4 == ConstKeyValueIter()); + QVERIFY(!hash.isDetached()); + + T hash3; + using KeyValueIter = typename T::key_value_iterator; + + KeyValueIter it5 = hash3.keyValueEnd(); + QVERIFY(it5 == KeyValueIter()); + QVERIFY(!hash3.isDetached()); + + KeyValueIter it6 = hash3.keyValueBegin(); // calls detach() + QVERIFY(it6 == KeyValueIter()); + QVERIFY(hash3.isDetached()); +} + +void tst_QHash::keyValueIteratorInEmptyHash() +{ + keyValueIteratorInEmptyHashTestMethod<QHash<int, int>>(); + if (QTest::currentTestFailed()) + return; + + keyValueIteratorInEmptyHashTestMethod<QMultiHash<int, int>>(); +} + void tst_QHash::rehash_isnt_quadratic() { // this test should be incredibly slow if rehash() is quadratic for (int j = 0; j < 5; ++j) { QHash<int, int> testHash; for (int i = 0; i < 500000; ++i) - testHash.insertMulti(1, 1); + testHash.insert(i, 1); } } @@ -1218,16 +1911,24 @@ void tst_QHash::dont_need_default_constructor() void tst_QHash::qmultihash_specific() { QMultiHash<int, int> hash1; + + QVERIFY(!hash1.contains(1)); + QVERIFY(!hash1.contains(1, 2)); + QVERIFY(!hash1.isDetached()); + for (int i = 1; i <= 9; ++i) { + QVERIFY(!hash1.contains(i)); for (int j = 1; j <= i; ++j) { int k = i * 10 + j; QVERIFY(!hash1.contains(i, k)); hash1.insert(i, k); QVERIFY(hash1.contains(i, k)); } + QVERIFY(hash1.contains(i)); } for (int i = 1; i <= 9; ++i) { + QVERIFY(hash1.contains(i)); for (int j = 1; j <= i; ++j) { int k = i * 10 + j; QVERIFY(hash1.contains(i, k)); @@ -1235,26 +1936,26 @@ void tst_QHash::qmultihash_specific() } QVERIFY(hash1.contains(9, 99)); - QCOMPARE(hash1.count(), 45); + QCOMPARE(hash1.size(), 45); hash1.remove(9, 99); QVERIFY(!hash1.contains(9, 99)); - QCOMPARE(hash1.count(), 44); + QCOMPARE(hash1.size(), 44); hash1.remove(9, 99); QVERIFY(!hash1.contains(9, 99)); - QCOMPARE(hash1.count(), 44); + QCOMPARE(hash1.size(), 44); hash1.remove(1, 99); - QCOMPARE(hash1.count(), 44); + QCOMPARE(hash1.size(), 44); hash1.insert(1, 99); hash1.insert(1, 99); - QCOMPARE(hash1.count(), 46); + QCOMPARE(hash1.size(), 46); hash1.remove(1, 99); - QCOMPARE(hash1.count(), 44); + QCOMPARE(hash1.size(), 44); hash1.remove(1, 99); - QCOMPARE(hash1.count(), 44); + QCOMPARE(hash1.size(), 44); { QMultiHash<int, int>::const_iterator i = hash1.constFind(1, 11); @@ -1299,6 +2000,12 @@ void tst_QHash::qmultihash_specific() QVERIFY(i.value() == 98); } + QCOMPARE(hash1.count(9), 8); + QCOMPARE(hash1.size(), 44); + hash1.remove(9); + QCOMPARE(hash1.count(9), 0); + QCOMPARE(hash1.size(), 36); + { QMultiHash<int, int> map1; map1.insert(42, 1); @@ -1313,63 +2020,394 @@ void tst_QHash::qmultihash_specific() map2.insert(42, 1); map2.insert(10, 2); map2.insert(48, 3); - QCOMPARE(map1.count(), map2.count()); + QCOMPARE(map1.size(), map2.size()); QVERIFY(map1.remove(42,5)); + QVERIFY(map1 != map2); QVERIFY(map2.remove(42,5)); QVERIFY(map1 == map2); + + QHash<int, int> hash; + hash.insert(-1, -1); + map2.unite(hash); + QCOMPARE(map2.size(), 6); + QCOMPARE(map2[-1], -1); } } -template <typename T> -QList<T> sorted(const QList<T> &list) +void tst_QHash::qmultihash_qhash_rvalue_ref_ctor() { - QList<T> res = list; - std::sort(res.begin(), res.end()); - return res; + // QHash is empty + { + QHash<int, MyClass> hash; + QMultiHash<int, MyClass> multiHash(std::move(hash)); + QVERIFY(multiHash.isEmpty()); + } + + // QHash is detached + { + MyClass::copies = 0; + MyClass::moves = 0; + QHash<int, MyClass> hash; + hash.emplace(0, "a"); + hash.emplace(1, "b"); + QMultiHash<int, MyClass> multiHash(std::move(hash)); + QCOMPARE(multiHash.size(), 2); + QCOMPARE(multiHash[0].str, QString("a")); + QCOMPARE(multiHash[1].str, QString("b")); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 2); + QCOMPARE(MyClass::count, 2); + } + + // QHash is shared + { + MyClass::copies = 0; + MyClass::moves = 0; + QHash<int, MyClass> hash; + hash.emplace(0, "a"); + hash.emplace(1, "b"); + QHash<int, MyClass> hash2(hash); + QMultiHash<int, MyClass> multiHash(std::move(hash)); + QCOMPARE(multiHash.size(), 2); + QCOMPARE(multiHash[0].str, QString("a")); + QCOMPARE(multiHash[1].str, QString("b")); + QCOMPARE(MyClass::copies, 2); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 4); + } +} + +void tst_QHash::qmultihash_qhash_rvalue_ref_unite() +{ + // QHash is empty + { + QHash<int, MyClass> hash; + QMultiHash<int, MyClass> multiHash; + multiHash.unite(std::move(hash)); + QVERIFY(multiHash.isEmpty()); + } + + // QHash is detached + { + MyClass::copies = 0; + MyClass::moves = 0; + QHash<int, MyClass> hash; + hash.emplace(0, "a"); + hash.emplace(1, "b"); + QMultiHash<int, MyClass> multiHash; + multiHash.unite(std::move(hash)); + QCOMPARE(multiHash.size(), 2); + QCOMPARE(multiHash[0].str, QString("a")); + QCOMPARE(multiHash[1].str, QString("b")); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 2); + QCOMPARE(MyClass::count, 2); + } + + // QHash is shared + { + MyClass::copies = 0; + MyClass::moves = 0; + QHash<int, MyClass> hash; + hash.emplace(0, "a"); + hash.emplace(1, "b"); + QHash<int, MyClass> hash2(hash); + QMultiHash<int, MyClass> multiHash; + multiHash.unite(std::move(hash)); + QCOMPARE(multiHash.size(), 2); + QCOMPARE(multiHash[0].str, QString("a")); + QCOMPARE(multiHash[1].str, QString("b")); + QCOMPARE(MyClass::copies, 2); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 4); + } + + // QMultiHash already contains an item with the same key + { + MyClass::copies = 0; + MyClass::moves = 0; + QHash<int, MyClass> hash; + hash.emplace(0, "a"); + hash.emplace(1, "b"); + QMultiHash<int, MyClass> multiHash; + multiHash.emplace(0, "c"); + multiHash.unite(std::move(hash)); + QCOMPARE(multiHash.size(), 3); + const auto aRange = multiHash.equal_range(0); + QCOMPARE(std::distance(aRange.first, aRange.second), 2); + auto it = aRange.first; + QCOMPARE(it->str, QString("a")); + QCOMPARE((++it)->str, QString("c")); + QCOMPARE(multiHash[1].str, QString("b")); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 2); + QCOMPARE(MyClass::count, 3); + } +} + +void tst_QHash::qmultihashUnite() +{ + // Joining two multi hashes, first is empty + { + MyClass::copies = 0; + MyClass::moves = 0; + QMultiHash<int, MyClass> hash1; + QMultiHash<int, MyClass> hash2; + hash2.emplace(0, "a"); + hash2.emplace(1, "b"); + + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 2); + + hash1.unite(hash2); + // hash1 is empty, so we just share the data between hash1 and hash2 + QCOMPARE(hash1.size(), 2); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 2); + } + // Joining two multi hashes, second is empty + { + MyClass::copies = 0; + MyClass::moves = 0; + QMultiHash<int, MyClass> hash1; + QMultiHash<int, MyClass> hash2; + hash1.emplace(0, "a"); + hash1.emplace(1, "b"); + + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 2); + + hash1.unite(hash2); + // hash2 is empty, so nothing happens + QVERIFY(hash2.isEmpty()); + QVERIFY(!hash2.isDetached()); + QCOMPARE(hash1.size(), 2); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 2); + } + // Joining two multi hashes + { + MyClass::copies = 0; + MyClass::moves = 0; + QMultiHash<int, MyClass> hash1; + QMultiHash<int, MyClass> hash2; + hash1.emplace(0, "a"); + hash1.emplace(1, "b"); + hash2.emplace(0, "c"); + hash2.emplace(1, "d"); + + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 4); + + hash1.unite(hash2); + QCOMPARE(hash1.size(), 4); + QCOMPARE(MyClass::copies, 2); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 6); + } + + // operator+() uses unite() internally. + + // using operator+(), hash1 is empty + { + MyClass::copies = 0; + MyClass::moves = 0; + QMultiHash<int, MyClass> hash1; + QMultiHash<int, MyClass> hash2; + hash2.emplace(0, "a"); + hash2.emplace(1, "b"); + + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 2); + + auto hash3 = hash1 + hash2; + // hash1 is empty, so we just share the data between hash3 and hash2 + QCOMPARE(hash1.size(), 0); + QCOMPARE(hash2.size(), 2); + QCOMPARE(hash3.size(), 2); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 2); + } + // using operator+(), hash2 is empty + { + MyClass::copies = 0; + MyClass::moves = 0; + QMultiHash<int, MyClass> hash1; + QMultiHash<int, MyClass> hash2; + hash1.emplace(0, "a"); + hash1.emplace(1, "b"); + + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 2); + + auto hash3 = hash1 + hash2; + // hash2 is empty, so we just share the data between hash3 and hash1 + QCOMPARE(hash1.size(), 2); + QCOMPARE(hash2.size(), 0); + QCOMPARE(hash3.size(), 2); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 2); + } + // using operator+() + { + MyClass::copies = 0; + MyClass::moves = 0; + QMultiHash<int, MyClass> hash1; + QMultiHash<int, MyClass> hash2; + hash1.emplace(0, "a"); + hash1.emplace(1, "b"); + hash2.emplace(0, "c"); + hash2.emplace(1, "d"); + + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 4); + + auto hash3 = hash1 + hash2; + QCOMPARE(hash1.size(), 2); + QCOMPARE(hash2.size(), 2); + QCOMPARE(hash3.size(), 4); + QCOMPARE(MyClass::copies, 4); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 8); + } +} + +void tst_QHash::qmultihashSize() +{ + // QMultiHash has an extra m_size member that counts the number of values, + // while d->size (shared with QHash) counts the number of distinct keys. + { + QMultiHash<int, int> hash; + QCOMPARE(hash.size(), 0); + QVERIFY(hash.isEmpty()); + + hash.insert(0, 42); + QCOMPARE(hash.size(), 1); + QVERIFY(!hash.isEmpty()); + + hash.insert(0, 42); + QCOMPARE(hash.size(), 2); + QVERIFY(!hash.isEmpty()); + + hash.emplace(0, 42); + QCOMPARE(hash.size(), 3); + QVERIFY(!hash.isEmpty()); + + QCOMPARE(hash.take(0), 42); + QCOMPARE(hash.size(), 2); + QVERIFY(!hash.isEmpty()); + + QCOMPARE(hash.remove(0), 2); + QCOMPARE(hash.size(), 0); + QVERIFY(hash.isEmpty()); + } + + { + QMultiHash<int, int> hash; + hash.emplace(0, 0); + hash.emplace(0, 0); + QCOMPARE(hash.size(), 2); + QVERIFY(!hash.isEmpty()); + + hash.emplace(0, 1); + QCOMPARE(hash.size(), 3); + QVERIFY(!hash.isEmpty()); + + QCOMPARE(hash.remove(0, 0), 2); + QCOMPARE(hash.size(), 1); + QVERIFY(!hash.isEmpty()); + + hash.remove(0); + QCOMPARE(hash.size(), 0); + QVERIFY(hash.isEmpty()); + } + + { + QMultiHash<int, int> hash; + + hash[0] = 0; + QCOMPARE(hash.size(), 1); + QVERIFY(!hash.isEmpty()); + + hash.replace(0, 1); + QCOMPARE(hash.size(), 1); + QVERIFY(!hash.isEmpty()); + + hash.insert(0, 1); + hash.erase(hash.cbegin()); + QCOMPARE(hash.size(), 1); + QVERIFY(!hash.isEmpty()); + + hash.erase(hash.cbegin()); + QCOMPARE(hash.size(), 0); + QVERIFY(hash.isEmpty()); + } +} + +void tst_QHash::qmultihashHeterogeneousSearch() +{ + heterogeneousSearchTest<QMultiHash, QString, HeterogeneousHashingType>({ "Hello", {}, "World" }); +} + +void tst_QHash::qmultihashHeterogeneousSearchConstKey() +{ + heterogeneousSearchTest<QMultiHash, const QString, HeterogeneousHashingType>({ "Hello", {}, "World" }); +} + +void tst_QHash::qmultihashHeterogeneousSearchByteArray() +{ + heterogeneousSearchTest<QMultiHash, QByteArray, QByteArrayView>({ "Hello", {}, "World" }); +} + +void tst_QHash::qmultihashHeterogeneousSearchString() +{ + heterogeneousSearchTest<QMultiHash, QString, QStringView>({ "Hello", {}, "World" }); +} + +void tst_QHash::qmultihashHeterogeneousSearchLatin1String() +{ + ::heterogeneousSearchLatin1String<QMultiHash>(QHashHeterogeneousSearch<QString, QLatin1StringView>{}); } void tst_QHash::keys_values_uniqueKeys() { - QHash<QString, int> hash; + QMultiHash<QString, int> hash; QVERIFY(hash.uniqueKeys().isEmpty()); QVERIFY(hash.keys().isEmpty()); QVERIFY(hash.values().isEmpty()); + QVERIFY(!hash.isDetached()); - hash.insertMulti("alpha", 1); + hash.insert("alpha", 1); QVERIFY(sorted(hash.keys()) == (QList<QString>() << "alpha")); QVERIFY(hash.keys() == hash.uniqueKeys()); QVERIFY(hash.values() == (QList<int>() << 1)); - hash.insertMulti("beta", -2); + hash.insert("beta", -2); QVERIFY(sorted(hash.keys()) == (QList<QString>() << "alpha" << "beta")); QVERIFY(hash.keys() == hash.uniqueKeys()); QVERIFY(sorted(hash.values()) == sorted(QList<int>() << 1 << -2)); - hash.insertMulti("alpha", 2); + hash.insert("alpha", 2); QVERIFY(sorted(hash.uniqueKeys()) == (QList<QString>() << "alpha" << "beta")); QVERIFY(sorted(hash.keys()) == (QList<QString>() << "alpha" << "alpha" << "beta")); QVERIFY(sorted(hash.values()) == sorted(QList<int>() << 2 << 1 << -2)); - hash.insertMulti("beta", 4); + hash.insert("beta", 4); QVERIFY(sorted(hash.uniqueKeys()) == (QList<QString>() << "alpha" << "beta")); QVERIFY(sorted(hash.keys()) == (QList<QString>() << "alpha" << "alpha" << "beta" << "beta")); QVERIFY(sorted(hash.values()) == sorted(QList<int>() << 2 << 1 << 4 << -2)); } -void tst_QHash::noNeedlessRehashes() -{ - QHash<int, int> hash; - for (int i = 0; i < 512; ++i) { - int j = (i * 345) % 512; - hash.insert(j, j); - int oldCapacity = hash.capacity(); - hash[j] = j + 1; - QCOMPARE(oldCapacity, hash.capacity()); - hash.insert(j, j + 1); - QCOMPARE(oldCapacity, hash.capacity()); - } -} - void tst_QHash::const_shared_null() { QHash<int, QString> hash2; @@ -1381,52 +2419,52 @@ static int wrongqHashOverload = 0; struct OneArgumentQHashStruct1 {}; bool operator==(const OneArgumentQHashStruct1 &, const OneArgumentQHashStruct1 &) { return false; } -uint qHash(OneArgumentQHashStruct1) { return 0; } +size_t qHash(OneArgumentQHashStruct1) { return 0; } struct OneArgumentQHashStruct2 {}; bool operator==(const OneArgumentQHashStruct2 &, const OneArgumentQHashStruct2 &) { return false; } -uint qHash(const OneArgumentQHashStruct2 &) { return 0; } +size_t qHash(const OneArgumentQHashStruct2 &) { return 0; } struct OneArgumentQHashStruct3 {}; bool operator==(const OneArgumentQHashStruct3 &, const OneArgumentQHashStruct3 &) { return false; } -uint qHash(OneArgumentQHashStruct3) { return 0; } -uint qHash(OneArgumentQHashStruct3 &, uint) { wrongqHashOverload = 1; return 0; } +size_t qHash(OneArgumentQHashStruct3) { return 0; } +size_t qHash(OneArgumentQHashStruct3 &, size_t) { wrongqHashOverload = 1; return 0; } struct OneArgumentQHashStruct4 {}; bool operator==(const OneArgumentQHashStruct4 &, const OneArgumentQHashStruct4 &) { return false; } -uint qHash(const OneArgumentQHashStruct4 &) { return 0; } -uint qHash(OneArgumentQHashStruct4 &, uint) { wrongqHashOverload = 1; return 0; } +size_t qHash(const OneArgumentQHashStruct4 &) { return 0; } +size_t qHash(OneArgumentQHashStruct4 &, size_t) { wrongqHashOverload = 1; return 0; } struct TwoArgumentsQHashStruct1 {}; bool operator==(const TwoArgumentsQHashStruct1 &, const TwoArgumentsQHashStruct1 &) { return false; } -uint qHash(const TwoArgumentsQHashStruct1 &) { wrongqHashOverload = 1; return 0; } -uint qHash(const TwoArgumentsQHashStruct1 &, uint) { return 0; } +size_t qHash(const TwoArgumentsQHashStruct1 &) { wrongqHashOverload = 1; return 0; } +size_t qHash(const TwoArgumentsQHashStruct1 &, size_t) { return 0; } struct TwoArgumentsQHashStruct2 {}; bool operator==(const TwoArgumentsQHashStruct2 &, const TwoArgumentsQHashStruct2 &) { return false; } -uint qHash(TwoArgumentsQHashStruct2) { wrongqHashOverload = 1; return 0; } -uint qHash(const TwoArgumentsQHashStruct2 &, uint) { return 0; } +size_t qHash(TwoArgumentsQHashStruct2) { wrongqHashOverload = 1; return 0; } +size_t qHash(const TwoArgumentsQHashStruct2 &, size_t) { return 0; } struct TwoArgumentsQHashStruct3 {}; bool operator==(const TwoArgumentsQHashStruct3 &, const TwoArgumentsQHashStruct3 &) { return false; } -uint qHash(const TwoArgumentsQHashStruct3 &) { wrongqHashOverload = 1; return 0; } -uint qHash(TwoArgumentsQHashStruct3, uint) { return 0; } +size_t qHash(const TwoArgumentsQHashStruct3 &) { wrongqHashOverload = 1; return 0; } +size_t qHash(TwoArgumentsQHashStruct3, size_t) { return 0; } struct TwoArgumentsQHashStruct4 {}; bool operator==(const TwoArgumentsQHashStruct4 &, const TwoArgumentsQHashStruct4 &) { return false; } -uint qHash(TwoArgumentsQHashStruct4) { wrongqHashOverload = 1; return 0; } -uint qHash(TwoArgumentsQHashStruct4, uint) { return 0; } +size_t qHash(TwoArgumentsQHashStruct4) { wrongqHashOverload = 1; return 0; } +size_t qHash(TwoArgumentsQHashStruct4, size_t) { return 0; } /*! \internal Check that QHash picks up the right overload. The best one, for a type T, is the two-args version of qHash: - either uint qHash(T, uint) or uint qHash(const T &, uint). + either size_t qHash(T, size_t) or size_t qHash(const T &, size_t). If neither of these exists, then one between - uint qHash(T) or uint qHash(const T &) must exist + size_t qHash(T) or size_t qHash(const T &) must exist (and it gets selected instead). */ void tst_QHash::twoArguments_qHash() @@ -1475,7 +2513,7 @@ void tst_QHash::twoArguments_qHash() void tst_QHash::initializerList() { QHash<int, QString> hash = {{1, "bar"}, {1, "hello"}, {2, "initializer_list"}}; - QCOMPARE(hash.count(), 2); + QCOMPARE(hash.size(), 2); QCOMPARE(hash[1], QString("hello")); QCOMPARE(hash[2], QString("initializer_list")); @@ -1485,9 +2523,9 @@ void tst_QHash::initializerList() // QCOMPARE(stdh[1], QString("bar")); QMultiHash<QString, int> multiHash{{"il", 1}, {"il", 2}, {"il", 3}}; - QCOMPARE(multiHash.count(), 3); + QCOMPARE(multiHash.size(), 3); QList<int> values = multiHash.values("il"); - QCOMPARE(values.count(), 3); + QCOMPARE(values.size(), 3); QHash<int, int> emptyHash{}; QVERIFY(emptyHash.isEmpty()); @@ -1504,14 +2542,14 @@ void tst_QHash::initializerList() void tst_QHash::eraseValidIteratorOnSharedHash() { - QHash<int, int> a, b; + QMultiHash<int, int> a, b; a.insert(10, 10); - a.insertMulti(10, 25); - a.insertMulti(10, 30); + a.insert(10, 25); + a.insert(10, 30); a.insert(20, 20); a.insert(40, 40); - QHash<int, int>::iterator i = a.begin(); + auto i = a.begin(); while (i.value() != 25) ++i; @@ -1533,7 +2571,7 @@ void tst_QHash::eraseValidIteratorOnSharedHash() void tst_QHash::equal_range() { - QHash<int, QString> hash; + QMultiHash<int, QString> hash; auto result = hash.equal_range(0); QCOMPARE(result.first, hash.end()); @@ -1546,7 +2584,7 @@ void tst_QHash::equal_range() QCOMPARE(result.first, hash.find(1)); QVERIFY(std::distance(result.first, result.second) == 1); - QHash<int, int> h1; + QMultiHash<int, int> h1; { auto p = h1.equal_range(0); QVERIFY(p.first == p.second); @@ -1597,7 +2635,7 @@ void tst_QHash::equal_range() QVERIFY(p2.first == m1.begin() || p2.second == m1.end()); } - const QHash<int, int> ch1 = h1; + const QMultiHash<int, int> ch1 = h1; { auto p1 = ch1.equal_range(9); QVERIFY(p1.first == p1.second); @@ -1623,10 +2661,17 @@ void tst_QHash::equal_range() QVERIFY(p2.first == cm1.cbegin() || p2.second == cm1.cend()); } - QHash<int, int> h2; + { + const QMultiHash<int, int> cm2; + auto p1 = cm2.equal_range(0); + QVERIFY(p1.first == cm2.end()); + QVERIFY(p1.second == cm2.end()); + } + + QMultiHash<int, int> h2; for (int i = 0; i < 8; ++i) for (int j = 0; j < 8; ++j) - h2.insertMulti(i, i*j); + h2.insert(i, i*j); for (int i = 0; i < 8; ++i) { auto pair = h2.equal_range(i); @@ -1652,7 +2697,7 @@ void tst_QHash::insert_hash() hash.insert(hash2); - QCOMPARE(hash.count(), 5); + QCOMPARE(hash.size(), 5); for (int i = 0; i < 5; ++i) QCOMPARE(hash[i], i); } @@ -1664,7 +2709,7 @@ void tst_QHash::insert_hash() hash.insert(hash2); - QCOMPARE(hash.count(), 1); + QCOMPARE(hash.size(), 1); QCOMPARE(hash[0], 5); } { @@ -1674,7 +2719,7 @@ void tst_QHash::insert_hash() hash.insert(hash2); - QCOMPARE(hash.count(), 1); + QCOMPARE(hash.size(), 1); QCOMPARE(hash[0], 5); QCOMPARE(hash, hash2); } @@ -1687,25 +2732,508 @@ void tst_QHash::insert_hash() // insert into ourself, nothing should happen hash.insert(hash); - QCOMPARE(hash.count(), 3); + QCOMPARE(hash.size(), 3); QCOMPARE(hash[0], 7); QCOMPARE(hash[2], 5); QCOMPARE(hash[7], 55); } +} + +void tst_QHash::multiHashStoresInReverseInsertionOrder() +{ + const QString strings[] = { + u"zero"_s, + u"null"_s, + u"nada"_s, + }; + { + QMultiHash<int, QString> hash; + for (const QString &string : strings) + hash.insert(0, string); + auto printOnFailure = qScopeGuard([&] { qDebug() << hash; }); + QVERIFY(std::equal(hash.begin(), hash.end(), + std::rbegin(strings), std::rend(strings))); + printOnFailure.dismiss(); + } +} + +void tst_QHash::emplace() +{ + { + QHash<QString, MyClass> hash; + MyClass::copies = 0; + MyClass::moves = 0; + + hash.emplace(QString("a"), QString("a")); + QCOMPARE(hash["a"].str, "a"); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + hash.emplace(QString("ab"), QString("ab")); + QCOMPARE(hash["ab"].str, "ab"); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + hash.emplace(QString("ab"), QString("abc")); + QCOMPARE(hash["ab"].str, "abc"); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 1); + } + { + QMultiHash<QString, MyClass> hash; + MyClass::copies = 0; + MyClass::moves = 0; + + hash.emplace(QString("a"), QString("a")); + QCOMPARE(hash["a"].str, "a"); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + hash.emplace(QString("ab"), QString("ab")); + QCOMPARE(hash["ab"].str, "ab"); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + hash.emplace(QString("ab"), QString("abc")); + QCOMPARE(hash["ab"].str, "abc"); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 0); + hash.emplaceReplace(QString("ab"), QString("abcd")); + QCOMPARE(hash["ab"].str, "abcd"); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 1); + } +} + +struct BadKey { + int k; + BadKey(int i) : k(i) {} + bool operator==(const BadKey &other) const + { + return k == other.k; + } +}; + +size_t qHash(BadKey, size_t seed) +{ + return seed; +} + +void tst_QHash::badHashFunction() +{ + QHash<BadKey, int> hash; + for (int i = 0; i < 10000; ++i) + hash.insert(i, i); + + for (int i = 0; i < 10000; ++i) + QCOMPARE(hash.value(i), i); + + for (int i = 10000; i < 20000; ++i) + QVERIFY(!hash.contains(i)); + +} + +void tst_QHash::hashOfHash() +{ + QHash<int, int> hash; + (void)qHash(hash); + + QMultiHash<int, int> multiHash; + (void)qHash(multiHash); +} + +template <bool HasQHash_> +struct StdHashKeyType { + static inline constexpr bool HasQHash = HasQHash_; + static bool StdHashUsed; + + int i; + friend bool operator==(const StdHashKeyType &lhs, const StdHashKeyType &rhs) + { return lhs.i == rhs.i; } +}; + +template <bool HasQHash> +bool StdHashKeyType<HasQHash>::StdHashUsed = false; + +namespace std { +template <bool HasQHash> struct hash<StdHashKeyType<HasQHash>> +{ + size_t operator()(const StdHashKeyType<HasQHash> &s, size_t seed = 0) const { + StdHashKeyType<HasQHash>::StdHashUsed = true; + return hash<int>()(s.i) ^ seed; + } +}; +} + +template <bool HasQHash> +std::enable_if_t<HasQHash, size_t> +qHash(const StdHashKeyType<HasQHash> &s, size_t seed) +{ + return qHash(s.i, seed); +} + +template <typename T> +void stdHashImpl() +{ + QHash<T, int> hash; + for (int i = 0; i < 1000; ++i) + hash.insert(T{i}, i); + + QCOMPARE(hash.size(), 1000); + for (int i = 0; i < 1000; ++i) + QCOMPARE(hash.value(T{i}, -1), i); + + for (int i = 500; i < 1500; ++i) + hash.insert(T{i}, i); + + QCOMPARE(hash.size(), 1500); + for (int i = 0; i < 1500; ++i) + QCOMPARE(hash.value(T{i}, -1), i); + + qsizetype count = 0; + for (int i = -2000; i < 2000; ++i) { + if (hash.contains(T{i})) + ++count; + } + QCOMPARE(count, 1500); + QCOMPARE(T::StdHashUsed, !T::HasQHash); + + + std::unordered_set<T> set; + for (int i = 0; i < 1000; ++i) + set.insert(T{i}); + + for (int i = 500; i < 1500; ++i) + set.insert(T{i}); + + QCOMPARE(set.size(), size_t(1500)); + count = 0; + for (int i = -2000; i < 2000; ++i) + count += qsizetype(set.count(T{i})); + QCOMPARE(count, 1500); + QVERIFY(T::StdHashUsed); +} + +void tst_QHash::stdHash() +{ + stdHashImpl<StdHashKeyType<false>>(); + stdHashImpl<StdHashKeyType<true>>(); + + QSet<std::string> strings{ "a", "b", "c" }; + QVERIFY(strings.contains("a")); + QVERIFY(!strings.contains("z")); +} + +void tst_QHash::countInEmptyHash() +{ { - // This will use a QMultiHash and then insert that into QHash, - // the ordering is undefined so we won't test that but make - // sure this isn't adding multiple entries with the same key - // to the QHash. QHash<int, int> hash; - QMultiHash<int, int> hash2; - hash2.insert(0, 5); - hash2.insert(0, 6); - hash2.insert(0, 7); + QCOMPARE(hash.size(), 0); + QCOMPARE(hash.count(42), 0); + } - hash.insert(hash2); + { + QMultiHash<int, int> hash; + QCOMPARE(hash.size(), 0); + QCOMPARE(hash.count(42), 0); + QCOMPARE(hash.count(42, 1), 0); + } +} + +void tst_QHash::removeInEmptyHash() +{ + { + QHash<QString, int> hash; + QCOMPARE(hash.remove("test"), false); + QVERIFY(!hash.isDetached()); + + using Iter = QHash<QString, int>::iterator; + const auto removed = hash.removeIf([](Iter) { return true; }); + QCOMPARE(removed, 0); + } + { + QMultiHash<QString, int> hash; + QCOMPARE(hash.remove("key"), 0); + QCOMPARE(hash.remove("key", 1), 0); + QVERIFY(!hash.isDetached()); + + using Iter = QMultiHash<QString, int>::iterator; + const auto removed = hash.removeIf([](Iter) { return true; }); + QCOMPARE(removed, 0); + } +} + +template<typename T> +void valueInEmptyHashTestFunction() +{ + T hash; + QCOMPARE(hash.value("key"), 0); + QCOMPARE(hash.value("key", -1), -1); + QVERIFY(hash.values().isEmpty()); + QVERIFY(!hash.isDetached()); + + const T constHash; + QCOMPARE(constHash["key"], 0); +} + +void tst_QHash::valueInEmptyHash() +{ + valueInEmptyHashTestFunction<QHash<QString, int>>(); + if (QTest::currentTestFailed()) + return; + + valueInEmptyHashTestFunction<QMultiHash<QString, int>>(); +} + +void tst_QHash::fineTuningInEmptyHash() +{ + QHash<QString, int> hash; + QCOMPARE(hash.capacity(), 0); + hash.squeeze(); + QCOMPARE(hash.capacity(), 0); + QVERIFY(qFuzzyIsNull(hash.load_factor())); + QVERIFY(!hash.isDetached()); + + hash.reserve(10); + QVERIFY(hash.capacity() >= 10); + hash.squeeze(); + QVERIFY(hash.capacity() > 0); +} + +void tst_QHash::reserveShared() +{ + QHash<char, char> hash; + hash.insert('c', 'c'); + auto hash2 = hash; + + QCOMPARE(hash2.capacity(), hash.capacity()); + auto oldCap = hash.capacity(); + + hash2.reserve(100); // This shouldn't crash + + QVERIFY(hash2.capacity() >= 100); + QCOMPARE(hash.capacity(), oldCap); +} + +void tst_QHash::reserveLessThanCurrentAmount() +{ + { + QHash<int, int> hash; + for (int i = 0; i < 1000; ++i) + hash.insert(i, i * 10); + + // This used to hang in an infinite loop: QTBUG-102067 + hash.reserve(1); + + // Make sure that hash still has all elements + for (int i = 0; i < 1000; ++i) + QCOMPARE(hash.value(i), i * 10); + } + { + QMultiHash<int, int> hash; + for (int i = 0; i < 1000; ++i) { + hash.insert(i, i * 10); + hash.insert(i, i * 10 + 1); + } + + // This used to hang in infinite loop: QTBUG-102067 + hash.reserve(1); + + // Make sure that hash still has all elements + for (int i = 0; i < 1000; ++i) + QCOMPARE(hash.values(i), QList<int>({ i * 10 + 1, i * 10 })); + } +} + +void tst_QHash::reserveKeepCapacity_data() +{ + QTest::addColumn<qsizetype>("requested"); + auto addRow = [](qsizetype requested) { + QTest::addRow("%td", ptrdiff_t(requested)) << requested; + }; + + QHash<int, int> testHash = {{1, 1}}; + qsizetype minCapacity = testHash.capacity(); + addRow(minCapacity - 1); + addRow(minCapacity + 0); + addRow(minCapacity + 1); + addRow(2 * minCapacity - 1); + addRow(2 * minCapacity + 0); + addRow(2 * minCapacity + 1); +} + +void tst_QHash::reserveKeepCapacity() +{ + QFETCH(qsizetype, requested); + + QHash<qsizetype, qsizetype> hash; + hash.reserve(requested); + qsizetype initialCapacity = hash.capacity(); + QCOMPARE_GE(initialCapacity, requested); + + // insert this many elements into the hash + for (qsizetype i = 0; i < requested; ++i) + hash.insert(i, i); + + // it mustn't have increased capacity after inserting the elements + QCOMPARE(hash.capacity(), initialCapacity); +} + +void tst_QHash::QTBUG98265() +{ + QMultiHash<QUuid, QByteArray> a; + QMultiHash<QUuid, QByteArray> b; + a.insert(QUuid("3e0dfb4d-90eb-43a4-bd54-88f5b69832c1"), QByteArray()); + b.insert(QUuid("1b710ada-3dd7-432e-b7c8-e852e59f46a0"), QByteArray()); + + QVERIFY(a != b); +} + +/* + Calling functions which take a const-ref argument for a key with a reference + to a key inside the hash itself should keep the key valid as long as it is + needed. If not users may get hard-to-debug races where CoW should've + shielded them. +*/ +void tst_QHash::detachAndReferences() +{ + // Repeat a few times because it's not a guarantee + for (int i = 0; i < 50; ++i) { + QHash<char, char> hash; + hash.insert('a', 'a'); + hash.insert('b', 'a'); + hash.insert('c', 'a'); + hash.insert('d', 'a'); + hash.insert('e', 'a'); + hash.insert('f', 'a'); + hash.insert('g', 'a'); + + QSemaphore sem; + QSemaphore sem2; + std::thread th([&sem, &sem2, hash]() mutable { + sem.release(); + sem2.acquire(); + hash.reserve(100); // [2]: ...then this rehashes directly, without detaching + }); + + // The key is a reference to an entry in the hash. If we were already + // detached then no problem occurs! The problem happens because _after_ + // we detach but before using the key the other thread resizes and + // rehashes, leaving our const-ref dangling. + auto it = hash.constBegin(); + const auto &key = it.key(); // [3]: leaving our const-refs dangling + auto kCopy = key; + const auto &value = it.value(); + auto vCopy = value; + sem2.release(); + sem.acquire(); + hash.insert(key, value); // [1]: this detaches first... + + th.join(); + QCOMPARE(hash.size(), 7); + QVERIFY(hash.contains(kCopy)); + QCOMPARE(hash.value(kCopy), vCopy); + } +} + +void tst_QHash::lookupUsingKeyIterator() +{ + QHash<QString, QString> hash; + hash.reserve(1); + qsizetype minCapacity = hash.capacity(); + // Beholden to internal implementation details: + qsizetype rehashLimit = minCapacity == 64 ? 63 : 8; + + for (char16_t c = u'a'; c <= u'a' + rehashLimit; ++c) + hash.insert(QString(QChar(c)), u"h"_s); + + for (auto it = hash.keyBegin(), end = hash.keyEnd(); it != end; ++it) + QVERIFY(!hash[*it].isEmpty()); +} + +void tst_QHash::squeeze() +{ + { + QHash<int, int> hash; + hash.reserve(1000); + for (int i = 0; i < 10; ++i) + hash.insert(i, i * 10); + QVERIFY(hash.isDetached()); + const size_t buckets = hash.bucket_count(); + const qsizetype size = hash.size(); + + hash.squeeze(); + + QVERIFY(hash.bucket_count() < buckets); + QCOMPARE(hash.size(), size); + for (int i = 0; i < size; ++i) + QCOMPARE(hash.value(i), i * 10); + } + { + QMultiHash<int, int> hash; + hash.reserve(1000); + for (int i = 0; i < 10; ++i) { + hash.insert(i, i * 10); + hash.insert(i, i * 10 + 1); + } + QVERIFY(hash.isDetached()); + const size_t buckets = hash.bucket_count(); + const qsizetype size = hash.size(); + + hash.squeeze(); + + QVERIFY(hash.bucket_count() < buckets); + QCOMPARE(hash.size(), size); + for (int i = 0; i < (size / 2); ++i) + QCOMPARE(hash.values(i), QList<int>({ i * 10 + 1, i * 10 })); + } +} + +void tst_QHash::squeezeShared() +{ + { + QHash<int, int> hash; + hash.reserve(1000); + for (int i = 0; i < 10; ++i) + hash.insert(i, i * 10); + + QHash<int, int> other = hash; + + // Check that when squeezing a hash with shared d_ptr, the number of + // buckets actually decreases. + QVERIFY(!other.isDetached()); + const size_t buckets = other.bucket_count(); + const qsizetype size = other.size(); + + other.squeeze(); + + QCOMPARE(hash.bucket_count(), buckets); + QVERIFY(other.bucket_count() < buckets); + + QCOMPARE(other.size(), size); + for (int i = 0; i < size; ++i) + QCOMPARE(other.value(i), i * 10); + } + { + QMultiHash<int, int> hash; + hash.reserve(1000); + for (int i = 0; i < 10; ++i) { + hash.insert(i, i * 10); + hash.insert(i, i * 10 + 1); + } + + QMultiHash<int, int> other = hash; + + // Check that when squeezing a hash with shared d_ptr, the number of + // buckets actually decreases. + QVERIFY(!other.isDetached()); + const size_t buckets = other.bucket_count(); + const qsizetype size = other.size(); + + other.squeeze(); + + QCOMPARE(hash.bucket_count(), buckets); + QVERIFY(other.bucket_count() < buckets); - QCOMPARE(hash.count(), 1); + QCOMPARE(other.size(), size); + for (int i = 0; i < (size / 2); ++i) + QCOMPARE(other.values(i), QList<int>({ i * 10 + 1, i * 10 })); } } diff --git a/tests/auto/corelib/tools/qhashfunctions/CMakeLists.txt b/tests/auto/corelib/tools/qhashfunctions/CMakeLists.txt new file mode 100644 index 0000000000..6cbba503dc --- /dev/null +++ b/tests/auto/corelib/tools/qhashfunctions/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qhashfunctions Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qhashfunctions LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qhashfunctions + SOURCES + tst_qhashfunctions.cpp +) diff --git a/tests/auto/corelib/tools/qhashfunctions/qhashfunctions.pro b/tests/auto/corelib/tools/qhashfunctions/qhashfunctions.pro deleted file mode 100644 index 853e9f30e5..0000000000 --- a/tests/auto/corelib/tools/qhashfunctions/qhashfunctions.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qhashfunctions -QT = core testlib -SOURCES = $$PWD/tst_qhashfunctions.cpp diff --git a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp index f76f3aa0c6..00ee5763ed 100644 --- a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp +++ b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp @@ -1,34 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2024 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QVarLengthArray> #include <qhash.h> +#include <qfloat16.h> #include <iterator> #include <sstream> @@ -40,22 +18,32 @@ class tst_QHashFunctions : public QObject { Q_OBJECT public: - enum { - // random value - RandomSeed = 1045982819 - }; - uint seed; + // random values + static constexpr quint64 ZeroSeed = 0; + static constexpr quint64 RandomSeed32 = 1045982819; + static constexpr quint64 RandomSeed64 = QtPrivate::QHashCombine{}(RandomSeed32, RandomSeed32); + size_t seed; + + template <typename T1, typename T2> void stdPair_template(const T1 &t1, const T2 &t2); public slots: void initTestCase(); void init(); private Q_SLOTS: - void consistent(); + void unsignedIntegerConsistency_data(); + void unsignedIntegerConsistency(); + void signedIntegerConsistency_data(); + void signedIntegerConsistency(); + void extendedIntegerConsistency(); + void floatingPointConsistency_data(); + void floatingPointConsistency(); + void stringConsistency_data(); + void stringConsistency(); void qhash(); void qhash_of_empty_and_null_qstring(); void qhash_of_empty_and_null_qbytearray(); - void fp_qhash_of_zero_is_seed(); + void qhash_of_zero_floating_points(); void qthash_data(); void qthash(); void range(); @@ -63,33 +51,289 @@ private Q_SLOTS: void stdHash(); + void stdPair_int_int() { stdPair_template(1, 2); } + void stdPair_ulong_llong() { stdPair_template(1UL, -2LL); } + void stdPair_ullong_long() { stdPair_template(1ULL, -2L); } + void stdPair_string_int() { stdPair_template(QString("Hello"), 2); } + void stdPair_int_string() { stdPair_template(1, QString("Hello")); } + void stdPair_bytearray_string() { stdPair_template(QByteArray("Hello"), QString("World")); } + void stdPair_string_bytearray() { stdPair_template(QString("Hello"), QByteArray("World")); } + void stdPair_int_pairIntInt() { stdPair_template(1, std::make_pair(2, 3)); } + void stdPair_2x_pairIntInt() { stdPair_template(std::make_pair(1, 2), std::make_pair(2, 3)); } + void stdPair_string_pairIntInt() { stdPair_template(QString("Hello"), std::make_pair(42, -47)); } // QTBUG-92910 + void stdPair_int_pairIntPairIntInt() { stdPair_template(1, std::make_pair(2, std::make_pair(3, 4))); } + + void enum_int_consistent_hash_qtbug108032(); + +#if QT_DEPRECATED_SINCE(6, 6) void setGlobalQHashSeed(); +#endif }; -void tst_QHashFunctions::consistent() +void tst_QHashFunctions::initTestCase() { - // QString-like - { - const QString s = QStringLiteral("abcdefghijklmnopqrstuvxyz").repeated(16); + QTest::addColumn<quint64>("seedValue"); + + QTest::newRow("zero-seed") << ZeroSeed; + QTest::newRow("zero-seed-negated") << ~ZeroSeed; + QTest::newRow("non-zero-seed-32bit") << RandomSeed32; + QTest::newRow("non-zero-seed-32bit-negated") + << quint64{~quint32(RandomSeed32)}; // ensure this->seed gets same value on 32/64-bit + if constexpr (sizeof(size_t) == sizeof(quint64)) { + QTest::newRow("non-zero-seed-64bit") << RandomSeed64; + QTest::newRow("non-zero-seed-64bit-negated") << ~RandomSeed64; + } +} + +void tst_QHashFunctions::init() +{ + QFETCH_GLOBAL(quint64, seedValue); + seed = size_t(seedValue); +} - QCOMPARE(qHash(s), qHash(QStringRef(&s))); - QCOMPARE(qHash(s), qHash(QStringView(s))); +template <typename T> static void addPositiveCommonRows() +{ + QTest::addRow("zero") << T(0); + QTest::addRow("positive_7bit") << T(42); + QTest::addRow("positive_15bit") << T(0x1f3f); + QTest::addRow("positive_31bit") << T(0x4b3d'93c4); + QTest::addRow("positive_63bit") << T(Q_INT64_C(0x39df'7338'4b14'fcb0)); + + QTest::addRow("SCHAR_MAX") << T(SCHAR_MAX); + QTest::addRow("SHRT_MAX") << T(SHRT_MAX); + QTest::addRow("INT_MAX") << T(INT_MAX); + QTest::addRow("LLONG_MAX") << T(LLONG_MAX); +} + +void tst_QHashFunctions::signedIntegerConsistency_data() +{ + QTest::addColumn<qint64>("value"); + addPositiveCommonRows<qint64>(); + QTest::addRow("negative_7bit") << Q_INT64_C(-28); + QTest::addRow("negative_15bit") << Q_INT64_C(-0x387c); + QTest::addRow("negative_31bit") << qint64(-0x7713'30f9); + + QTest::addRow("SCHAR_MIN") << qint64(SCHAR_MIN); + QTest::addRow("SHRT_MIN") << qint64(SHRT_MIN); + QTest::addRow("INT_MIN") << qint64(INT_MIN); + QTest::addRow("LLONG_MIN") << LLONG_MIN; +} + +void tst_QHashFunctions::unsignedIntegerConsistency_data() +{ + QTest::addColumn<quint64>("value"); + addPositiveCommonRows<quint64>(); + + QTest::addRow("positive_8bit") << Q_UINT64_C(0xE4); + QTest::addRow("positive_16bit") << Q_UINT64_C(0xcafe); + QTest::addRow("positive_32bit") << quint64(0xcafe'babe); + + QTest::addRow("UCHAR_MAX") << quint64(UCHAR_MAX); + QTest::addRow("UHRT_MAX") << quint64(USHRT_MAX); + QTest::addRow("UINT_MAX") << quint64(UINT_MAX); + QTest::addRow("ULLONG_MAX") << ULLONG_MAX; +} + +static void unsignedIntegerConsistency(quint64 value, size_t seed) +{ + quint8 v8 = quint8(value); + quint16 v16 = quint16(value); + quint32 v32 = quint32(value); + + const auto hu8 = qHash(v8, seed); + const auto hu16 = qHash(v16, seed); + const auto hu32 = qHash(v32, seed); + const auto hu64 = qHash(value, seed); + + if (v8 == value) + QCOMPARE(hu8, hu32); + if (v16 == value) + QCOMPARE(hu16, hu32); + if (v32 == value) + QCOMPARE(hu64, hu32); + +#if QT_SUPPORTS_INT128 + const auto hu128 = qHash(quint128(value), seed); + QCOMPARE(hu128, hu64); +#endif + + // there are a few more unsigned types: +#ifdef __cpp_char8_t + const auto hc8 = qHash(char8_t(value), seed); +#endif + const auto hc16 = qHash(char16_t(value), seed); + const auto hc32 = qHash(char32_t(value), seed); +#ifdef __cpp_char8_t + QCOMPARE(hc8, hu8); +#endif + QCOMPARE(hc16, hu16); + QCOMPARE(hc32, hu32); +} + +void tst_QHashFunctions::unsignedIntegerConsistency() +{ + QFETCH(quint64, value); + ::unsignedIntegerConsistency(value, seed); +} + +void tst_QHashFunctions::signedIntegerConsistency() +{ + QFETCH(qint64, value); + qint8 v8 = qint8(value); + qint16 v16 = qint16(value); + qint32 v32 = qint32(value); + + const auto hs8 = qHash(v8, seed); + const auto hs16 = qHash(v16, seed); + const auto hs32 = qHash(v32, seed); + const auto hs64 = qHash(value, seed); + + if (v8 == value) + QCOMPARE(hs8, hs32); + if (v16 == value) + QCOMPARE(hs16, hs32); + if (v32 == value) { + // because of QTBUG-116080, this may not match, but we can't guarantee + // it mismatches 100% of the time either + if constexpr (sizeof(size_t) > sizeof(int) || QT_VERSION_MAJOR > 6) + QCOMPARE(hs64, hs32); + } + +#if QT_SUPPORTS_INT128 + const auto hs128 = qHash(qint128(value), seed); + QCOMPARE(hs128, hs64); +#endif + + if (value > 0) { + quint64 u64 = quint64(value); + const auto hu64 = qHash(u64, seed); + QCOMPARE(hu64, hs64); + ::unsignedIntegerConsistency(u64, seed); + // by A == B && B == C -> A == C, we've shown hsXX == huXX for all XX } } -void tst_QHashFunctions::initTestCase() +void tst_QHashFunctions::extendedIntegerConsistency() +{ +#ifdef QT_SUPPORTS_INT128 + // We only need to check qint128 and quint128 consistency here. + qint128 v65bit = Q_INT128_C(0x1'abea'06b7'dcf5'106a); + qint128 v127bit = Q_INT128_C(0x387c'ac7a'22a0'5242'9ee9'bcaa'6a53'13af); + + QCOMPARE(qHash(quint128(v65bit), seed), qHash(v65bit, seed)); + QCOMPARE(qHash(quint128(v127bit), seed), qHash(v127bit, seed)); +#else + QSKIP("This platform does not support extended integer types."); +#endif +} + +void tst_QHashFunctions::floatingPointConsistency_data() { - Q_STATIC_ASSERT(int(RandomSeed) > 0); + QTest::addColumn<double>("value"); + QTest::addRow("zero") << 0.0; - QTest::addColumn<uint>("seedValue"); - QTest::newRow("zero-seed") << 0U; - QTest::newRow("non-zero-seed") << uint(RandomSeed); + QTest::addRow("1.0") << 1.0; + QTest::addRow("infinity") << std::numeric_limits<double>::infinity(); + + QTest::addRow("fp16_epsilon") << double(std::numeric_limits<qfloat16>::epsilon()); + QTest::addRow("fp16_min") << double(std::numeric_limits<qfloat16>::min()); + QTest::addRow("fp16_max") << double(std::numeric_limits<qfloat16>::max()); + + QTest::addRow("float_epsilon") << double(std::numeric_limits<float>::epsilon()); + QTest::addRow("float_min") << double(std::numeric_limits<float>::min()); + QTest::addRow("float_max") << double(std::numeric_limits<float>::max()); + + QTest::addRow("double_epsilon") << double(std::numeric_limits<double>::epsilon()); + QTest::addRow("double_min") << double(std::numeric_limits<double>::min()); + QTest::addRow("double_max") << double(std::numeric_limits<double>::max()); } -void tst_QHashFunctions::init() +void tst_QHashFunctions::floatingPointConsistency() +{ + QFETCH(double, value); + long double lvalue = value; + float fp32 = float(value); + qfloat16 fp16 = qfloat16(value); + + const auto hfld = qHash(lvalue, seed); + const auto hf64 = qHash(value, seed); + const auto hf32 = qHash(fp32, seed); + const auto hf16 = qHash(fp16, seed); + + const auto hnfld = qHash(-lvalue, seed); + const auto hnf64 = qHash(-value, seed); + const auto hnf32 = qHash(-fp32, seed); + const auto hnf16 = qHash(-fp16, seed); + + if (fp16 == fp32) { + QCOMPARE(hf16, hf32); + QCOMPARE(hnf16, hnf32); + } + + // See QTBUG-116077; the rest isn't guaranteed to match (but we can't + // guarantee it will mismatch either). + return; + + if (fp32 == value) { + QCOMPARE(hf32, hf64); + QCOMPARE(hnf32, hnf64); + } + + QCOMPARE(hfld, hf64); + QCOMPARE(hnfld, hnf64); +} + +void tst_QHashFunctions::stringConsistency_data() { - QFETCH_GLOBAL(uint, seedValue); - seed = seedValue; + QTest::addColumn<QString>("value"); + QTest::newRow("null") << QString(); + QTest::newRow("empty") << ""; + QTest::newRow("withnull") << QStringLiteral("A\0z"); + QTest::newRow("short-ascii") << "Hello"; // 10 bytes + QTest::newRow("medium-ascii") << "Hello, World"; // 24 bytes + QTest::newRow("long-ascii") << QStringLiteral("abcdefghijklmnopqrstuvxyz").repeated(16); + + QTest::newRow("short-latin1") << "Bokmål"; + QTest::newRow("medium-latin1") << "Det går bra!"; // 24 bytes + QTest::newRow("long-latin1") + << R"(Alle mennesker er født frie og med samme menneskeverd og menneskerettigheter. + De er utstyrt med fornuft og samvittighet og bør handle mot hverandre i brorskapets ånd.)"; + + QTest::newRow("short-nonlatin1") << "Ελληνικά"; + QTest::newRow("long-nonlatin1") + << R"('Ολοι οι άνθρωποι γεννιούνται ελεύθεροι και ίσοι στην αξιοπρέπεια και τα + δικαιώματα. Είναι προικισμένοι με λογική και συνείδηση, και οφείλουν να συμπεριφέρονται μεταξύ + τους με πνεύμα αδελφοσύνης.)"; +} + +void tst_QHashFunctions::stringConsistency() +{ + QFETCH(QString, value); + QStringView sv = value; + QByteArray u8ba = value.toUtf8(); + QByteArray u8bav = u8ba; + + // sanity checking: + QCOMPARE(sv.isNull(), value.isNull()); + QCOMPARE(sv.isEmpty(), value.isEmpty()); + QCOMPARE(u8ba.isNull(), value.isNull()); + QCOMPARE(u8ba.isEmpty(), value.isEmpty()); + QCOMPARE(u8bav.isNull(), value.isNull()); + QCOMPARE(u8bav.isEmpty(), value.isEmpty()); + + QCOMPARE(qHash(sv, seed), qHash(value, seed)); + QCOMPARE(qHash(u8bav, seed), qHash(u8ba, seed)); + + if (seed == 0 || QHashHeterogeneousSearch<QString, QLatin1StringView>::value) { + QByteArray l1ba = value.toLatin1(); + QLatin1StringView l1sv(l1ba.data(), l1ba.size()); +#ifdef Q_PROCESSOR_ARM + // zero-extending aeshash not implemented on ARM +#else + if (value == l1sv) + QCOMPARE(qHash(l1sv, seed), qHash(value, seed)); +#endif + } } void tst_QHashFunctions::qhash() @@ -97,7 +341,6 @@ void tst_QHashFunctions::qhash() { QBitArray a1; QBitArray a2; - QCOMPARE(qHash(a1, seed), seed); a1.resize(1); a1.setBit(0, true); @@ -105,8 +348,8 @@ void tst_QHashFunctions::qhash() a2.resize(1); a2.setBit(0, false); - uint h1 = qHash(a1, seed); - uint h2 = qHash(a2, seed); + size_t h1 = qHash(a1, seed); + size_t h2 = qHash(a2, seed); QVERIFY(h1 != h2); // not guaranteed @@ -124,14 +367,14 @@ void tst_QHashFunctions::qhash() QVERIFY(h1 == h2); a2.setBit(0, false); - uint h3 = qHash(a2, seed); + size_t h3 = qHash(a2, seed); QVERIFY(h2 != h3); // not guaranteed a2.setBit(0, true); QVERIFY(h2 == qHash(a2, seed)); a2.setBit(6, false); - uint h4 = qHash(a2, seed); + size_t h4 = qHash(a2, seed); QVERIFY(h2 != h4); // not guaranteed a2.setBit(6, true); @@ -177,10 +420,6 @@ void tst_QHashFunctions::qhash_of_empty_and_null_qstring() QCOMPARE(null, empty); QCOMPARE(qHash(null, seed), qHash(empty, seed)); - QStringRef nullRef, emptyRef(&empty); - QCOMPARE(nullRef, emptyRef); - QCOMPARE(qHash(nullRef, seed), qHash(emptyRef, seed)); - QStringView nullView, emptyView(empty); QCOMPARE(nullView, emptyView); QCOMPARE(qHash(nullView, seed), qHash(emptyView, seed)); @@ -193,18 +432,11 @@ void tst_QHashFunctions::qhash_of_empty_and_null_qbytearray() QCOMPARE(qHash(null, seed), qHash(empty, seed)); } -void tst_QHashFunctions::fp_qhash_of_zero_is_seed() +void tst_QHashFunctions::qhash_of_zero_floating_points() { - QCOMPARE(qHash(-0.0f, seed), seed); - QCOMPARE(qHash( 0.0f, seed), seed); - - QCOMPARE(qHash(-0.0 , seed), seed); - QCOMPARE(qHash( 0.0 , seed), seed); - -#ifndef Q_OS_DARWIN - QCOMPARE(qHash(-0.0L, seed), seed); - QCOMPARE(qHash( 0.0L, seed), seed); -#endif + QCOMPARE(qHash(-0.0f, seed), qHash(0.0f, seed)); + QCOMPARE(qHash(-0.0 , seed), qHash(0.0 , seed)); + QCOMPARE(qHash(-0.0L, seed), qHash(0.0L, seed)); } void tst_QHashFunctions::qthash_data() @@ -228,10 +460,16 @@ void tst_QHashFunctions::qthash() namespace SomeNamespace { struct Hashable { int i; }; - inline uint qHash(Hashable h, uint seed = 0) + inline size_t qHash(Hashable h, size_t seed = 0) { return QT_PREPEND_NAMESPACE(qHash)(h.i, seed); } -} + struct AdlHashable { + int i; + private: + friend size_t qHash(AdlHashable h, size_t seed = 0) + { return QT_PREPEND_NAMESPACE(qHash)(h.i, seed); } + }; +} void tst_QHashFunctions::range() { static const int ints[] = {0, 1, 2, 3, 4, 5}; @@ -246,17 +484,23 @@ void tst_QHashFunctions::range() { // verify that the input iterator category suffices: std::stringstream sstream; - Q_STATIC_ASSERT((std::is_same<std::input_iterator_tag, std::istream_iterator<int>::iterator_category>::value)); + static_assert((std::is_same<std::input_iterator_tag, std::istream_iterator<int>::iterator_category>::value)); std::copy(ints, ints + numInts, std::ostream_iterator<int>(sstream, " ")); sstream.seekg(0); std::istream_iterator<int> it(sstream), end; QCOMPARE(qHashRange(ints, ints + numInts, seed), qHashRange(it, end, seed)); } - SomeNamespace::Hashable hashables[] = {{0}, {1}, {2}, {3}, {4}, {5}}; - static const size_t numHashables = sizeof hashables / sizeof *hashables; - // compile check: is qHash() found using ADL? - (void)qHashRange(hashables, hashables + numHashables, seed); + { + SomeNamespace::Hashable hashables[] = {{0}, {1}, {2}, {3}, {4}, {5}}; + // compile check: is qHash() found using ADL? + [[maybe_unused]] auto r = qHashRange(std::begin(hashables), std::end(hashables), seed); + } + { + SomeNamespace::AdlHashable hashables[] = {{0}, {1}, {2}, {3}, {4}, {5}}; + // compile check: is qHash() found as a hidden friend? + [[maybe_unused]] auto r = qHashRange(std::begin(hashables), std::end(hashables), seed); + } } void tst_QHashFunctions::rangeCommutative() @@ -279,15 +523,47 @@ void tst_QHashFunctions::rangeCommutative() QCOMPARE(qHashRangeCommutative(ints, ints + numInts, seed), qHashRangeCommutative(it, end, seed)); } - SomeNamespace::Hashable hashables[] = {{0}, {1}, {2}, {3}, {4}, {5}}; - static const size_t numHashables = sizeof hashables / sizeof *hashables; - // compile check: is qHash() found using ADL? - (void)qHashRangeCommutative(hashables, hashables + numHashables, seed); + { + SomeNamespace::Hashable hashables[] = {{0}, {1}, {2}, {3}, {4}, {5}}; + // compile check: is qHash() found using ADL? + [[maybe_unused]] auto r = qHashRangeCommutative(std::begin(hashables), std::end(hashables), seed); + } + { + SomeNamespace::AdlHashable hashables[] = {{0}, {1}, {2}, {3}, {4}, {5}}; + // compile check: is qHash() found as a hidden friend? + [[maybe_unused]] auto r = qHashRangeCommutative(std::begin(hashables), std::end(hashables), seed); + } } +// QVarLengthArray these days has a qHash() as a hidden friend. +// This checks that QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH can deal with that: + +QT_BEGIN_NAMESPACE +QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(QVarLengthArray<QVector<int>>) +QT_END_NAMESPACE + void tst_QHashFunctions::stdHash() { { + std::unordered_set<QVarLengthArray<QVector<int>>> s = { + { + {0, 1, 2}, + {42, 43, 44}, + {}, + }, { + {11, 12, 13}, + {}, + }, + }; + QCOMPARE(s.size(), 2UL); + s.insert({ + {11, 12, 13}, + {}, + }); + QCOMPARE(s.size(), 2UL); + } + + { std::unordered_set<QString> s = {QStringLiteral("Hello"), QStringLiteral("World")}; QCOMPARE(s.size(), 2UL); s.insert(QStringLiteral("Hello")); @@ -315,10 +591,50 @@ void tst_QHashFunctions::stdHash() QCOMPARE(s.size(), 2UL); } + { + std::unordered_set<QChar> s = {u'H', u'W'}; + QCOMPARE(s.size(), 2UL); + s.insert(u'H'); + QCOMPARE(s.size(), 2UL); + } + +} + +template <typename T1, typename T2> +void tst_QHashFunctions::stdPair_template(const T1 &t1, const T2 &t2) +{ + std::pair<T1, T2> dpair{}; + std::pair<T1, T2> vpair{t1, t2}; + + // confirm proper working of the pair and of the underlying types + QVERIFY(t1 == t1); + QVERIFY(t2 == t2); + QCOMPARE(qHash(t1, seed), qHash(t1, seed)); + QCOMPARE(qHash(t2, seed), qHash(t2, seed)); + + QVERIFY(dpair == dpair); + QVERIFY(vpair == vpair); + + // therefore their hashes should be equal + QCOMPARE(qHash(dpair, seed), qHash(dpair, seed)); + QCOMPARE(qHash(vpair, seed), qHash(vpair, seed)); +} + +void tst_QHashFunctions::enum_int_consistent_hash_qtbug108032() +{ + enum E { E1, E2, E3 }; + + static_assert(QHashPrivate::HasQHashSingleArgOverload<E>); + + QCOMPARE(qHash(E1, seed), qHash(int(E1), seed)); + QCOMPARE(qHash(E2, seed), qHash(int(E2), seed)); + QCOMPARE(qHash(E3, seed), qHash(int(E3), seed)); } +#if QT_DEPRECATED_SINCE(6, 6) void tst_QHashFunctions::setGlobalQHashSeed() { +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED // Setter works as advertised qSetGlobalQHashSeed(0); QCOMPARE(qGlobalQHashSeed(), 0); @@ -331,7 +647,9 @@ void tst_QHashFunctions::setGlobalQHashSeed() // Reset works as advertised qSetGlobalQHashSeed(-1); QVERIFY(qGlobalQHashSeed() > 0); +QT_WARNING_POP } +#endif // QT_DEPRECATED_SINCE(6, 6) QTEST_APPLESS_MAIN(tst_QHashFunctions) #include "tst_qhashfunctions.moc" diff --git a/tests/auto/corelib/tools/qhashseed/CMakeLists.txt b/tests/auto/corelib/tools/qhashseed/CMakeLists.txt new file mode 100644 index 0000000000..27b4cce133 --- /dev/null +++ b/tests/auto/corelib/tools/qhashseed/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qhashseed Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qhashseed LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qhashseed + SOURCES + tst_qhashseed.cpp +) + +qt_internal_add_executable(tst_qhashseed_helper + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + SOURCES + tst_qhashseed_helper.cpp +) diff --git a/tests/auto/corelib/tools/qhashseed/tst_qhashseed.cpp b/tests/auto/corelib/tools/qhashseed/tst_qhashseed.cpp new file mode 100644 index 0000000000..99fc7c5772 --- /dev/null +++ b/tests/auto/corelib/tools/qhashseed/tst_qhashseed.cpp @@ -0,0 +1,186 @@ +// Copyright (C) 2021 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> + +#include <qhashfunctions.h> +#if QT_CONFIG(process) +#include <qprocess.h> +#endif + +class tst_QHashSeed : public QObject +{ + Q_OBJECT +public: + static void initMain(); + +private Q_SLOTS: + void initTestCase(); + void environmentVariable_data(); + void environmentVariable(); + void deterministicSeed(); + void reseeding(); + void quality(); +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) + void compatibilityApi(); + void deterministicSeed_compat(); +#endif +}; + +void tst_QHashSeed::initMain() +{ + qunsetenv("QT_HASH_SEED"); +} + +void tst_QHashSeed::initTestCase() +{ + // in case the qunsetenv above didn't work + if (qEnvironmentVariableIsSet("QT_HASH_SEED")) + QSKIP("QT_HASH_SEED environment variable is set, please don't do that"); +} + +void tst_QHashSeed::environmentVariable_data() +{ +#ifdef Q_OS_ANDROID + QSKIP("This test needs a helper binary, so is excluded from this platform."); +#endif + + QTest::addColumn<QByteArray>("envVar"); + QTest::addColumn<bool>("isZero"); + QTest::newRow("unset-environment") << QByteArray() << false; + QTest::newRow("empty-environment") << QByteArray("") << false; + QTest::newRow("zero-seed") << QByteArray("0") << true; +} + +void tst_QHashSeed::environmentVariable() +{ + #if QT_CONFIG(process) + QFETCH(QByteArray, envVar); + QFETCH(bool, isZero); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + if (envVar.isNull()) + env.remove("QT_HASH_SEED"); + else + env.insert("QT_HASH_SEED", envVar); + + QProcess helper; + helper.setProcessEnvironment(env); + helper.setProgram("./tst_qhashseed_helper"); + helper.start(); + QVERIFY2(helper.waitForStarted(5000), qPrintable(helper.errorString())); + QVERIFY2(helper.waitForFinished(5000), qPrintable(helper.errorString())); + QCOMPARE(helper.exitStatus(), 0); + + QByteArray line1 = helper.readLine().trimmed(); + QByteArray line2 = helper.readLine().trimmed(); + QCOMPARE(line2, line1); + QCOMPARE(line1 == "0", isZero); +#endif +} + +void tst_QHashSeed::deterministicSeed() +{ + QHashSeed::setDeterministicGlobalSeed(); + QCOMPARE(size_t(QHashSeed::globalSeed()), size_t(0)); + + // now reset + QHashSeed::resetRandomGlobalSeed(); + QVERIFY(QHashSeed::globalSeed() != 0); +} + +void tst_QHashSeed::reseeding() +{ + constexpr int Iterations = 4; + size_t seeds[Iterations]; + for (int i = 0; i < Iterations; ++i) { + seeds[i] = QHashSeed::globalSeed(); + QHashSeed::resetRandomGlobalSeed(); + } + + // verify that they are all different + QString fmt = QStringLiteral("seeds[%1] = 0x%3, seeds[%2] = 0x%4"); + for (int i = 0; i < Iterations; ++i) { + for (int j = i + 1; j < Iterations; ++j) { + QVERIFY2(seeds[i] != seeds[j], + qPrintable(fmt.arg(i).arg(j).arg(seeds[i], 16).arg(seeds[j], 16))); + } + } +} + +void tst_QHashSeed::quality() +{ + // this "bad seed" is used internally in qhash.cpp and should never leak! + constexpr size_t BadSeed = size_t(Q_UINT64_C(0x5555'5555'5555'5555)); + + constexpr int Iterations = 24; // nicely divisible by 3 + int oneThird = 0; + int badSeeds = 0; + int seedsToMinus1 = 0; + size_t ored = 0; + + for (int i = 0; i < Iterations; ++i) { + size_t seed = QHashSeed::globalSeed(); + ored |= seed; + int bits = qPopulationCount(quintptr(seed)); + QVERIFY2(bits > 0, QByteArray::number(bits)); // mandatory + + if (bits >= std::numeric_limits<size_t>::digits / 3) + ++oneThird; + if (seed == BadSeed) + ++badSeeds; + if (ored != size_t(-1)) + ++seedsToMinus1; + + QHashSeed::resetRandomGlobalSeed(); + } + + // report out + qInfo() << "Number of seeds until all bits became set:" << seedsToMinus1 << '/' << Iterations; + qInfo() << "Number of seeds with at least one third of the bits set:" + << oneThird << '/' << Iterations; + + // we must have set all bits after all the iterations + QCOMPARE(ored, size_t(-1)); + + // at least one third of the seeds must have one third of all the bits set + QVERIFY(oneThird > (Iterations/3)); + + // at most one seed can be the bad seed, if 32-bit, none on 64-bit + if (std::numeric_limits<size_t>::digits > 32) + QCOMPARE(badSeeds, 0); + else + QVERIFY2(badSeeds <= 1, "badSeeds = " + QByteArray::number(badSeeds)); + + // we must have taken at most two thirds of the iterations to have set each + // bit at least once + QVERIFY2(seedsToMinus1 < 2*Iterations/3, + "seedsToMinus1 = " + QByteArray::number(seedsToMinus1)); +} + +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) +QT_WARNING_DISABLE_DEPRECATED +void tst_QHashSeed::compatibilityApi() +{ + int oldSeed = qGlobalQHashSeed(); + size_t newSeed = QHashSeed::globalSeed(); + + QCOMPARE(size_t(oldSeed), newSeed & size_t(INT_MAX)); +} + +void tst_QHashSeed::deterministicSeed_compat() +{ + // same as above, but using the compat API + qSetGlobalQHashSeed(0); + QCOMPARE(size_t(QHashSeed::globalSeed()), size_t(0)); + QCOMPARE(qGlobalQHashSeed(), 0); + + // now reset + qSetGlobalQHashSeed(-1); + QVERIFY(QHashSeed::globalSeed() != 0); + QVERIFY(qGlobalQHashSeed() != 0); + QVERIFY(qGlobalQHashSeed() != -1); // possible, but extremely unlikely +} +#endif // Qt 7 + +QTEST_MAIN(tst_QHashSeed) +#include "tst_qhashseed.moc" diff --git a/tests/auto/corelib/tools/qhashseed/tst_qhashseed_helper.cpp b/tests/auto/corelib/tools/qhashseed/tst_qhashseed_helper.cpp new file mode 100644 index 0000000000..25e7909870 --- /dev/null +++ b/tests/auto/corelib/tools/qhashseed/tst_qhashseed_helper.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2021 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <qhashfunctions.h> +#include <stdio.h> + +int main() +{ + // appless: + QHashSeed seed1 = QHashSeed::globalSeed(); + QHashSeed seed2 = QHashSeed::globalSeed(); + printf("%zu\n%zu\n", size_t(seed1), size_t(seed2)); + return 0; +} diff --git a/tests/auto/corelib/tools/qline/CMakeLists.txt b/tests/auto/corelib/tools/qline/CMakeLists.txt new file mode 100644 index 0000000000..17a3a1bcef --- /dev/null +++ b/tests/auto/corelib/tools/qline/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qline Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qline LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qline + SOURCES + tst_qline.cpp +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_qline CONDITION UNIX AND NOT APPLE AND NOT HAIKU AND NOT INTEGRITY AND NOT VXWORKS + LIBRARIES + m +) diff --git a/tests/auto/corelib/tools/qline/qline.pro b/tests/auto/corelib/tools/qline/qline.pro deleted file mode 100644 index 81e2f17118..0000000000 --- a/tests/auto/corelib/tools/qline/qline.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qline -QT = core testlib -SOURCES = tst_qline.cpp -unix:!darwin:!vxworks:!haiku:!integrity: LIBS+=-lm diff --git a/tests/auto/corelib/tools/qline/tst_qline.cpp b/tests/auto/corelib/tools/qline/tst_qline.cpp index 31aa5b4e0c..51f1f8ac79 100644 --- a/tests/auto/corelib/tools/qline/tst_qline.cpp +++ b/tests/auto/corelib/tools/qline/tst_qline.cpp @@ -1,38 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <qline.h> -#include <math.h> +#include <qmath.h> -#ifndef M_2PI -#define M_2PI 6.28318530717958647692528676655900576 -#endif +#include <array> class tst_QLine : public QObject { @@ -53,11 +26,6 @@ private slots: void testNormalVector(); void testNormalVector_data(); -#if QT_DEPRECATED_SINCE(5, 14) - void testAngle(); - void testAngle_data(); -#endif - void testAngle2(); void testAngle2_data(); @@ -67,13 +35,10 @@ private slots: void testAngleTo_data(); void testSet(); -}; -// Square root of two -#define SQRT2 1.4142135623731 - -// Length of unit vector projected to x from 45 degrees -#define UNITX_45 0.707106781186547 + void toLineF_data(); + void toLineF(); +}; const qreal epsilon = sizeof(qreal) == sizeof(double) ? 1e-8 : 1e-4; @@ -208,9 +173,6 @@ void tst_QLine::testIntersection() QPointF ip; QLineF::IntersectionType itype = a.intersects(b, &ip); -#if QT_DEPRECATED_SINCE(5, 14) - QCOMPARE(a.intersect(b, &ip), itype); -#endif QCOMPARE(int(itype), type); if (type != QLineF::NoIntersection) { @@ -230,30 +192,46 @@ void tst_QLine::testLength_data() QTest::addColumn<double>("vx"); QTest::addColumn<double>("vy"); - QTest::newRow("[1,0]*2") << 0.0 << 0.0 << 1.0 << 0.0 << 1.0 << 2.0 << 2.0 << 0.0; - QTest::newRow("[0,1]*2") << 0.0 << 0.0 << 0.0 << 1.0 << 1.0 << 2.0 << 0.0 << 2.0; - QTest::newRow("[-1,0]*2") << 0.0 << 0.0 << -1.0 << 0.0 << 1.0 << 2.0 << -2.0 << 0.0; - QTest::newRow("[0,-1]*2") << 0.0 << 0.0 << 0.0 << -1.0 << 1.0 << 2.0 << 0.0 << -2.0; - QTest::newRow("[1,1]->|1|") << 0.0 << 0.0 << 1.0 << 1.0 - << double(SQRT2) << 1.0 << double(UNITX_45) << double(UNITX_45); + // Test name: [dx,dy]->|lenToSet| (x1,x2) + // with the last part omitted if (0,0) + QTest::newRow("[1,0]->|2|") << 0.0 << 0.0 << 1.0 << 0.0 << 1.0 << 2.0 << 2.0 << 0.0; + QTest::newRow("[0,1]->|2|") << 0.0 << 0.0 << 0.0 << 1.0 << 1.0 << 2.0 << 0.0 << 2.0; + QTest::newRow("[-1,0]->|2|") << 0.0 << 0.0 << -1.0 << 0.0 << 1.0 << 2.0 << -2.0 << 0.0; + QTest::newRow("[0,-1]->|2|") << 0.0 << 0.0 << 0.0 << -1.0 << 1.0 << 2.0 << 0.0 << -2.0; + QTest::newRow("[1,1]->->|1|") << 0.0 << 0.0 << 1.0 << 1.0 + << M_SQRT2 << 1.0 << M_SQRT1_2 << M_SQRT1_2; QTest::newRow("[-1,1]->|1|") << 0.0 << 0.0 << -1.0 << 1.0 - << double(SQRT2) << 1.0 << double(-UNITX_45) << double(UNITX_45); + << M_SQRT2 << 1.0 << -M_SQRT1_2 << M_SQRT1_2; QTest::newRow("[1,-1]->|1|") << 0.0 << 0.0 << 1.0 << -1.0 - << double(SQRT2) << 1.0 << double(UNITX_45) << double(-UNITX_45); + << M_SQRT2 << 1.0 << M_SQRT1_2 << -M_SQRT1_2; QTest::newRow("[-1,-1]->|1|") << 0.0 << 0.0 << -1.0 << -1.0 - << double(SQRT2) << 1.0 << double(-UNITX_45) << double(-UNITX_45); - QTest::newRow("[1,0]*2 (2,2)") << 2.0 << 2.0 << 3.0 << 2.0 << 1.0 << 2.0 << 2.0 << 0.0; - QTest::newRow("[0,1]*2 (2,2)") << 2.0 << 2.0 << 2.0 << 3.0 << 1.0 << 2.0 << 0.0 << 2.0; - QTest::newRow("[-1,0]*2 (2,2)") << 2.0 << 2.0 << 1.0 << 2.0 << 1.0 << 2.0 << -2.0 << 0.0; - QTest::newRow("[0,-1]*2 (2,2)") << 2.0 << 2.0 << 2.0 << 1.0 << 1.0 << 2.0 << 0.0 << -2.0; + << M_SQRT2 << 1.0 << -M_SQRT1_2 << -M_SQRT1_2; + QTest::newRow("[1,0]->|2| (2,2)") << 2.0 << 2.0 << 3.0 << 2.0 << 1.0 << 2.0 << 2.0 << 0.0; + QTest::newRow("[0,1]->|2| (2,2)") << 2.0 << 2.0 << 2.0 << 3.0 << 1.0 << 2.0 << 0.0 << 2.0; + QTest::newRow("[-1,0]->|2| (2,2)") << 2.0 << 2.0 << 1.0 << 2.0 << 1.0 << 2.0 << -2.0 << 0.0; + QTest::newRow("[0,-1]->|2| (2,2)") << 2.0 << 2.0 << 2.0 << 1.0 << 1.0 << 2.0 << 0.0 << -2.0; QTest::newRow("[1,1]->|1| (2,2)") << 2.0 << 2.0 << 3.0 << 3.0 - << double(SQRT2) << 1.0 << double(UNITX_45) << double(UNITX_45); + << M_SQRT2 << 1.0 << M_SQRT1_2 << M_SQRT1_2; QTest::newRow("[-1,1]->|1| (2,2)") << 2.0 << 2.0 << 1.0 << 3.0 - << double(SQRT2) << 1.0 << double(-UNITX_45) << double(UNITX_45); + << M_SQRT2 << 1.0 << -M_SQRT1_2 << M_SQRT1_2; QTest::newRow("[1,-1]->|1| (2,2)") << 2.0 << 2.0 << 3.0 << 1.0 - << double(SQRT2) << 1.0 << double(UNITX_45) << double(-UNITX_45); + << M_SQRT2 << 1.0 << M_SQRT1_2 << -M_SQRT1_2; QTest::newRow("[-1,-1]->|1| (2,2)") << 2.0 << 2.0 << 1.0 << 1.0 - << double(SQRT2) << 1.0 << double(-UNITX_45) << double(-UNITX_45); + << M_SQRT2 << 1.0 << -M_SQRT1_2 << -M_SQRT1_2; + const double small = qSqrt(std::numeric_limits<qreal>::denorm_min()) / 8; + QTest::newRow("[small,small]->|2| (-small/2,-small/2)") + << -(small * .5) << -(small * .5) << (small * .5) << (small * .5) + << (small * M_SQRT2) << (2 * M_SQRT2) << 2.0 << 2.0; + const double tiny = std::numeric_limits<qreal>::min() / 2; + QTest::newRow("[tiny,tiny]->|2| (-tiny/2,-tiny/2)") + << -(tiny * .5) << -(tiny * .5) << (tiny * .5) << (tiny * .5) + << (tiny * M_SQRT2) << (2 * M_SQRT2) << 2.0 << 2.0; + QTest::newRow("[1+3e-13,1+4e-13]|1895| (1, 1)") + << 1.0 << 1.0 << (1 + 3e-13) << (1 + 4e-13) + << 5e-13 << 1895.0 << 1137.0 << 1516.0; + QTest::newRow("[4e-323,5e-324]|1892|") // Unavoidable underflow: denormals + << 0.0 << 0.0 << 4e-323 << 5e-324 + << 4e-323 << 1892.0 << 4e-323 << 5e-324; // vx, vy values ignored } void tst_QLine::testLength() @@ -271,9 +249,21 @@ void tst_QLine::testLength() QCOMPARE(l.length(), qreal(length)); l.setLength(lengthToSet); - QCOMPARE(l.length(), qreal(lengthToSet)); - QCOMPARE(l.dx(), qreal(vx)); - QCOMPARE(l.dy(), qreal(vy)); + + if constexpr (std::numeric_limits<double>::has_denorm != std::denorm_present) { + if (qstrcmp(QTest::currentDataTag(), "[tiny,tiny]->|2| (-tiny/2,-tiny/2)") == 0 + || qstrcmp(QTest::currentDataTag(), "[4e-323,5e-324]|1892|") == 0) { + QSKIP("Skipping 'denorm' as this type lacks denormals on this system"); + } + } + // Scaling tiny values up to big can be imprecise: don't try to test vx, vy + if (length > 0 && qFuzzyIsNull(length)) { + QVERIFY(l.length() > lengthToSet / 2 && l.length() < lengthToSet * 2); + } else { + QCOMPARE(l.length(), length > 0 ? qreal(lengthToSet) : qreal(length)); + QCOMPARE(l.dx(), qreal(vx)); + QCOMPARE(l.dy(), qreal(vy)); + } } void tst_QLine::testCenter() @@ -383,57 +373,6 @@ void tst_QLine::testNormalVector() QCOMPARE(n.dy(), qreal(nvy)); } -#if QT_DEPRECATED_SINCE(5, 14) -void tst_QLine::testAngle_data() -{ - QTest::addColumn<double>("xa1"); - QTest::addColumn<double>("ya1"); - QTest::addColumn<double>("xa2"); - QTest::addColumn<double>("ya2"); - QTest::addColumn<double>("xb1"); - QTest::addColumn<double>("yb1"); - QTest::addColumn<double>("xb2"); - QTest::addColumn<double>("yb2"); - QTest::addColumn<double>("angle"); - - QTest::newRow("parallel") << 1.0 << 1.0 << 3.0 << 4.0 - << 5.0 << 6.0 << 7.0 << 9.0 - << 0.0; - QTest::newRow("[4,4]-[4,0]") << 1.0 << 1.0 << 5.0 << 5.0 - << 0.0 << 4.0 << 3.0 << 4.0 - << 45.0; - QTest::newRow("[4,4]-[-4,0]") << 1.0 << 1.0 << 5.0 << 5.0 - << 3.0 << 4.0 << 0.0 << 4.0 - << 135.0; - - for (int i=0; i<180; ++i) { - QTest::newRow(("angle:" + QByteArray::number(i)).constData()) - << 0.0 << 0.0 << double(cos(i*M_2PI/360)) << double(sin(i*M_2PI/360)) - << 0.0 << 0.0 << 1.0 << 0.0 - << double(i); - } -} - -void tst_QLine::testAngle() -{ - QFETCH(double, xa1); - QFETCH(double, ya1); - QFETCH(double, xa2); - QFETCH(double, ya2); - QFETCH(double, xb1); - QFETCH(double, yb1); - QFETCH(double, xb2); - QFETCH(double, yb2); - QFETCH(double, angle); - - QLineF a(xa1, ya1, xa2, ya2); - QLineF b(xb1, yb1, xb2, yb2); - - double resultAngle = a.angle(b); - QCOMPARE(qRound(resultAngle), qRound(angle)); -} -#endif - void tst_QLine::testAngle2_data() { QTest::addColumn<qreal>("x1"); @@ -543,5 +482,35 @@ void tst_QLine::testAngleTo_data() } } +void tst_QLine::toLineF_data() +{ + QTest::addColumn<QLine>("input"); + QTest::addColumn<QLineF>("result"); + + auto row = [](int x1, int y1, int x2, int y2) { + QTest::addRow("((%d, %d)->(%d, %d))", x1, y1, x2, y2) + << QLine(x1, y1, x2, y2) << QLineF(x1, y1, x2, y2); + }; + constexpr std::array samples = {-1, 0, 1}; + for (int x1 : samples) { + for (int y1 : samples) { + for (int x2 : samples) { + for (int y2 : samples) { + row(x1, y1, x2, y2); + } + } + } + } +} + +void tst_QLine::toLineF() +{ + QFETCH(const QLine, input); + QFETCH(const QLineF, result); + + QCOMPARE(input.toLineF(), result); +} + + QTEST_MAIN(tst_QLine) #include "tst_qline.moc" diff --git a/tests/auto/corelib/tools/qlinkedlist/qlinkedlist.pro b/tests/auto/corelib/tools/qlinkedlist/qlinkedlist.pro deleted file mode 100644 index c53d553d6d..0000000000 --- a/tests/auto/corelib/tools/qlinkedlist/qlinkedlist.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -TARGET = tst_qlinkedlist -QT = core testlib -qtConfig(c++14): CONFIG += c++14 -qtConfig(c++1z): CONFIG += c++1z -SOURCES = tst_qlinkedlist.cpp -DEFINES -= QT_NO_LINKED_LIST diff --git a/tests/auto/corelib/tools/qlinkedlist/tst_qlinkedlist.cpp b/tests/auto/corelib/tools/qlinkedlist/tst_qlinkedlist.cpp deleted file mode 100644 index ed0abff456..0000000000 --- a/tests/auto/corelib/tools/qlinkedlist/tst_qlinkedlist.cpp +++ /dev/null @@ -1,1072 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> -#include <QLinkedList> - -struct Movable -{ - Movable(char input = 'j') : i(input), state(Constructed) - { - ++liveCount; - } - Movable(const Movable &other) - : i(other.i) - , state(Constructed) - { - check(other.state, Constructed); - ++liveCount; - } - - ~Movable() - { - check(state, Constructed); - i = 0; - --liveCount; - state = Destructed; - } - - bool operator ==(const Movable &other) const - { - check(state, Constructed); - check(other.state, Constructed); - return i == other.i; - } - - Movable &operator=(const Movable &other) - { - check(state, Constructed); - check(other.state, Constructed); - i = other.i; - return *this; - } - char i; - - static int getLiveCount() { return liveCount; } -private: - static int liveCount; - - enum State { Constructed = 106, Destructed = 110 }; - State state; - - static void check(const State state1, const State state2) - { - QCOMPARE(int(state1), int(state2)); - } -}; - -int Movable::liveCount = 0; - -QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE); -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(Movable); - -Q_DECLARE_METATYPE(QLinkedList<int>); - - -int qHash(const Movable& movable) -{ - return qHash(movable.i); -} - -struct Complex -{ - Complex(int val = 0) - : value(val) - , checkSum(this) - { - ++liveCount; - } - - Complex(Complex const &other) - : value(other.value) - , checkSum(this) - { - ++liveCount; - } - - Complex &operator=(Complex const &other) - { - check(); other.check(); - - value = other.value; - return *this; - } - - ~Complex() - { - --liveCount; - check(); - } - - operator int() const { return value; } - - bool operator==(Complex const &other) const - { - check(); other.check(); - return value == other.value; - } - - void check() const - { - QVERIFY(this == checkSum); - } - - static int getLiveCount() { return liveCount; } -private: - static int liveCount; - - int value; - void *checkSum; -}; - -int Complex::liveCount = 0; - -Q_DECLARE_METATYPE(Complex); - -// Tests depend on the fact that: -Q_STATIC_ASSERT(!QTypeInfo<int>::isStatic); -Q_STATIC_ASSERT(!QTypeInfo<int>::isComplex); -Q_STATIC_ASSERT(!QTypeInfo<Movable>::isStatic); -Q_STATIC_ASSERT(QTypeInfo<Movable>::isComplex); -Q_STATIC_ASSERT(QTypeInfo<Complex>::isStatic); -Q_STATIC_ASSERT(QTypeInfo<Complex>::isComplex); - -class tst_QLinkedList : public QObject -{ - Q_OBJECT -private slots: - void eraseValidIteratorsOnSharedList() const; - void insertWithIteratorsOnSharedList() const; - void lengthInt() const; - void lengthMovable() const; - void lengthComplex() const; - void lengthSignature() const; - void firstInt() const; - void firstMovable() const; - void firstComplex() const; - void lastInt() const; - void lastMovable() const; - void lastComplex() const; - void beginInt() const; - void beginMovable() const; - void beginComplex() const; - void endInt() const; - void endMovable() const; - void endComplex() const; - void containsInt() const; - void containsMovable() const; - void containsComplex() const; - void countInt() const; - void countMovable() const; - void countComplex() const; - void cpp17ctad() const; - void emptyInt() const; - void emptyMovable() const; - void emptyComplex() const; - void endsWithInt() const; - void endsWithMovable() const; - void endsWithComplex() const; - void removeAllInt() const; - void removeAllMovable() const; - void removeAllComplex() const; - void removeOneInt() const; - void removeOneMovable() const; - void removeOneComplex() const; - void reverseIterators() const; - void startsWithInt() const; - void startsWithMovable() const; - void startsWithComplex() const; - void takeFirstInt() const; - void takeFirstMovable() const; - void takeFirstComplex() const; - void takeLastInt() const; - void takeLastMovable() const; - void takeLastComplex() const; - void toStdListInt() const; - void toStdListMovable() const; - void toStdListComplex() const; - void testOperatorsInt() const; - void testOperatorsMovable() const; - void testOperatorsComplex() const; - void testSTLIteratorsInt() const; - void testSTLIteratorsMovable() const; - void testSTLIteratorsComplex() const; - - void initializeList() const; - - void constSharedNullInt() const; - void constSharedNullMovable() const; - void constSharedNullComplex() const; -private: - template<typename T> void length() const; - template<typename T> void first() const; - template<typename T> void last() const; - template<typename T> void begin() const; - template<typename T> void end() const; - template<typename T> void contains() const; - template<typename T> void count() const; - template<typename T> void empty() const; - template<typename T> void endsWith() const; - template<typename T> void move() const; - template<typename T> void removeAll() const; - template<typename T> void removeOne() const; - template<typename T> void startsWith() const; - template<typename T> void swap() const; - template<typename T> void takeFirst() const; - template<typename T> void takeLast() const; - template<typename T> void toStdList() const; - template<typename T> void value() const; - - template<typename T> void testOperators() const; - template<typename T> void testSTLIterators() const; - - template<typename T> void constSharedNull() const; - - int dummyForGuard; -}; - -template<typename T> struct SimpleValue -{ - static T at(int index) - { - return values[index % maxSize]; - } - static const uint maxSize = 7; - static const T values[maxSize]; -}; - -template<> -const int SimpleValue<int>::values[] = { 10, 20, 30, 40, 100, 101, 102 }; -template<> -const Movable SimpleValue<Movable>::values[] = { 10, 20, 30, 40, 100, 101, 102 }; -template<> -const Complex SimpleValue<Complex>::values[] = { 10, 20, 30, 40, 100, 101, 102 }; - -// Make some macros for the tests to use in order to be slightly more readable... -#define T_FOO SimpleValue<T>::at(0) -#define T_BAR SimpleValue<T>::at(1) -#define T_BAZ SimpleValue<T>::at(2) -#define T_CAT SimpleValue<T>::at(3) -#define T_DOG SimpleValue<T>::at(4) -#define T_BLAH SimpleValue<T>::at(5) -#define T_WEEE SimpleValue<T>::at(6) - -template<typename T> -void tst_QLinkedList::length() const -{ - /* Empty list. */ - { - const QLinkedList<T> list; - QCOMPARE(list.size(), 0); - } - - /* One entry. */ - { - QLinkedList<T> list; - list.append(T_FOO); - QCOMPARE(list.size(), 1); - } - - /* Two entries. */ - { - QLinkedList<T> list; - list.append(T_FOO); - list.append(T_BAR); - QCOMPARE(list.size(), 2); - } - - /* Three entries. */ - { - QLinkedList<T> list; - list.append(T_FOO); - list.append(T_BAR); - list.append(T_BAZ); - QCOMPARE(list.size(), 3); - } -} - -void tst_QLinkedList::eraseValidIteratorsOnSharedList() const -{ - QLinkedList<int> a, b; - a.append(5); - a.append(10); - a.append(20); - a.append(20); - a.append(20); - a.append(20); - a.append(30); - - QLinkedList<int>::iterator i = a.begin(); - ++i; - ++i; - ++i; - b = a; - QLinkedList<int>::iterator r = a.erase(i); - QCOMPARE(b.size(), 7); - QCOMPARE(a.size(), 6); - --r; - --r; - QCOMPARE(*r, 10); // Ensure that number 2 instance was removed; -} - -void tst_QLinkedList::insertWithIteratorsOnSharedList() const -{ - QLinkedList<int> a, b; - a.append(5); - a.append(10); - a.append(20); - QLinkedList<int>::iterator i = a.begin(); - ++i; - ++i; - b = a; - - QLinkedList<int>::iterator i2 = a.insert(i, 15); - QCOMPARE(b.size(), 3); - QCOMPARE(a.size(), 4); - --i2; - QCOMPARE(*i2, 10); -} - -void tst_QLinkedList::lengthInt() const -{ - length<int>(); -} - -void tst_QLinkedList::lengthMovable() const -{ - const int liveCount = Movable::getLiveCount(); - length<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::lengthComplex() const -{ - const int liveCount = Complex::getLiveCount(); - length<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -void tst_QLinkedList::lengthSignature() const -{ - /* Constness. */ - { - const QLinkedList<int> list; - /* The function should be const. */ - list.size(); - } -} - -template<typename T> -void tst_QLinkedList::first() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR; - - QCOMPARE(list.first(), T_FOO); - - // remove an item, make sure it still works - list.pop_front(); - QVERIFY(list.size() == 1); - QCOMPARE(list.first(), T_BAR); -} - -void tst_QLinkedList::firstInt() const -{ - first<int>(); -} - -void tst_QLinkedList::firstMovable() const -{ - const int liveCount = Movable::getLiveCount(); - first<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::firstComplex() const -{ - const int liveCount = Complex::getLiveCount(); - first<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::last() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR; - - QCOMPARE(list.last(), T_BAR); - - // remove an item, make sure it still works - list.pop_back(); - QVERIFY(list.size() == 1); - QCOMPARE(list.last(), T_FOO); -} - -void tst_QLinkedList::lastInt() const -{ - last<int>(); -} - -void tst_QLinkedList::lastMovable() const -{ - const int liveCount = Movable::getLiveCount(); - last<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::lastComplex() const -{ - const int liveCount = Complex::getLiveCount(); - last<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::begin() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR; - - QCOMPARE(*list.begin(), T_FOO); - - // remove an item, make sure it still works - list.pop_front(); - QVERIFY(list.size() == 1); - QCOMPARE(*list.begin(), T_BAR); -} - -void tst_QLinkedList::beginInt() const -{ - begin<int>(); -} - -void tst_QLinkedList::beginMovable() const -{ - const int liveCount = Movable::getLiveCount(); - begin<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::beginComplex() const -{ - const int liveCount = Complex::getLiveCount(); - begin<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::end() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR; - - QCOMPARE(*--list.end(), T_BAR); - - // remove an item, make sure it still works - list.pop_back(); - QVERIFY(list.size() == 1); - QCOMPARE(*--list.end(), T_FOO); -} - -void tst_QLinkedList::endInt() const -{ - end<int>(); -} - -void tst_QLinkedList::endMovable() const -{ - const int liveCount = Movable::getLiveCount(); - end<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::endComplex() const -{ - const int liveCount = Complex::getLiveCount(); - end<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::contains() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR << T_BAZ; - - QVERIFY(list.contains(T_FOO)); - QVERIFY(list.contains(T_BLAH) != true); - - // add it and make sure it matches - list.append(T_BLAH); - QVERIFY(list.contains(T_BLAH)); -} - -void tst_QLinkedList::containsInt() const -{ - contains<int>(); -} - -void tst_QLinkedList::containsMovable() const -{ - const int liveCount = Movable::getLiveCount(); - contains<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::containsComplex() const -{ - const int liveCount = Complex::getLiveCount(); - contains<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::count() const -{ - QLinkedList<T> list; - - // starts empty - QVERIFY(list.count() == 0); - - // goes up - list.append(T_FOO); - QVERIFY(list.count() == 1); - - // and up - list.append(T_BAR); - QVERIFY(list.count() == 2); - - // and down - list.pop_back(); - QVERIFY(list.count() == 1); - - // and empty. :) - list.pop_back(); - QVERIFY(list.count() == 0); -} - -void tst_QLinkedList::countInt() const -{ - count<int>(); -} - -void tst_QLinkedList::countMovable() const -{ - const int liveCount = Movable::getLiveCount(); - count<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::countComplex() const -{ - const int liveCount = Complex::getLiveCount(); - count<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -void tst_QLinkedList::cpp17ctad() const -{ -#ifdef __cpp_deduction_guides -#define QVERIFY_IS_LIST_OF(obj, Type) \ - QVERIFY2((std::is_same<decltype(obj), QLinkedList<Type>>::value), \ - QMetaType::typeName(qMetaTypeId<decltype(obj)::value_type>())) -#define CHECK(Type, One, Two, Three) \ - do { \ - const Type v[] = {One, Two, Three}; \ - QLinkedList v1 = {One, Two, Three}; \ - QVERIFY_IS_LIST_OF(v1, Type); \ - QLinkedList v2(v1.begin(), v1.end()); \ - QVERIFY_IS_LIST_OF(v2, Type); \ - QLinkedList v3(std::begin(v), std::end(v)); \ - QVERIFY_IS_LIST_OF(v3, Type); \ - } while (false) \ - /*end*/ - CHECK(int, 1, 2, 3); - CHECK(double, 1.0, 2.0, 3.0); - CHECK(QString, QStringLiteral("one"), QStringLiteral("two"), QStringLiteral("three")); -#undef QVERIFY_IS_LIST_OF -#undef CHECK -#else - QSKIP("This test requires C++17 Constructor Template Argument Deduction support enabled in the compiler."); -#endif -} - -template<typename T> -void tst_QLinkedList::empty() const -{ - QLinkedList<T> list; - - // make sure it starts empty - QVERIFY(list.empty()); - - // and doesn't stay empty - list.append(T_FOO); - QVERIFY(!list.empty()); - - // and goes back to being empty - list.pop_back(); - QVERIFY(list.empty()); -} - -void tst_QLinkedList::emptyInt() const -{ - empty<int>(); -} - -void tst_QLinkedList::emptyMovable() const -{ - const int liveCount = Movable::getLiveCount(); - empty<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::emptyComplex() const -{ - const int liveCount = Complex::getLiveCount(); - empty<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::endsWith() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR << T_BAZ; - - // test it returns correctly in both cases - QVERIFY(list.endsWith(T_BAZ)); - QVERIFY(!list.endsWith(T_BAR)); - - // remove an item and make sure the end item changes - list.pop_back(); - QVERIFY(list.endsWith(T_BAR)); -} - -void tst_QLinkedList::endsWithInt() const -{ - endsWith<int>(); -} - -void tst_QLinkedList::endsWithMovable() const -{ - const int liveCount = Movable::getLiveCount(); - endsWith<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::endsWithComplex() const -{ - const int liveCount = Complex::getLiveCount(); - endsWith<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::removeAll() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR << T_BAZ; - - // remove one instance - list.removeAll(T_BAR); - QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ); - - // many instances - list << T_FOO << T_BAR << T_BAZ << T_FOO << T_BAR << T_BAZ << T_FOO << T_BAR << T_BAZ; - list.removeAll(T_BAR); - QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ); - - // try remove something that doesn't exist - list.removeAll(T_WEEE); - QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ); -} - -void tst_QLinkedList::removeAllInt() const -{ - removeAll<int>(); -} - -void tst_QLinkedList::removeAllMovable() const -{ - const int liveCount = Movable::getLiveCount(); - removeAll<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::removeAllComplex() const -{ - const int liveCount = Complex::getLiveCount(); - removeAll<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::removeOne() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR << T_BAZ; - - // middle - list.removeOne(T_BAR); - QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ); - - // start - list.removeOne(T_FOO); - QCOMPARE(list, QLinkedList<T>() << T_BAZ); - - // last - list.removeOne(T_BAZ); - QCOMPARE(list, QLinkedList<T>()); - - // make sure it really only removes one :) - list << T_FOO << T_FOO; - list.removeOne(T_FOO); - QCOMPARE(list, QLinkedList<T>() << T_FOO); - - // try remove something that doesn't exist - list.removeOne(T_WEEE); - QCOMPARE(list, QLinkedList<T>() << T_FOO); -} - -void tst_QLinkedList::removeOneInt() const -{ - removeOne<int>(); -} - -void tst_QLinkedList::removeOneMovable() const -{ - const int liveCount = Movable::getLiveCount(); - removeOne<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::removeOneComplex() const -{ - const int liveCount = Complex::getLiveCount(); - removeOne<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -void tst_QLinkedList::reverseIterators() const -{ - QLinkedList<int> l; - l << 1 << 2 << 3 << 4; - QLinkedList<int> lr = l; - std::reverse(lr.begin(), lr.end()); - const QLinkedList<int> &clr = lr; - QVERIFY(std::equal(l.begin(), l.end(), lr.rbegin())); - QVERIFY(std::equal(l.begin(), l.end(), lr.crbegin())); - QVERIFY(std::equal(l.begin(), l.end(), clr.rbegin())); - QVERIFY(std::equal(lr.rbegin(), lr.rend(), l.begin())); - QVERIFY(std::equal(lr.crbegin(), lr.crend(), l.begin())); - QVERIFY(std::equal(clr.rbegin(), clr.rend(), l.begin())); -} - -template<typename T> -void tst_QLinkedList::startsWith() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR << T_BAZ; - - // make sure it starts ok - QVERIFY(list.startsWith(T_FOO)); - - // remove an item - list.removeFirst(); - QVERIFY(list.startsWith(T_BAR)); -} - -void tst_QLinkedList::startsWithInt() const -{ - startsWith<int>(); -} - -void tst_QLinkedList::startsWithMovable() const -{ - const int liveCount = Movable::getLiveCount(); - startsWith<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::startsWithComplex() const -{ - const int liveCount = Complex::getLiveCount(); - startsWith<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::takeFirst() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR << T_BAZ; - - QCOMPARE(list.takeFirst(), T_FOO); - QVERIFY(list.size() == 2); - QCOMPARE(list.takeFirst(), T_BAR); - QVERIFY(list.size() == 1); - QCOMPARE(list.takeFirst(), T_BAZ); - QVERIFY(list.size() == 0); -} - -void tst_QLinkedList::takeFirstInt() const -{ - takeFirst<int>(); -} - -void tst_QLinkedList::takeFirstMovable() const -{ - const int liveCount = Movable::getLiveCount(); - takeFirst<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::takeFirstComplex() const -{ - const int liveCount = Complex::getLiveCount(); - takeFirst<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::takeLast() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR << T_BAZ; - - QCOMPARE(list.takeLast(), T_BAZ); - QCOMPARE(list.takeLast(), T_BAR); - QCOMPARE(list.takeLast(), T_FOO); -} - -void tst_QLinkedList::takeLastInt() const -{ - takeLast<int>(); -} - -void tst_QLinkedList::takeLastMovable() const -{ - const int liveCount = Movable::getLiveCount(); - takeLast<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::takeLastComplex() const -{ - const int liveCount = Complex::getLiveCount(); - takeLast<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::toStdList() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR << T_BAZ; - - // yuck. - std::list<T> slist; - slist.push_back(T_FOO); - slist.push_back(T_BAR); - slist.push_back(T_BAZ); - - QCOMPARE(list.toStdList(), slist); - QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAR << T_BAZ); -} - -void tst_QLinkedList::toStdListInt() const -{ - toStdList<int>(); -} - -void tst_QLinkedList::toStdListMovable() const -{ - const int liveCount = Movable::getLiveCount(); - toStdList<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::toStdListComplex() const -{ - const int liveCount = Complex::getLiveCount(); - toStdList<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::testOperators() const -{ - QLinkedList<T> list; - list << T_FOO << T_BAR << T_BAZ; - - QLinkedList<T> listtwo; - listtwo << T_FOO << T_BAR << T_BAZ; - - // test equal - QVERIFY(list == listtwo); - - // not equal - listtwo.append(T_CAT); - QVERIFY(list != listtwo); - - // += - list += listtwo; - QVERIFY(list.size() == 7); - QVERIFY(listtwo.size() == 4); - QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAR << T_BAZ - << T_FOO << T_BAR << T_BAZ << T_CAT); - - // = - list = listtwo; - QCOMPARE(list, listtwo); - QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAR << T_BAZ << T_CAT); -} - -void tst_QLinkedList::testOperatorsInt() const -{ - testOperators<int>(); -} - -void tst_QLinkedList::testOperatorsMovable() const -{ - const int liveCount = Movable::getLiveCount(); - testOperators<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::testOperatorsComplex() const -{ - const int liveCount = Complex::getLiveCount(); - testOperators<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -template<typename T> -void tst_QLinkedList::testSTLIterators() const -{ - QLinkedList<T> list; - - // create a list - list << T_FOO << T_BAR << T_BAZ; - typename QLinkedList<T>::iterator it = list.begin(); - QCOMPARE(*it, T_FOO); it++; - QCOMPARE(*it, T_BAR); it++; - QCOMPARE(*it, T_BAZ); it++; - QCOMPARE(it, list.end()); it--; - - // walk backwards - QCOMPARE(*it, T_BAZ); it--; - QCOMPARE(*it, T_BAR); it--; - QCOMPARE(*it, T_FOO); - - // test erase - it = list.erase(it); - QVERIFY(list.size() == 2); - QCOMPARE(*it, T_BAR); - - // test multiple erase - it = list.erase(it, it + 2); - QVERIFY(list.size() == 0); - QCOMPARE(it, list.end()); - - // insert again - it = list.insert(it, T_FOO); - QVERIFY(list.size() == 1); - QCOMPARE(*it, T_FOO); - - // insert again - it = list.insert(it, T_BAR); - QVERIFY(list.size() == 2); - QCOMPARE(*it++, T_BAR); - QCOMPARE(*it, T_FOO); -} - -void tst_QLinkedList::testSTLIteratorsInt() const -{ - testSTLIterators<int>(); -} - -void tst_QLinkedList::testSTLIteratorsMovable() const -{ - const int liveCount = Movable::getLiveCount(); - testSTLIterators<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::testSTLIteratorsComplex() const -{ - const int liveCount = Complex::getLiveCount(); - testSTLIterators<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -void tst_QLinkedList::initializeList() const -{ - QLinkedList<int> v1 { 2, 3, 4 }; - QCOMPARE(v1, QLinkedList<int>() << 2 << 3 << 4); - QCOMPARE(v1, (QLinkedList<int> { 2, 3, 4})); - - QLinkedList<QLinkedList<int>> v2{ v1, { 1 }, QLinkedList<int>(), { 2, 3, 4 } }; - QLinkedList<QLinkedList<int>> v3; - v3 << v1 << (QLinkedList<int>() << 1) << QLinkedList<int>() << v1; - QCOMPARE(v3, v2); -} - - -template<typename T> -void tst_QLinkedList::constSharedNull() const -{ - QLinkedList<T> list2; - QVERIFY(!list2.isDetached()); -} - -void tst_QLinkedList::constSharedNullInt() const -{ - constSharedNull<int>(); -} - -void tst_QLinkedList::constSharedNullMovable() const -{ - const int liveCount = Movable::getLiveCount(); - constSharedNull<Movable>(); - QCOMPARE(liveCount, Movable::getLiveCount()); -} - -void tst_QLinkedList::constSharedNullComplex() const -{ - const int liveCount = Complex::getLiveCount(); - constSharedNull<Complex>(); - QCOMPARE(liveCount, Complex::getLiveCount()); -} - -QTEST_APPLESS_MAIN(tst_QLinkedList) -#include "tst_qlinkedlist.moc" diff --git a/tests/auto/corelib/tools/qvector/.gitignore b/tests/auto/corelib/tools/qlist/.gitignore index 5520039486..5520039486 100644 --- a/tests/auto/corelib/tools/qvector/.gitignore +++ b/tests/auto/corelib/tools/qlist/.gitignore diff --git a/tests/auto/corelib/tools/qlist/CMakeLists.txt b/tests/auto/corelib/tools/qlist/CMakeLists.txt new file mode 100644 index 0000000000..fdcfcd7424 --- /dev/null +++ b/tests/auto/corelib/tools/qlist/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qlist Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlist LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qlist + SOURCES + tst_qlist.cpp + LIBRARIES + Qt::CorePrivate +) + +## Scopes: +##################################################################### diff --git a/tests/auto/corelib/tools/qlist/tst_qlist.cpp b/tests/auto/corelib/tools/qlist/tst_qlist.cpp new file mode 100644 index 0000000000..35d69e8433 --- /dev/null +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -0,0 +1,3969 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QAtomicInt> +#include <QThread> +#include <QSemaphore> +#include <QAtomicScopedValueRollback> +#include <qlist.h> + + +#ifdef QT_COMPILER_HAS_LWG3346 +# if __has_include(<concepts>) +# include <concepts> +# if defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002L + static_assert(std::contiguous_iterator<QList<int>::iterator>); + static_assert(std::contiguous_iterator<QList<int>::const_iterator>); +# endif +# endif +# if __has_include(<ranges>) +# include <ranges> +# if defined(__cpp_lib_ranges) + namespace rns = std::ranges; + + static_assert(rns::contiguous_range<QList<int>>); + static_assert(rns::contiguous_range<const QList<int>>); +# endif +# endif +#endif + +struct Movable { + Movable(char input = 'j') + : i(input) + , that(this) + , state(Constructed) + { + counter.fetchAndAddRelaxed(1); + } + Movable(const Movable &other) + : i(other.i) + , that(this) + , state(Constructed) + { + check(other.state, Constructed); + counter.fetchAndAddRelaxed(1); + } + Movable(Movable &&other) + : i(other.i) + , that(other.that) + , state(Constructed) + { + check(other.state, Constructed); + counter.fetchAndAddRelaxed(1); + other.that = nullptr; + } + + ~Movable() + { + check(state, Constructed); + i = 0; + counter.fetchAndAddRelaxed(-1); + state = Destructed; + } + + bool operator ==(const Movable &other) const + { + check(state, Constructed); + check(other.state, Constructed); + return i == other.i; + } + + Movable &operator=(const Movable &other) + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + that = this; + return *this; + } + Movable &operator=(Movable &&other) + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + that = other.that; + other.that = nullptr; + return *this; + } + bool wasConstructedAt(const Movable *other) const + { + return that == other; + } + char i; + static QAtomicInt counter; +private: + Movable *that; // used to check if an instance was moved + + enum State { Constructed = 106, Destructed = 110 }; + State state; + + static void check(const State state1, const State state2) + { + QCOMPARE(int(state1), int(state2)); + } +}; + +inline size_t qHash(const Movable &key, size_t seed = 0) { return qHash(key.i, seed); } + +QAtomicInt Movable::counter = 0; +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(Movable, Q_RELOCATABLE_TYPE); +QT_END_NAMESPACE +Q_DECLARE_METATYPE(Movable); + +struct Custom { + Custom(char input = 'j') + : i(input) + , that(this) + , state(Constructed) + { + counter.fetchAndAddRelaxed(1); + } + Custom(const Custom &other) + : that(this) + , state(Constructed) + { + check(&other); + counter.fetchAndAddRelaxed(1); + this->i = other.i; + } + ~Custom() + { + check(this); + i = 0; + counter.fetchAndAddRelaxed(-1); + state = Destructed; + QVERIFY(heapData.use_count() > 0); // otherwise it's double free + } + + bool operator ==(const Custom &other) const + { + check(&other); + check(this); + return i == other.i; + } + + bool operator<(const Custom &other) const + { + check(&other); + check(this); + return i < other.i; + } + + Custom &operator=(const Custom &other) + { + check(&other); + check(this); + i = other.i; + return *this; + } + static QAtomicInt counter; + + char i; // used to identify orgin of an instance +private: + Custom *that; // used to check if an instance was moved + // shared_ptr triggers ASan/LSan and can track if double free happens, which + // is convenient to ensure there's no malfunctioning QList APIs + std::shared_ptr<int> heapData = std::shared_ptr<int>(new int(42)); + + enum State { Constructed = 106, Destructed = 110 }; + State state; + + static void check(const Custom *c) + { + // check if c object has been moved + QCOMPARE(c, c->that); + QCOMPARE(int(c->state), int(Constructed)); + } +}; +QAtomicInt Custom::counter = 0; + +inline size_t qHash(const Custom &key, size_t seed = 0) { return qHash(key.i, seed); } + +Q_DECLARE_METATYPE(Custom); + +// tests depends on the fact that: +static_assert(QTypeInfo<int>::isRelocatable); +static_assert(!QTypeInfo<int>::isComplex); +static_assert(QTypeInfo<Movable>::isRelocatable); +static_assert(QTypeInfo<Movable>::isComplex); +static_assert(!QTypeInfo<Custom>::isRelocatable); +static_assert(QTypeInfo<Custom>::isComplex); + +// leak checking utility: +template<typename T> +struct LeakChecker +{ + int instancesCount; + LeakChecker() : instancesCount(T::counter.loadAcquire()) { } + ~LeakChecker() { QCOMPARE(instancesCount, T::counter.loadAcquire()); } +}; +template<> struct LeakChecker<int>{}; +template<> struct LeakChecker<QString>{}; +#define TST_QLIST_CHECK_LEAKS(Type) \ + LeakChecker<Type> checker; \ + Q_UNUSED(checker); + +class tst_QList : public QObject +{ + Q_OBJECT + +private slots: + void constructors_empty() const; + void constructors_emptyReserveZero() const; + void constructors_emptyReserve() const; + void constructors_reserveAndInitialize() const; + void copyConstructorInt() const { copyConstructor<int>(); } + void copyConstructorMovable() const { copyConstructor<Movable>(); } + void copyConstructorCustom() const { copyConstructor<Custom>(); } + void assignmentInt() const { testAssignment<int>(); } + void assignmentMovable() const { testAssignment<Movable>(); } + void assignmentCustom() const { testAssignment<Custom>(); } + void assignFromInitializerListInt() const { assignFromInitializerList<int>(); } + void assignFromInitializerListMovable() const { assignFromInitializerList<Movable>(); } + void assignFromInitializerListCustom() const { assignFromInitializerList<Custom>(); } + void addInt() const { add<int>(); } + void addMovable() const { add<Movable>(); } + void addCustom() const { add<Custom>(); } + void appendInt() const { append<int>(); } + void appendMovable() const { append<Movable>(); } + void appendCustom() const { append<Custom>(); } + void appendRvalue() const; + void appendList() const; + void assignEmpty() const; + void assignInt() const { assign<int>(); } + void assignMovable() const { assign<Movable>(); } + void assignCustom() const { assign<Custom>(); } + void assignUsesPrependBuffer_int_data() { assignUsesPrependBuffer_data(); } + void assignUsesPrependBuffer_int() const { assignUsesPrependBuffer<int>(); } + void assignUsesPrependBuffer_Movable_data() { assignUsesPrependBuffer_data(); } + void assignUsesPrependBuffer_Movable() const { assignUsesPrependBuffer<Movable>(); } + void assignUsesPrependBuffer_Custom_data() { assignUsesPrependBuffer_data(); } + void assignUsesPrependBuffer_Custom() const { assignUsesPrependBuffer<Custom>(); } + void at() const; + void capacityInt() const { capacity<int>(); } + void capacityMovable() const { capacity<Movable>(); } + void capacityCustom() const { capacity<Custom>(); } + void clearInt() const { clear<int>(); } + void clearMovable() const { clear<Movable>(); } + void clearCustom() const { clear<Custom>(); } + void constData() const; + void constFirst() const; + void constLast() const; + void contains() const; + void countInt() const { count<int>(); } + void countMovable() const { count<Movable>(); } + void countCustom() const { count<Custom>(); } + void cpp17ctad() const; + void data() const; + void emptyInt() const { empty<int>(); } + void emptyMovable() const { empty<Movable>(); } + void emptyCustom() const { empty<Custom>(); } + void endsWith() const; + void eraseEmptyInt() const { eraseEmpty<int>(); } + void eraseEmptyMovable() const { eraseEmpty<Movable>(); } + void eraseEmptyCustom() const { eraseEmpty<Custom>(); } + void eraseEmptyReservedInt() const { eraseEmptyReserved<int>(); } + void eraseEmptyReservedMovable() const { eraseEmptyReserved<Movable>(); } + void eraseEmptyReservedCustom() const { eraseEmptyReserved<Custom>(); } + void eraseInt() const { erase<int>(false); } + void eraseIntShared() const { erase<int>(true); } + void eraseMovable() const { erase<Movable>(false); } + void eraseMovableShared() const { erase<Movable>(true); } + void eraseCustom() const { erase<Custom>(false); } + void eraseCustomShared() const { erase<Custom>(true); } + void eraseReservedInt() const { eraseReserved<int>(); } + void eraseReservedMovable() const { eraseReserved<Movable>(); } + void eraseReservedCustom() const { eraseReserved<Custom>(); } + void fillInt() const { fill<int>(); } + void fillMovable() const { fill<Movable>(); } + void fillCustom() const { fill<Custom>(); } + void fillDetachInt() const { fillDetach<int>(); } + void fillDetachMovable() const { fillDetach<Movable>(); } + void fillDetachCustom() const { fillDetach<Custom>(); } + void first() const; + void fromListInt() const { fromList<int>(); } + void fromListMovable() const { fromList<Movable>(); } + void fromListCustom() const { fromList<Custom>(); } + void indexOf() const; + void insertInt() const { insert<int>(); } + void insertMovable() const { insert<Movable>(); } + void insertCustom() const { insert<Custom>(); } + void insertZeroCount_data(); + void insertZeroCount() const; + void isEmpty() const; + void last() const; + void lastIndexOf() const; + void mid() const; + void sliced() const; + void moveInt() const { move<int>(); } + void moveMovable() const { move<Movable>(); } + void moveCustom() const { move<Custom>(); } + void prependInt() const { prepend<int>(); } + void prependMovable() const { prepend<Movable>(); } + void prependCustom() const { prepend<Custom>(); } + void prependRvalue() const; + void qhashInt() const { qhash<int>(); } + void qhashMovable() const { qhash<Movable>(); } + void qhashCustom() const { qhash<Custom>(); } + void removeAllWithAlias() const; + void removeInt() const { remove<int>(); } + void removeMovable() const { remove<Movable>(); } + void removeCustom() const { remove<Custom>(); } + void removeFirstLast() const; + void resizePOD_data() const; + void resizePOD() const; + void resizeComplexMovable_data() const; + void resizeComplexMovable() const; + void resizeComplex_data() const; + void resizeComplex() const; + void resizeCtorAndDtor() const; + void resizeToZero() const; + void resizeToTheSameSize_data(); + void resizeToTheSameSize() const; + void resizeForOverwrite() const; + void iterators() const; + void constIterators() const; + void reverseIterators() const; + void sizeInt() const { size<int>(); } + void sizeMovable() const { size<Movable>(); } + void sizeCustom() const { size<Custom>(); } + void startsWith() const; + void swapInt() const { swap<int>(); } + void swapMovable() const { swap<Movable>(); } + void swapCustom() const { swap<Custom>(); } + void toList() const; +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + void fromStdVector() const; + void toStdVector() const; +#endif + void value() const; + void testOperators() const; + void reserve(); + void reserveZero(); + void initializeListInt() { initializeList<int>(); } + void initializeListMovable() { initializeList<Movable>(); } + void initializeListCustom() { initializeList<Custom>(); } + void const_shared_null(); + void detachInt() const { detach<int>(); } + void detachMovable() const { detach<Movable>(); } + void detachCustom() const { detach<Custom>(); } + void detachThreadSafetyInt() const; + void detachThreadSafetyMovable() const; + void detachThreadSafetyCustom() const; + void insertMove() const; + void swapItemsAt() const; + void emplaceInt() { emplaceImpl<int>(); } + void emplaceCustom() { emplaceImpl<Custom>(); } + void emplaceMovable() { emplaceImpl<Movable>(); } + void emplaceConsistentWithStdVectorInt() { emplaceConsistentWithStdVectorImpl<int>(); } + void emplaceConsistentWithStdVectorCustom() { emplaceConsistentWithStdVectorImpl<Custom>(); } + void emplaceConsistentWithStdVectorMovable() { emplaceConsistentWithStdVectorImpl<Movable>(); } + void emplaceConsistentWithStdVectorQString() { emplaceConsistentWithStdVectorImpl<QString>(); } + void emplaceReturnsIterator(); + void emplaceFront() const; + void emplaceFrontReturnsRef() const; + void emplaceBack(); + void emplaceBackReturnsRef(); + void emplaceWithElementFromTheSameContainer(); + void emplaceWithElementFromTheSameContainer_data(); + void replaceInt() const { replace<int>(); } + void replaceCustom() const { replace<Custom>(); } + void replaceMovable() const { replace<Movable>(); } + void fromReadOnlyData() const; + void reallocateCustomAlignedType_qtbug90359() const; + void reinsertToBeginInt_qtbug91360() const { reinsertToBegin<int>(); } + void reinsertToBeginMovable_qtbug91360() const { reinsertToBegin<Movable>(); } + void reinsertToBeginCustom_qtbug91360() const { reinsertToBegin<Custom>(); } + void reinsertToEndInt_qtbug91360() const { reinsertToEnd<int>(); } + void reinsertToEndMovable_qtbug91360() const { reinsertToEnd<Movable>(); } + void reinsertToEndCustom_qtbug91360() const { reinsertToEnd<Custom>(); } + void reinsertRangeToEndInt_qtbug91360() const { reinsertRangeToEnd<int>(); } + void reinsertRangeToEndMovable_qtbug91360() const { reinsertRangeToEnd<Movable>(); } + void reinsertRangeToEndCustom_qtbug91360() const { reinsertRangeToEnd<Custom>(); } + // QList reference stability tests: + void stability_reserveInt() const { stability_reserve<int>(); } + void stability_reserveMovable() const { stability_reserve<Movable>(); } + void stability_reserveCustom() const { stability_reserve<Custom>(); } + void stability_eraseInt() const { stability_erase<int>(); } + void stability_eraseMovable() const { stability_erase<Movable>(); } + void stability_eraseCustom() const { stability_erase<Custom>(); } + void stability_appendInt() const { stability_append<int>(); } + void stability_appendMovable() const { stability_append<Movable>(); } + void stability_appendCustom() const { stability_append<Custom>(); } + void stability_insertElementInt() const { stability_insertElement<int>(); } + void stability_insertElementMovable() const { stability_insertElement<Movable>(); } + void stability_insertElementCustom() const { stability_insertElement<Custom>(); } + void stability_emplaceInt() const { stability_emplace<int>(); } + void stability_emplaceMovable() const { stability_emplace<Movable>(); } + void stability_emplaceCustom() const { stability_emplace<Custom>(); } + void stability_resizeInt() const { stability_resize<int>(); } + void stability_resizeMovable() const { stability_resize<Movable>(); } + void stability_resizeCustom() const { stability_resize<Custom>(); } + +private: + template<typename T> void copyConstructor() const; + template<typename T> void testAssignment() const; + template<typename T> void add() const; + template<typename T> void append() const; + template<typename T> void assign() const; + void assignUsesPrependBuffer_data() const; + template<typename T> void assignUsesPrependBuffer() const; + template<typename T> void assignFromInitializerList() const; + template<typename T> void capacity() const; + template<typename T> void clear() const; + template<typename T> void count() const; + template<typename T> void empty() const; + template<typename T> void eraseEmpty() const; + template<typename T> void eraseEmptyReserved() const; + template<typename T> void erase(bool shared) const; + template<typename T> void eraseReserved() const; + template<typename T> void fill() const; + template<typename T> void fillDetach() const; + template<typename T> void fromList() const; + template<typename T> void insert() const; + template<typename T> void qhash() const; + template<typename T> void move() const; + template<typename T> void prepend() const; + template<typename T> void remove() const; + template<typename T> void size() const; + template<typename T> void swap() const; + template<typename T> void initializeList(); + template<typename T> void detach() const; + template<typename T> void detachThreadSafety() const; + template<typename T> void emplaceImpl() const; + template<typename T> void emplaceConsistentWithStdVectorImpl() const; + template<typename T> void replace() const; + template<typename T, typename Reinsert> + void reinsert(Reinsert op) const; + template<typename T> + void reinsertToBegin() const + { + reinsert<T>([](QList<T> &list) { + list.prepend(list.back()); + list.removeLast(); + }); + } + template<typename T> + void reinsertToEnd() const + { + reinsert<T>([](QList<T> &list) { + list.append(list.front()); + list.removeFirst(); + }); + } + template<typename T> + void reinsertRangeToEnd() const + { + reinsert<T>([](QList<T> &list) { + list.append(list.begin(), list.begin() + 1); + list.removeFirst(); + }); + } + template<typename T> + void stability_reserve() const; + template<typename T> + void stability_erase() const; + template<typename T> + void stability_append() const; + template<typename T, typename Insert> + void stability_insert(Insert op) const; + template<typename T> + void stability_resize() const; + + template<typename T> + void stability_insertElement() const + { + stability_insert<T>( + [](QList<T> &list, int pos, const T &value) { list.insert(pos, 1, value); }); + } + template<typename T> + void stability_emplace() const + { + stability_insert<T>( + [](QList<T> &list, int pos, const T &value) { list.emplace(pos, value); }); + } +}; + + +template<typename T> struct SimpleValue +{ + static T at(int index) + { + return Values[index % MaxIndex]; + } + + static QList<T> vector(int size) + { + QList<T> ret; + for (int i = 0; i < size; i++) + ret.append(at(i)); + return ret; + } + + static const uint MaxIndex = 6; + static const T Values[MaxIndex]; +}; + +template<> +const int SimpleValue<int>::Values[] = { 110, 105, 101, 114, 111, 98 }; +template<> +const Movable SimpleValue<Movable>::Values[] = { 110, 105, 101, 114, 111, 98 }; +template<> +const Custom SimpleValue<Custom>::Values[] = { 110, 105, 101, 114, 111, 98 }; + +// Make some macros for the tests to use in order to be slightly more readable... +#define T_FOO SimpleValue<T>::at(0) +#define T_BAR SimpleValue<T>::at(1) +#define T_BAZ SimpleValue<T>::at(2) +#define T_CAT SimpleValue<T>::at(3) +#define T_DOG SimpleValue<T>::at(4) +#define T_BLAH SimpleValue<T>::at(5) + +// returns a pair of QList<T> and QList<T *> +template<typename It> +decltype(auto) qlistCopyAndReferenceFromRange(It first, It last) +{ + using T = typename std::iterator_traits<It>::value_type; + QList<T> copy(first, last); + QList<T *> reference; + for (; first != last; ++first) + reference.append(std::addressof(*first)); + return std::make_pair(copy, reference); +} + +void tst_QList::constructors_empty() const +{ + QList<int> emptyInt; + QList<Movable> emptyMovable; + QList<Custom> emptyCustom; +} + +void tst_QList::constructors_emptyReserveZero() const +{ + QList<int> emptyInt(0); + QList<Movable> emptyMovable(0); + QList<Custom> emptyCustom(0); +} + +void tst_QList::constructors_emptyReserve() const +{ + // pre-reserve capacity + QList<int> myInt(5); + QVERIFY(myInt.capacity() == 5); + QList<Movable> myMovable(5); + QVERIFY(myMovable.capacity() == 5); + QList<Custom> myCustom(4); + QVERIFY(myCustom.capacity() == 4); +} + +void tst_QList::constructors_reserveAndInitialize() const +{ + // default-initialise items + + const QList<int> myInt(5, 42); + QVERIFY(myInt.capacity() == 5); + for (int meaningoflife : myInt) + QCOMPARE(meaningoflife, 42); + + const QList<QString> myString(5, QString::fromLatin1("c++")); + QVERIFY(myString.capacity() == 5); + // make sure all items are initialised ok + for (const QString &meaningoflife : myString) + QCOMPARE(meaningoflife, QString::fromLatin1("c++")); + + const QList<Custom> myCustom(5, Custom('n')); + QVERIFY(myCustom.capacity() == 5); + // make sure all items are initialised ok + for (Custom meaningoflife : myCustom) + QCOMPARE(meaningoflife.i, 'n'); +} + +template<typename T> +void tst_QList::copyConstructor() const +{ + TST_QLIST_CHECK_LEAKS(T) + + T value1(SimpleValue<T>::at(0)); + T value2(SimpleValue<T>::at(1)); + T value3(SimpleValue<T>::at(2)); + T value4(SimpleValue<T>::at(3)); + { + QList<T> v1; + QList<T> v2(v1); + QCOMPARE(v1, v2); + } + { + QList<T> v1; + v1 << value1 << value2 << value3 << value4; + QList<T> v2(v1); + QCOMPARE(v1, v2); + } +} + +template<typename T> +void tst_QList::testAssignment() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> v1(5); + QCOMPARE(v1.size(), 5); + QVERIFY(v1.isDetached()); + + QList<T> v2(7); + QCOMPARE(v2.size(), 7); + QVERIFY(v2.isDetached()); + + QVERIFY(!v1.isSharedWith(v2)); + + v1 = v2; + + QVERIFY(!v1.isDetached()); + QVERIFY(!v2.isDetached()); + QVERIFY(v1.isSharedWith(v2)); + + const void *const data1 = v1.constData(); + const void *const data2 = v2.constData(); + + QCOMPARE(data1, data2); + + v1.clear(); + + QVERIFY(v2.isDetached()); + QVERIFY(!v1.isSharedWith(v2)); + QCOMPARE((void *)v2.constData(), data2); +} + +template<typename T> +void tst_QList::assignFromInitializerList() const +{ + TST_QLIST_CHECK_LEAKS(T) + + T val1(SimpleValue<T>::at(1)); + T val2(SimpleValue<T>::at(2)); + T val3(SimpleValue<T>::at(3)); + + QList<T> v1 = {val1, val2, val3}; + QCOMPARE(v1, QList<T>() << val1 << val2 << val3); + QCOMPARE(v1, (QList<T> {val1, val2, val3})); + + v1 = {}; + QCOMPARE(v1.size(), 0); +} + +template<typename T> +void tst_QList::add() const +{ + TST_QLIST_CHECK_LEAKS(T) + + { + QList<T> empty1; + QList<T> empty2; + QVERIFY((empty1 + empty2).isEmpty()); + empty1 += empty2; + QVERIFY(empty1.isEmpty()); + QVERIFY(empty2.isEmpty()); + } + { + QList<T> v(12); + QList<T> empty; + QCOMPARE((v + empty), v); + v += empty; + QVERIFY(!v.isEmpty()); + QCOMPARE(v.size(), 12); + QVERIFY(empty.isEmpty()); + } + { + QList<T> v1(12); + QList<T> v2; + v2 += v1; + QVERIFY(!v1.isEmpty()); + QCOMPARE(v1.size(), 12); + QVERIFY(!v2.isEmpty()); + QCOMPARE(v2.size(), 12); + } +} + +template<typename T> +void tst_QList::append() const +{ + TST_QLIST_CHECK_LEAKS(T) + + { + QList<T> myvec; + myvec.append(SimpleValue<T>::at(0)); + QVERIFY(myvec.size() == 1); + myvec.append(SimpleValue<T>::at(1)); + QVERIFY(myvec.size() == 2); + myvec.append(SimpleValue<T>::at(2)); + QVERIFY(myvec.size() == 3); + + QCOMPARE(myvec, QList<T>() << SimpleValue<T>::at(0) + << SimpleValue<T>::at(1) + << SimpleValue<T>::at(2)); + } + { + QList<T> v(2); + v.append(SimpleValue<T>::at(0)); + QVERIFY(v.size() == 3); + QCOMPARE(v.at(v.size() - 1), SimpleValue<T>::at(0)); + } + { + QList<T> v(2); + v.reserve(12); + v.append(SimpleValue<T>::at(0)); + QVERIFY(v.size() == 3); + QCOMPARE(v.at(v.size() - 1), SimpleValue<T>::at(0)); + } + { + QList<int> v; + v << 1 << 2 << 3; + QList<int> x; + x << 4 << 5 << 6; + v.append(x); + + QList<int> combined; + combined << 1 << 2 << 3 << 4 << 5 << 6; + + QCOMPARE(v, combined); + } + { + const QList<T> otherVec { SimpleValue<T>::at(0), + SimpleValue<T>::at(1), + SimpleValue<T>::at(2), + SimpleValue<T>::at(3) }; + QList<T> myvec; + myvec.append(otherVec.cbegin(), otherVec.cbegin() + 3); + QCOMPARE(myvec.size(), 3); + QCOMPARE(myvec, QList<T>() << SimpleValue<T>::at(0) + << SimpleValue<T>::at(1) + << SimpleValue<T>::at(2)); + } + { + QList<T> emptyVec; + QList<T> otherEmptyVec; + + emptyVec.append(otherEmptyVec); + + QVERIFY(emptyVec.isEmpty()); + QVERIFY(!emptyVec.isDetached()); + QVERIFY(!otherEmptyVec.isDetached()); + } + { + QList<T> myvec { SimpleValue<T>::at(0), SimpleValue<T>::at(1) }; + QList<T> emptyVec; + + myvec.append(emptyVec); + QVERIFY(emptyVec.isEmpty()); + QVERIFY(!emptyVec.isDetached()); + QCOMPARE(myvec, QList<T>({ SimpleValue<T>::at(0), SimpleValue<T>::at(1) })); + } +} + +void tst_QList::assignEmpty() const +{ + // Test that the realloc branch in assign(it, it) doesn't crash. + using T = int; + QList<T> list; + QList<T> ref1 = list; + QVERIFY(list.d.needsDetach()); + list.assign(list.begin(), list.begin()); + +#if !defined Q_OS_QNX // QNX has problems with the empty istream_iterator + auto empty = std::istream_iterator<T>{}; + list.squeeze(); + QCOMPARE_EQ(list.capacity(), 0); + ref1 = list; + QVERIFY(list.d.needsDetach()); + list.assign(empty, empty); +#endif +} + +template <typename T> +void tst_QList::assign() const +{ + TST_QLIST_CHECK_LEAKS(T) + { + QList<T> myvec; + myvec.assign(2, T_FOO); + QVERIFY(myvec.isDetached()); + QCOMPARE(myvec, QList<T>() << T_FOO << T_FOO); + + QList<T> myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + myvec.assign(3, T_BAR); + QCOMPARE(myvec, QList<T>() << T_BAR << T_BAR << T_BAR); + QVERIFY(myvec.isDetached()); + QVERIFY(myvecCopy.isDetached()); + QVERIFY(!myvec.isSharedWith(myvecCopy)); + QVERIFY(!myvecCopy.isSharedWith(myvec)); + } + { + QList<T> myvec; + myvec.assign(4, T_FOO); + QVERIFY(myvec.isDetached()); + QCOMPARE(myvec, QList<T>() << T_FOO << T_FOO << T_FOO << T_FOO); + + QList<T> myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + myvecCopy.assign(myvec.begin(), myvec.begin() + 2); + QVERIFY(myvec.isDetached()); + QVERIFY(myvecCopy.isDetached()); + QVERIFY(!myvec.isSharedWith(myvecCopy)); + QVERIFY(!myvecCopy.isSharedWith(myvec)); + QCOMPARE(myvecCopy, QList<T>() << T_FOO << T_FOO); + } +} + +inline namespace Scenarios { +Q_NAMESPACE +enum ListState { + UnsharedList, + SharedList, +}; +Q_ENUM_NS(ListState) +enum RelationWithPrependBuffer { + FitsIntoFreeSpaceAtBegin, + FitsFreeSpaceAtBeginExactly, + ExceedsFreeSpaceAtBegin, + FitsFreeSpaceAtBeginPlusSizeExactly, + FullCapacity, +}; +Q_ENUM_NS(RelationWithPrependBuffer) +} // namespace Scenarios + +void tst_QList::assignUsesPrependBuffer_data() const +{ + QTest::addColumn<ListState>("listState"); + QTest::addColumn<RelationWithPrependBuffer>("relationWithPrependBuffer"); + + const auto sme = QMetaEnum::fromType<ListState>(); + const auto rme = QMetaEnum::fromType<RelationWithPrependBuffer>(); + + for (int i = 0, s = sme.value(i); s != -1; s = sme.value(++i)) { + for (int j = 0, r = rme.value(j); r != -1; r = rme.value(++j)) { + QTest::addRow("%s-%s", sme.key(i), rme.key(j)) + << ListState(s) << RelationWithPrependBuffer(r); + } + } +} + +template <typename T> +void tst_QList::assignUsesPrependBuffer() const +{ + QFETCH(const ListState, listState); + QFETCH(const RelationWithPrependBuffer, relationWithPrependBuffer); + + const auto capBegin = [](const QList<T> &l) { + return l.begin() - l.d.freeSpaceAtBegin(); + }; + const auto capEnd = [](const QList<T> &l) { + return l.end() + l.d.freeSpaceAtEnd(); + }; + + TST_QLIST_CHECK_LEAKS(T) + { + // Test the prepend optimization. + QList<T> withFreeSpaceAtBegin(16, T_FOO); + // try at most 100 times to create freeSpaceAtBegin(): + for (int i = 0; i < 100 && withFreeSpaceAtBegin.d.freeSpaceAtBegin() < 2; ++i) + withFreeSpaceAtBegin.prepend(T_FOO); + QCOMPARE_GT(withFreeSpaceAtBegin.d.freeSpaceAtBegin(), 1); + + auto c = [&] { + switch (listState) { + case UnsharedList: return std::move(withFreeSpaceAtBegin); + case SharedList: return withFreeSpaceAtBegin; + } + Q_UNREACHABLE_RETURN(withFreeSpaceAtBegin); + }(); + + const auto n = [&] () -> qsizetype { + switch (relationWithPrependBuffer) { + case FitsIntoFreeSpaceAtBegin: + return qsizetype(1); + case FitsFreeSpaceAtBeginExactly: + return c.d.freeSpaceAtBegin(); + case ExceedsFreeSpaceAtBegin: + return c.d.freeSpaceAtBegin() + 1; + case FitsFreeSpaceAtBeginPlusSizeExactly: + return c.d.freeSpaceAtBegin() + c.size(); + case FullCapacity: + return c.capacity(); + }; + Q_UNREACHABLE_RETURN(0); + }(); + + const auto oldCapBegin = capBegin(c); + const auto oldCapEnd = capEnd(c); + + const std::vector v(n, T_BAR); + c.assign(v.begin(), v.end()); + QCOMPARE_EQ(c.d.freeSpaceAtBegin(), 0); // we used the prepend-buffer + if (listState != SharedList) { + // check that we didn't reallocate + QCOMPARE_EQ(capBegin(c), oldCapBegin); + QCOMPARE_EQ(capEnd(c), oldCapEnd); + } + } +} + +void tst_QList::appendRvalue() const +{ + QList<QString> v; + v.append("hello"); + QString world = "world"; + v.append(std::move(world)); + QCOMPARE(v.front(), QString("hello")); + QCOMPARE(v.back(), QString("world")); + + // check append rvalue to empty list + QList<QString> myvec; + QString test = "test"; + myvec.append(std::move(test)); + QCOMPARE(myvec.size(), 1); + QCOMPARE(myvec.front(), QString("test")); +} + +struct ConstructionCounted +{ + ConstructionCounted(int i) : i(i) { } + ConstructionCounted(ConstructionCounted &&other) noexcept + : i(other.i), copies(other.copies), moves(other.moves + 1) + { + // set to some easily noticeable values + other.i = -64; + other.copies = -64; + other.moves = -64; + } + ConstructionCounted &operator=(ConstructionCounted &&other) noexcept + { + i = other.i; + copies = other.copies; + moves = other.moves + 1; + // set to some easily noticeable values + other.i = -64; + other.copies = -64; + other.moves = -64; + return *this; + } + ConstructionCounted(const ConstructionCounted &other) noexcept + : i(other.i), copies(other.copies + 1), moves(other.moves) + { + } + ConstructionCounted &operator=(const ConstructionCounted &other) noexcept + { + i = other.i; + copies = other.copies + 1; + moves = other.moves; + return *this; + } + ~ConstructionCounted() = default; + + friend bool operator==(const ConstructionCounted &lhs, const ConstructionCounted &rhs) + { + return lhs.i == rhs.i; + } + + QString toString() { return QString::number(i); } + + int i; + int copies = 0; + int moves = 0; +}; +QT_BEGIN_NAMESPACE +namespace QTest { +char *toString(const ConstructionCounted &cc) +{ + char *str = new char[5]; + qsnprintf(str, 4, "%d", cc.i); + return str; +} +} +QT_END_NAMESPACE + +void tst_QList::appendList() const +{ + // By const-ref + { + QList<int> v1 = { 1, 2, 3, 4 }; + QList<int> v2 = { 5, 6, 7, 8 }; + v1.append(v2); + QCOMPARE(v2.size(), 4); + QCOMPARE(v1.size(), 8); + QList<int> expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; + QCOMPARE(v1, expected); + + QList<int> doubleExpected = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 }; + // append self to self + v1.append(v1); + QCOMPARE(v1.size(), 16); + QCOMPARE(v1, doubleExpected); + v1.resize(8); + + // append to self, but was shared + QList v1_2(v1); + v1.append(v1); + QCOMPARE(v1_2.size(), 8); + QCOMPARE(v1_2, expected); + QCOMPARE(v1.size(), 16); + QCOMPARE(v1, doubleExpected); + v1.resize(8); + + // append empty + QList<int> v3; + v1.append(v3); + + // append to empty + QList<int> v4; + v4.append(v1); + QCOMPARE(v4, expected); + + v1 = { 1, 2, 3, 4 }; + // Using operators + // << + QList<int> v5; + v5 << v1 << v2; + QCOMPARE(v5, expected); + + // += + QList<int> v6; + v6 += v1; + v6 += v2; + QCOMPARE(v6, expected); + + // + + QCOMPARE(v1 + v2, expected); + } + // By move + { + QList<ConstructionCounted> v1 = { 1, 2, 3, 4 }; + // Sanity check + QCOMPARE(v1.at(3).moves, 0); + QCOMPARE(v1.at(3).copies, 1); // because of initializer list + + QList<ConstructionCounted> v2 = { 5, 6, 7, 8 }; + v1.append(std::move(v2)); + QCOMPARE(v1.size(), 8); + QList<ConstructionCounted> expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; + QCOMPARE(v1, expected); + QCOMPARE(v1.at(0).copies, 1); + QCOMPARE(v1.at(0).moves, 1); + + QCOMPARE(v1.at(4).copies, 1); // was v2.at(0) + QCOMPARE(v1.at(4).moves, 1); + + // append move from empty + QList<ConstructionCounted> v3; + v1.append(std::move(v3)); + QCOMPARE(v1.size(), 8); + QCOMPARE(v1, expected); + + for (qsizetype i = 0; i < v1.size(); ++i) { + const auto &counter = v1.at(i); + QCOMPARE(counter.copies, 1); + QCOMPARE(counter.moves, 1); + } + + // append move to empty + QList<ConstructionCounted> v4; + v4.reserve(64); + v4.append(std::move(v1)); + QCOMPARE(v4.size(), 8); + QCOMPARE(v4, expected); + + for (qsizetype i = 0; i < v4.size(); ++i) { + const auto &counter = v4.at(i); + QCOMPARE(counter.copies, 1); + QCOMPARE(counter.moves, 2); + } + + QVERIFY(v4.capacity() >= 64); + + v1.swap(v4); // swap back... + + // append move from shared + QList<ConstructionCounted> v5 = { 1, 2, 3, 4 }; + QList<ConstructionCounted> v5_2(v5); + v1.append(std::move(v5_2)); + QCOMPARE(v1.size(), 12); + QList<ConstructionCounted> expectedTwelve = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4 }; + QCOMPARE(v1, expectedTwelve); + QCOMPARE(v5.size(), 4); + QList<ConstructionCounted> expectedFour = { 1, 2, 3, 4 }; + QCOMPARE(v5, expectedFour); + + QCOMPARE(v5.at(0).copies, 1); // from constructing with std::initializer_list + QCOMPARE(v5.at(0).moves, 0); + + // Using operators + // << + QList<ConstructionCounted> v6; + v6.reserve(4); + v6 << (QList<ConstructionCounted>() << 1 << 2); + v6 << (QList<ConstructionCounted>() << 3 << 4); + QCOMPARE(v6, expectedFour); + QCOMPARE(v6.at(0).copies, 1); + QCOMPARE(v6.at(0).moves, 3); + + // += + QList<ConstructionCounted> v7; + v7 += (QList<ConstructionCounted>() << 1 << 2); + v7 += (QList<ConstructionCounted>() << 3 << 4); + QCOMPARE(v7, expectedFour); + + // + + QList<ConstructionCounted> v8; + QCOMPARE(v8 + (QList<ConstructionCounted>() << 1 << 2 << 3 << 4), expectedFour); + v8 = { 1, 2 }; + QCOMPARE(v8 + (QList<ConstructionCounted>() << 3 << 4), expectedFour); + } +} + +void tst_QList::at() const +{ + QList<QString> myvec; + myvec << "foo" << "bar" << "baz"; + + QVERIFY(myvec.size() == 3); + QCOMPARE(myvec.at(0), QLatin1String("foo")); + QCOMPARE(myvec.at(1), QLatin1String("bar")); + QCOMPARE(myvec.at(2), QLatin1String("baz")); + + // append an item + myvec << "hello"; + QVERIFY(myvec.size() == 4); + QCOMPARE(myvec.at(0), QLatin1String("foo")); + QCOMPARE(myvec.at(1), QLatin1String("bar")); + QCOMPARE(myvec.at(2), QLatin1String("baz")); + QCOMPARE(myvec.at(3), QLatin1String("hello")); + + // remove an item + myvec.remove(1); + QVERIFY(myvec.size() == 3); + QCOMPARE(myvec.at(0), QLatin1String("foo")); + QCOMPARE(myvec.at(1), QLatin1String("baz")); + QCOMPARE(myvec.at(2), QLatin1String("hello")); +} + +template<typename T> +void tst_QList::capacity() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> myvec; + + // TODO: is this guaranteed? seems a safe assumption, but I suppose preallocation of a + // few items isn't an entirely unforseeable possibility. + QVERIFY(myvec.capacity() == 0); + QVERIFY(!myvec.isDetached()); + + // test it gets a size + myvec << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2); + QVERIFY(myvec.capacity() >= 3); + + // make sure it grows ok + myvec << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2); + QVERIFY(myvec.capacity() >= 6); + // let's try squeeze a bit + myvec.remove(3); + myvec.remove(3); + myvec.remove(3); + myvec.squeeze(); + QVERIFY(myvec.capacity() >= 3); + + myvec.remove(0); + myvec.remove(0); + myvec.remove(0); + myvec.squeeze(); + QVERIFY(myvec.capacity() == 0); +} + +template<typename T> +void tst_QList::clear() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> myvec; + myvec.clear(); + QVERIFY(!myvec.isDetached()); + + myvec << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2); + + const auto oldCapacity = myvec.capacity(); + QCOMPARE(myvec.size(), 3); + myvec.clear(); + QCOMPARE(myvec.size(), 0); + QCOMPARE(myvec.capacity(), oldCapacity); +} + +void tst_QList::constData() const +{ + int arr[] = { 42, 43, 44 }; + QList<int> myvec; + QCOMPARE(myvec.constData(), nullptr); + QVERIFY(!myvec.isDetached()); + + myvec << 42 << 43 << 44; + + QCOMPARE(memcmp(myvec.constData(), reinterpret_cast<const int *>(&arr), sizeof(int) * 3), 0); +} + +void tst_QList::contains() const +{ + QList<QString> myvec; + + QVERIFY(!myvec.contains(QLatin1String("test"))); + QVERIFY(!myvec.isDetached()); + + myvec << "aaa" << "bbb" << "ccc"; + + QVERIFY(myvec.contains(QLatin1String("aaa"))); + QVERIFY(myvec.contains(QLatin1String("bbb"))); + QVERIFY(myvec.contains(QLatin1String("ccc"))); + QVERIFY(!myvec.contains(QLatin1String("I don't exist"))); + + // add it and make sure it does :) + myvec.append(QLatin1String("I don't exist")); + QVERIFY(myvec.contains(QLatin1String("I don't exist"))); +} + +template<typename T> +void tst_QList::count() const +{ + TST_QLIST_CHECK_LEAKS(T) + + // total size + { + // zero size + QList<T> myvec; + QVERIFY(myvec.size() == 0); + QVERIFY(!myvec.isDetached()); + + // grow + myvec.append(SimpleValue<T>::at(0)); + QVERIFY(myvec.size() == 1); + myvec.append(SimpleValue<T>::at(1)); + QVERIFY(myvec.size() == 2); + + // shrink + myvec.remove(0); + QVERIFY(myvec.size() == 1); + myvec.remove(0); + QVERIFY(myvec.size() == 0); + } + + // count of items + { + QList<T> myvec; + QCOMPARE(myvec.count(SimpleValue<T>::at(0)), 0); + QVERIFY(!myvec.isDetached()); + + myvec << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2); + + // initial tests + QVERIFY(myvec.count(SimpleValue<T>::at(0)) == 1); + QVERIFY(myvec.count(SimpleValue<T>::at(3)) == 0); + + // grow + myvec.append(SimpleValue<T>::at(0)); + QVERIFY(myvec.count(SimpleValue<T>::at(0)) == 2); + + // shrink + myvec.remove(0); + QVERIFY(myvec.count(SimpleValue<T>::at(0)) == 1); + } +} + +void tst_QList::cpp17ctad() const +{ +#define QVERIFY_IS_VECTOR_OF(obj, Type) \ + QVERIFY2((std::is_same<decltype(obj), QList<Type>>::value), \ + QMetaType::fromType<decltype(obj)::value_type>().name()) +#define CHECK(Type, One, Two, Three) \ + do { \ + const Type v[] = {One, Two, Three}; \ + QList v1 = {One, Two, Three}; \ + QVERIFY_IS_VECTOR_OF(v1, Type); \ + QList v2(v1.begin(), v1.end()); \ + QVERIFY_IS_VECTOR_OF(v2, Type); \ + QList v3(std::begin(v), std::end(v)); \ + QVERIFY_IS_VECTOR_OF(v3, Type); \ + } while (false) \ + /*end*/ + CHECK(int, 1, 2, 3); + CHECK(double, 1.0, 2.0, 3.0); + CHECK(QString, QStringLiteral("one"), QStringLiteral("two"), QStringLiteral("three")); +#undef QVERIFY_IS_VECTOR_OF +#undef CHECK +} + +void tst_QList::data() const +{ + QList<int> myvec; + QCOMPARE(myvec.data(), nullptr); + + myvec << 42 << 43 << 44; + + // make sure it starts off ok + QCOMPARE(*(myvec.data() + 1), 43); + + // alter it + *(myvec.data() + 1) = 69; + + // check it altered + QCOMPARE(*(myvec.data() + 1), 69); + + int arr[] = { 42, 69, 44 }; + QCOMPARE(memcmp(myvec.data(), reinterpret_cast<int *>(&arr), sizeof(int) * 3), 0); + + const QList<int> constVec = myvec; + QCOMPARE(memcmp(constVec.data(), reinterpret_cast<const int *>(&arr), sizeof(int) * 3), 0); + QVERIFY(!constVec.isDetached()); // const data() does not detach() +} + +template<typename T> +void tst_QList::empty() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> myvec; + + // starts empty + QVERIFY(myvec.empty()); + QVERIFY(!myvec.isDetached()); + + // not empty + myvec.append(SimpleValue<T>::at(2)); + QVERIFY(!myvec.empty()); + + // empty again + myvec.remove(0); + QVERIFY(myvec.empty()); +} + +void tst_QList::endsWith() const +{ + QList<int> myvec; + + // empty vector + QVERIFY(!myvec.endsWith(1)); + + // add the one, should work + myvec.append(1); + QVERIFY(myvec.endsWith(1)); + + // add something else, fails now + myvec.append(3); + QVERIFY(!myvec.endsWith(1)); + + // remove it again :) + myvec.remove(1); + QVERIFY(myvec.endsWith(1)); +} + +template<typename T> +void tst_QList::eraseEmpty() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> v; + v.erase(v.begin(), v.end()); + QCOMPARE(v.size(), 0); +} + +template<typename T> +void tst_QList::eraseEmptyReserved() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> v; + v.reserve(10); + v.erase(v.begin(), v.end()); + QCOMPARE(v.size(), 0); +} + +template<typename T> +struct SharedVectorChecker +{ + SharedVectorChecker(const QList<T> &original, bool doCopyVector) + : originalSize(-1), + copy(0) + { + if (doCopyVector) { + originalSize = original.size(); + copy = new QList<T>(original); + // this is unlikely to fail, but if the check in the destructor fails it's good to know that + // we were still alright here. + QCOMPARE(originalSize, copy->size()); + } + } + + ~SharedVectorChecker() + { + if (copy) + QCOMPARE(copy->size(), originalSize); + delete copy; + } + + int originalSize; + QList<T> *copy; +}; + +template<typename T> +void tst_QList::erase(bool shared) const +{ + TST_QLIST_CHECK_LEAKS(T) + + // note: remove() is actually more efficient, and more dangerous, because it uses the + // non-detaching begin() / end() internally. you can also use constBegin() and constEnd() with + // erase(), but only using reinterpret_cast... because both iterator types are really just + // pointers. so we use a mix of erase() and remove() to cover more cases. + { + QList<T> v = SimpleValue<T>::vector(12); + SharedVectorChecker<T> svc(v, shared); + v.erase(v.begin()); + QCOMPARE(v.size(), 11); + for (int i = 0; i < 11; i++) + QCOMPARE(v.at(i), SimpleValue<T>::at(i + 1)); + v.erase(v.begin(), v.end()); + QCOMPARE(v.size(), 0); + if (shared) + QCOMPARE(SimpleValue<T>::vector(12), *svc.copy); + } + { + QList<T> v = SimpleValue<T>::vector(12); + SharedVectorChecker<T> svc(v, shared); + v.remove(1); + QCOMPARE(v.size(), 11); + QCOMPARE(v.at(0), SimpleValue<T>::at(0)); + for (int i = 1; i < 11; i++) + QCOMPARE(v.at(i), SimpleValue<T>::at(i + 1)); + v.erase(v.begin() + 1, v.end()); + QCOMPARE(v.size(), 1); + QCOMPARE(v.at(0), SimpleValue<T>::at(0)); + if (shared) + QCOMPARE(SimpleValue<T>::vector(12), *svc.copy); + } + { + QList<T> v = SimpleValue<T>::vector(12); + SharedVectorChecker<T> svc(v, shared); + v.erase(v.begin(), v.end() - 1); + QCOMPARE(v.size(), 1); + QCOMPARE(v.at(0), SimpleValue<T>::at(11)); + if (shared) + QCOMPARE(SimpleValue<T>::vector(12), *svc.copy); + } + { + QList<T> v = SimpleValue<T>::vector(12); + SharedVectorChecker<T> svc(v, shared); + v.remove(5); + QCOMPARE(v.size(), 11); + for (int i = 0; i < 5; i++) + QCOMPARE(v.at(i), SimpleValue<T>::at(i)); + for (int i = 5; i < 11; i++) + QCOMPARE(v.at(i), SimpleValue<T>::at(i + 1)); + v.erase(v.begin() + 1, v.end() - 1); + QCOMPARE(v.at(0), SimpleValue<T>::at(0)); + QCOMPARE(v.at(1), SimpleValue<T>::at(11)); + QCOMPARE(v.size(), 2); + if (shared) + QCOMPARE(SimpleValue<T>::vector(12), *svc.copy); + } +} + +template<typename T> +void tst_QList::eraseReserved() const +{ + TST_QLIST_CHECK_LEAKS(T) + + { + QList<T> v(12); + v.reserve(16); + v.erase(v.begin()); + QCOMPARE(v.size(), 11); + v.erase(v.begin(), v.end()); + QCOMPARE(v.size(), 0); + } + { + QList<T> v(12); + v.reserve(16); + v.erase(v.begin() + 1); + QCOMPARE(v.size(), 11); + v.erase(v.begin() + 1, v.end()); + QCOMPARE(v.size(), 1); + } + { + QList<T> v(12); + v.reserve(16); + v.erase(v.begin(), v.end() - 1); + QCOMPARE(v.size(), 1); + } + { + QList<T> v(12); + v.reserve(16); + v.erase(v.begin() + 5); + QCOMPARE(v.size(), 11); + v.erase(v.begin() + 1, v.end() - 1); + QCOMPARE(v.size(), 2); + } +} + +template<typename T> +void tst_QList::fill() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> myvec; + + // fill an empty list - it should resize + myvec.fill(SimpleValue<T>::at(1), 2); + QCOMPARE(myvec, QList<T>({ SimpleValue<T>::at(1), SimpleValue<T>::at(1) })); + + // resize + myvec.resize(5); + myvec.fill(SimpleValue<T>::at(1)); + QCOMPARE(myvec, QList<T>() << SimpleValue<T>::at(1) << SimpleValue<T>::at(1) + << SimpleValue<T>::at(1) << SimpleValue<T>::at(1) + << SimpleValue<T>::at(1)); + + // make sure it can resize itself too + myvec.fill(SimpleValue<T>::at(2), 10); + QCOMPARE(myvec, QList<T>() << SimpleValue<T>::at(2) << SimpleValue<T>::at(2) + << SimpleValue<T>::at(2) << SimpleValue<T>::at(2) + << SimpleValue<T>::at(2) << SimpleValue<T>::at(2) + << SimpleValue<T>::at(2) << SimpleValue<T>::at(2) + << SimpleValue<T>::at(2) << SimpleValue<T>::at(2)); + + // make sure it can resize to smaller size as well + myvec.fill(SimpleValue<T>::at(3), 2); + QCOMPARE(myvec, QList<T>() << SimpleValue<T>::at(3) << SimpleValue<T>::at(3)); +} + +template<typename T> +void tst_QList::fillDetach() const +{ + TST_QLIST_CHECK_LEAKS(T) + + // detaches to the same size + { + QList<T> original = { SimpleValue<T>::at(1), SimpleValue<T>::at(1), SimpleValue<T>::at(1) }; + QList<T> copy = original; + copy.fill(SimpleValue<T>::at(2)); + + QCOMPARE(original, + QList<T>({ SimpleValue<T>::at(1), SimpleValue<T>::at(1), SimpleValue<T>::at(1) })); + QCOMPARE(copy, + QList<T>({ SimpleValue<T>::at(2), SimpleValue<T>::at(2), SimpleValue<T>::at(2) })); + } + + // detaches and grows in size + { + QList<T> original = { SimpleValue<T>::at(1), SimpleValue<T>::at(1), SimpleValue<T>::at(1) }; + QList<T> copy = original; + copy.fill(SimpleValue<T>::at(2), 5); + + QCOMPARE(original, + QList<T>({ SimpleValue<T>::at(1), SimpleValue<T>::at(1), SimpleValue<T>::at(1) })); + QCOMPARE(copy, + QList<T>({ SimpleValue<T>::at(2), SimpleValue<T>::at(2), SimpleValue<T>::at(2), + SimpleValue<T>::at(2), SimpleValue<T>::at(2) })); + } + + // detaches and shrinks in size + { + QList<T> original = { SimpleValue<T>::at(1), SimpleValue<T>::at(1), SimpleValue<T>::at(1) }; + QList<T> copy = original; + copy.fill(SimpleValue<T>::at(2), 1); + + QCOMPARE(original, + QList<T>({ SimpleValue<T>::at(1), SimpleValue<T>::at(1), SimpleValue<T>::at(1) })); + QCOMPARE(copy, QList<T>({ SimpleValue<T>::at(2) })); + } +} + +void tst_QList::first() const +{ + QList<int> myvec; + myvec << 69 << 42 << 3; + + // test it starts ok + QCOMPARE(myvec.first(), 69); + QCOMPARE(myvec.constFirst(), 69); + + // test removal changes + myvec.remove(0); + QCOMPARE(myvec.first(), 42); + QCOMPARE(myvec.constFirst(), 42); + + // test prepend changes + myvec.prepend(23); + QCOMPARE(myvec.first(), 23); + QCOMPARE(myvec.constFirst(), 23); + + + QCOMPARE(QList<int>().first(0), QList<int>()); + QCOMPARE(myvec.first(0), QList<int>()); + QCOMPARE(myvec.first(1), (QList<int>{23})); + QCOMPARE(myvec.first(2), (QList<int>{23, 42})); + QCOMPARE(myvec.first(3), myvec); +} + +void tst_QList::constFirst() const +{ + QList<int> myvec; + myvec << 69 << 42 << 3; + + // test it starts ok + QCOMPARE(myvec.constFirst(), 69); + QVERIFY(myvec.isDetached()); + + QList<int> myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constFirst(), 69); + QCOMPARE(myvecCopy.constFirst(), 69); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + // test removal changes + myvec.remove(0); + QVERIFY(myvec.isDetached()); + QVERIFY(!myvec.isSharedWith(myvecCopy)); + QCOMPARE(myvec.constFirst(), 42); + QCOMPARE(myvecCopy.constFirst(), 69); + + myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constFirst(), 42); + QCOMPARE(myvecCopy.constFirst(), 42); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + // test prepend changes + myvec.prepend(23); + QVERIFY(myvec.isDetached()); + QVERIFY(!myvec.isSharedWith(myvecCopy)); + QCOMPARE(myvec.constFirst(), 23); + QCOMPARE(myvecCopy.constFirst(), 42); + + myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constFirst(), 23); + QCOMPARE(myvecCopy.constFirst(), 23); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); +} + + +template<typename T> +void tst_QList::fromList() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> list; + list << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2) << SimpleValue<T>::at(3); + + QList<T> myvec; + myvec = QList<T>::fromList(list); + + // test it worked ok + QCOMPARE(myvec, QList<T>() << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2) << SimpleValue<T>::at(3)); + QCOMPARE(list, QList<T>() << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2) << SimpleValue<T>::at(3)); +} + +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) +void tst_QList::fromStdVector() const +{ + // stl = :( + std::vector<QString> svec; + svec.push_back(QLatin1String("aaa")); + svec.push_back(QLatin1String("bbb")); + svec.push_back(QLatin1String("ninjas")); + svec.push_back(QLatin1String("pirates")); + QList<QString> myvec = QList<QString>::fromStdVector(svec); + + // test it converts ok + QCOMPARE(myvec, QList<QString>() << "aaa" << "bbb" << "ninjas" << "pirates"); +} +#endif + +void tst_QList::indexOf() const +{ + QList<QString> myvec; + + QCOMPARE(myvec.indexOf("A"), -1); + QCOMPARE(myvec.indexOf("A", 5), -1); + QVERIFY(!myvec.isDetached()); + + myvec << "A" << "B" << "C" << "B" << "A"; + + QVERIFY(myvec.indexOf("B") == 1); + QVERIFY(myvec.indexOf("B", 1) == 1); + QVERIFY(myvec.indexOf("B", 2) == 3); + QVERIFY(myvec.indexOf("X") == -1); + QVERIFY(myvec.indexOf("X", 2) == -1); + + // add an X + myvec << "X"; + QVERIFY(myvec.indexOf("X") == 5); + QVERIFY(myvec.indexOf("X", 5) == 5); + QVERIFY(myvec.indexOf("X", 6) == -1); + + // remove first A + myvec.remove(0); + QVERIFY(myvec.indexOf("A") == 3); + QVERIFY(myvec.indexOf("A", 3) == 3); + QVERIFY(myvec.indexOf("A", 4) == -1); +} + +template <typename T> +void tst_QList::insert() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> myvec; + const T + tA = SimpleValue<T>::at(0), + tB = SimpleValue<T>::at(1), + tC = SimpleValue<T>::at(2), + tX = SimpleValue<T>::at(3), + tZ = SimpleValue<T>::at(4), + tT = SimpleValue<T>::at(5), + ti = SimpleValue<T>::at(6); + myvec << tA << tB << tC; + QList<T> myvec2 = myvec; + + // first position + QCOMPARE(myvec.at(0), tA); + myvec.insert(0, tX); + QCOMPARE(myvec.at(0), tX); + QCOMPARE(myvec.at(1), tA); + + QCOMPARE(myvec2.at(0), tA); + myvec2.insert(myvec2.begin(), tX); + QCOMPARE(myvec2.at(0), tX); + QCOMPARE(myvec2.at(1), tA); + + // middle + myvec.insert(1, tZ); + QCOMPARE(myvec.at(0), tX); + QCOMPARE(myvec.at(1), tZ); + QCOMPARE(myvec.at(2), tA); + + myvec2.insert(myvec2.begin() + 1, tZ); + QCOMPARE(myvec2.at(0), tX); + QCOMPARE(myvec2.at(1), tZ); + QCOMPARE(myvec2.at(2), tA); + + // end + myvec.insert(5, tT); + QCOMPARE(myvec.at(5), tT); + QCOMPARE(myvec.at(4), tC); + + myvec2.insert(myvec2.end(), tT); + QCOMPARE(myvec2.at(5), tT); + QCOMPARE(myvec2.at(4), tC); + + // insert a lot of garbage in the middle + myvec.insert(2, 2, ti); + QCOMPARE(myvec, QList<T>() << tX << tZ << ti << ti + << tA << tB << tC << tT); + + myvec2.insert(myvec2.begin() + 2, 2, ti); + QCOMPARE(myvec2, myvec); + + // insert from references to the same container: + myvec.insert(0, 1, myvec[5]); // inserts tB + myvec2.insert(0, 1, myvec2[5]); // inserts tB + QCOMPARE(myvec, QList<T>() << tB << tX << tZ << ti << ti + << tA << tB << tC << tT); + QCOMPARE(myvec2, myvec); + + myvec.insert(0, 1, const_cast<const QList<T>&>(myvec)[0]); // inserts tB + myvec2.insert(0, 1, const_cast<const QList<T>&>(myvec2)[0]); // inserts tB + QCOMPARE(myvec, QList<T>() << tB << tB << tX << tZ << ti << ti + << tA << tB << tC << tT); + QCOMPARE(myvec2, myvec); + + // Different insert() into empty list overloads + { + QList<T> myvec; + auto it = myvec.insert(0, tA); + QCOMPARE(myvec.size(), 1); + QCOMPARE(myvec.front(), tA); + QCOMPARE(it, myvec.begin()); + } + { + QList<T> myvec; + auto it = myvec.insert(0, 3, tX); + QCOMPARE(myvec.size(), 3); + QCOMPARE(myvec, QList<T>({ tX, tX, tX })); + QCOMPARE(it, myvec.begin()); + } + { + QList<T> myvec; + auto it = myvec.insert(myvec.cbegin(), tA); + QCOMPARE(myvec.size(), 1); + QCOMPARE(myvec.front(), tA); + QCOMPARE(it, myvec.begin()); + } + { + QList<T> myvec; + auto it = myvec.insert(myvec.cbegin(), 3, tX); + QCOMPARE(myvec.size(), 3); + QCOMPARE(myvec, QList<T>({ tX, tX, tX })); + QCOMPARE(it, myvec.begin()); + } + { + QList<QString> myvec; + QString test = "test"; + auto it = myvec.insert(0, std::move(test)); + QCOMPARE(myvec.size(), 1); + QCOMPARE(myvec.front(), u"test"); + QCOMPARE(it, myvec.begin()); + } + { + QList<QString> myvec; + QString test = "test"; + auto it = myvec.insert(myvec.cbegin(), std::move(test)); + QCOMPARE(myvec.size(), 1); + QCOMPARE(myvec.front(), u"test"); + QCOMPARE(it, myvec.begin()); + } +} + +void tst_QList::insertZeroCount_data() +{ + QTest::addColumn<int>("pos"); + QTest::newRow("0") << 0; + QTest::newRow("1") << 1; +} + +void tst_QList::insertZeroCount() const +{ + QFETCH(int, pos); + QList<int> x; + x << 0 << 0; + x.insert(pos, 0, 1); + QCOMPARE(x[pos], 0); + QList<int> y; + y = x; + y.insert(pos, 0, 2); + QCOMPARE(y[pos], 0); +} + +void tst_QList::isEmpty() const +{ + QList<QString> myvec; + + // starts ok + QVERIFY(myvec.isEmpty()); + QVERIFY(!myvec.isDetached()); + + // not empty now + myvec.append(QLatin1String("hello there")); + QVERIFY(!myvec.isEmpty()); + + // empty again + myvec.remove(0); + QVERIFY(myvec.isEmpty()); +} + +void tst_QList::last() const +{ + QList<QString> myvec; + myvec << "A" << "B" << "C"; + + // test starts ok + QCOMPARE(myvec.last(), QLatin1String("C")); + QCOMPARE(myvec.constLast(), QLatin1String("C")); + + // test it changes ok + myvec.append(QLatin1String("X")); + QCOMPARE(myvec.last(), QLatin1String("X")); + QCOMPARE(myvec.constLast(), QLatin1String("X")); + + // and remove again + myvec.remove(3); + QCOMPARE(myvec.last(), QLatin1String("C")); + QCOMPARE(myvec.constLast(), QLatin1String("C")); + + QCOMPARE(QList<QString>().last(0), QList<QString>()); + QCOMPARE(myvec.last(0), QList<QString>()); + QCOMPARE(myvec.last(1), (QList<QString>{QLatin1String("C")})); + QCOMPARE(myvec.last(2), (QList<QString>{QLatin1String("B"), QLatin1String("C")})); + QCOMPARE(myvec.last(3), myvec); +} + +void tst_QList::constLast() const +{ + QList<int> myvec; + myvec << 69 << 42 << 3; + + // test it starts ok + QCOMPARE(myvec.constLast(), 3); + QVERIFY(myvec.isDetached()); + + QList<int> myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constLast(), 3); + QCOMPARE(myvecCopy.constLast(), 3); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + // test removal changes + myvec.removeLast(); + QVERIFY(myvec.isDetached()); + QVERIFY(!myvec.isSharedWith(myvecCopy)); + QCOMPARE(myvec.constLast(), 42); + QCOMPARE(myvecCopy.constLast(), 3); + + myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constLast(), 42); + QCOMPARE(myvecCopy.constLast(), 42); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + // test prepend changes + myvec.append(23); + QVERIFY(myvec.isDetached()); + QVERIFY(!myvec.isSharedWith(myvecCopy)); + QCOMPARE(myvec.constLast(), 23); + QCOMPARE(myvecCopy.constLast(), 42); + + myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constLast(), 23); + QCOMPARE(myvecCopy.constLast(), 23); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); +} + +void tst_QList::lastIndexOf() const +{ + QList<QString> myvec; + + QCOMPARE(myvec.lastIndexOf("A"), -1); + QCOMPARE(myvec.lastIndexOf("A", 5), -1); + QVERIFY(!myvec.isDetached()); + + myvec << "A" << "B" << "C" << "B" << "A"; + + QVERIFY(myvec.lastIndexOf("B") == 3); + QVERIFY(myvec.lastIndexOf("B", 2) == 1); + QVERIFY(myvec.lastIndexOf("X") == -1); + QVERIFY(myvec.lastIndexOf("X", 2) == -1); + + // add an X + myvec << "X"; + QVERIFY(myvec.lastIndexOf("X") == 5); + QVERIFY(myvec.lastIndexOf("X", 5) == 5); + QVERIFY(myvec.lastIndexOf("X", 3) == -1); + + // remove first A + myvec.remove(0); + QVERIFY(myvec.lastIndexOf("A") == 3); + QVERIFY(myvec.lastIndexOf("A", 3) == 3); + QVERIFY(myvec.lastIndexOf("A", 2) == -1); +} + +void tst_QList::mid() const +{ + QList<QString> list; + + QCOMPARE(list.mid(4, 2), QList<QString>()); + QCOMPARE(list.mid(0, 3), QList<QString>()); + QCOMPARE(list.mid(-2, 3), QList<QString>()); + QVERIFY(!list.isDetached()); + + list << "foo" << "bar" << "baz" << "bak" << "buck" << "hello" << "kitty"; + + QCOMPARE(list.mid(3, 3), QList<QString>() << "bak" << "buck" << "hello"); + QCOMPARE(list.mid(6, 10), QList<QString>() << "kitty"); + QCOMPARE(list.mid(-1, 20), list); + QCOMPARE(list.mid(4), QList<QString>() << "buck" << "hello" << "kitty"); +} + +void tst_QList::sliced() const +{ + QList<QString> list; + list << "foo" << "bar" << "baz" << "bak" << "buck" << "hello" << "kitty"; + + QCOMPARE(QList<QString>().sliced(0), QList<QString>()); + QCOMPARE(QList<QString>().sliced(0, 0), QList<QString>()); + QCOMPARE(list.sliced(3, 3), QList<QString>() << "bak" << "buck" << "hello"); + QCOMPARE(list.sliced(3), QList<QString>() << "bak" << "buck" << "hello" << "kitty"); + QCOMPARE(list.sliced(6, 1), QList<QString>() << "kitty"); + QCOMPARE(list.sliced(6), QList<QString>() << "kitty"); + QCOMPARE(list.sliced(0, list.size()), list); + QCOMPARE(list.sliced(0), list); + QCOMPARE(list.sliced(4), QList<QString>() << "buck" << "hello" << "kitty"); +} + +template <typename T> +void tst_QList::qhash() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> l1, l2; + QCOMPARE(qHash(l1), qHash(l2)); + l1 << SimpleValue<T>::at(0); + l2 << SimpleValue<T>::at(0); + QCOMPARE(qHash(l1), qHash(l2)); +} + +template <typename T> +void tst_QList::move() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // move an item + list.move(0, list.size() - 1); + QCOMPARE(list, QList<T>() << T_BAR << T_BAZ << T_FOO); + + // move it back + list.move(list.size() - 1, 0); + QCOMPARE(list, QList<T>() << T_FOO << T_BAR << T_BAZ); + + // move an item in the middle + list.move(1, 0); + QCOMPARE(list, QList<T>() << T_BAR << T_FOO << T_BAZ); +} + +template<typename T> +void tst_QList::prepend() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> myvec; + + T val1 = SimpleValue<T>::at(0); + T val2 = SimpleValue<T>::at(1); + T val3 = SimpleValue<T>::at(2); + T val4 = SimpleValue<T>::at(3); + T val5 = SimpleValue<T>::at(4); + + // prepend to default-constructed empty list + myvec.prepend(val1); + QCOMPARE(myvec.size(), 1); + QCOMPARE(myvec.at(0), val1); + myvec.clear(); + + myvec << val1 << val2 << val3; + + // starts ok + QVERIFY(myvec.size() == 3); + QCOMPARE(myvec.at(0), val1); + + // add something + myvec.prepend(val4); + QCOMPARE(myvec.at(0), val4); + QCOMPARE(myvec.at(1), val1); + QVERIFY(myvec.size() == 4); + + // something else + myvec.prepend(val5); + QCOMPARE(myvec.at(0), val5); + QCOMPARE(myvec.at(1), val4); + QCOMPARE(myvec.at(2), val1); + QVERIFY(myvec.size() == 5); + + // clear and prepend to an empty vector + myvec.clear(); + QVERIFY(myvec.size() == 0); + myvec.prepend(val5); + QVERIFY(myvec.size() == 1); + QCOMPARE(myvec.at(0), val5); +} + +void tst_QList::prependRvalue() const +{ + QList<QString> myvec; + + QString hello = "hello"; + QString world = "world"; + + myvec.prepend(std::move(world)); + QCOMPARE(myvec.size(), 1); + + myvec.prepend(std::move(hello)); + QCOMPARE(myvec.size(), 2); + QCOMPARE(myvec, QList<QString>({ "hello", "world" })); +} + +void tst_QList::removeAllWithAlias() const +{ + QList<QString> strings; + strings << "One" << "Two" << "Three" << "One" /* must be distinct, but equal */; + QCOMPARE(strings.removeAll(strings.front()), 2); // will trigger asan/ubsan +} + +template<typename T> +void tst_QList::remove() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> myvec; + T val1 = SimpleValue<T>::at(1); + T val2 = SimpleValue<T>::at(2); + T val3 = SimpleValue<T>::at(3); + T val4 = SimpleValue<T>::at(4); + T val5 = SimpleValue<T>::at(5); + + // some operations on empty list + QVERIFY(!myvec.removeOne(val1)); + QCOMPARE(myvec.removeAll(val2), 0); + auto count = myvec.removeIf([](const T&) { return true; }); + QCOMPARE(count, 0); + + myvec << val1 << val2 << val3 << val4; + myvec << val1 << val2 << val3 << val4; + myvec << val1 << val2 << val3 << val4; + // remove by index + myvec.remove(1); + QCOMPARE(myvec, QList<T>({ val1, val3, val4, val1, val2, val3, val4, val1, val2, val3, val4 })); + myvec.removeAt(6); + QCOMPARE(myvec, QList<T>({ val1, val3, val4, val1, val2, val3, val1, val2, val3, val4 })); + + // removeOne() + QVERIFY(!myvec.removeOne(val5)); + QVERIFY(myvec.removeOne(val2)); + QCOMPARE(myvec, QList<T>({ val1, val3, val4, val1, val3, val1, val2, val3, val4 })); + + QList<T> myvecCopy = myvec; + QVERIFY(myvecCopy.isSharedWith(myvec)); + // removeAll() + QCOMPARE(myvec.removeAll(val5), 0); + QVERIFY(myvecCopy.isSharedWith(myvec)); + QCOMPARE(myvec.removeAll(val1), 3); + QVERIFY(!myvecCopy.isSharedWith(myvec)); + QCOMPARE(myvec, QList<T>({ val3, val4, val3, val2, val3, val4 })); + QCOMPARE(myvecCopy, QList<T>({ val1, val3, val4, val1, val3, val1, val2, val3, val4 })); + myvecCopy = myvec; + QVERIFY(myvecCopy.isSharedWith(myvec)); + QCOMPARE(myvec.removeAll(val2), 1); + QVERIFY(!myvecCopy.isSharedWith(myvec)); + QCOMPARE(myvec, QList<T>({ val3, val4, val3, val3, val4 })); + QCOMPARE(myvecCopy, QList<T>({ val3, val4, val3, val2, val3, val4 })); + + // removeIf + count = myvec.removeIf([&val4](const T &val) { return val == val4; }); + QCOMPARE(count, 2); + QCOMPARE(myvec, QList<T>({ val3, val3, val3 })); + + // remove rest + myvec.remove(0, 3); + QCOMPARE(myvec, QList<T>()); +} + +struct RemoveLastTestClass +{ + RemoveLastTestClass() { other = 0; deleted = false; } + RemoveLastTestClass *other; + bool deleted; + ~RemoveLastTestClass() + { + deleted = true; + if (other) + other->other = 0; + } +}; + +void tst_QList::removeFirstLast() const +{ + // pop_pack - pop_front + QList<int> t, t2; + t.append(1); + t.append(2); + t.append(3); + t.append(4); + t2 = t; + t.pop_front(); + QCOMPARE(t.size(), 3); + QCOMPARE(t.at(0), 2); + t.pop_back(); + QCOMPARE(t.size(), 2); + QCOMPARE(t.at(0), 2); + QCOMPARE(t.at(1), 3); + + // takefirst - takeLast + int n1 = t2.takeLast(); + QCOMPARE(t2.size(), 3); + QCOMPARE(n1, 4); + QCOMPARE(t2.at(0), 1); + QCOMPARE(t2.at(2), 3); + n1 = t2.takeFirst(); + QCOMPARE(t2.size(), 2); + QCOMPARE(n1, 1); + QCOMPARE(t2.at(0), 2); + QCOMPARE(t2.at(1), 3); + + // remove first + QList<int> x, y; + x.append(1); + x.append(2); + y = x; + x.removeFirst(); + QCOMPARE(x.size(), 1); + QCOMPARE(y.size(), 2); + QCOMPARE(x.at(0), 2); + + // remove Last + QList<RemoveLastTestClass> v; + v.resize(2); + v[0].other = &(v[1]); + v[1].other = &(v[0]); + // Check dtor - complex type + QVERIFY(v.at(0).other != 0); + v.removeLast(); + QVERIFY(v.at(0).other == 0); + QCOMPARE(v.at(0).deleted, false); + // check iterator + int count = 0; + for (QList<RemoveLastTestClass>::const_iterator i = v.constBegin(); i != v.constEnd(); ++i) { + ++count; + QVERIFY(i->other == 0); + QCOMPARE(i->deleted, false); + } + // Check size + QCOMPARE(count, 1); + QCOMPARE(v.size(), 1); + v.removeLast(); + QCOMPARE(v.size(), 0); + // Check if we do correct realloc + QList<int> v2, v3; + v2.append(1); + v2.append(2); + v3 = v2; // shared + v2.removeLast(); + QCOMPARE(v2.size(), 1); + QCOMPARE(v3.size(), 2); + QCOMPARE(v2.at(0), 1); + QCOMPARE(v3.at(0), 1); + QCOMPARE(v3.at(1), 2); + + // Remove last with shared + QList<int> z1, z2; + z1.append(9); + z2 = z1; + z1.removeLast(); + QCOMPARE(z1.size(), 0); + QCOMPARE(z2.size(), 1); + QCOMPARE(z2.at(0), 9); +} + + +void tst_QList::resizePOD_data() const +{ + QTest::addColumn<QList<int> >("vector"); + QTest::addColumn<int>("size"); + + QVERIFY(!QTypeInfo<int>::isComplex); + QVERIFY(QTypeInfo<int>::isRelocatable); + + QList<int> null; + QList<int> empty(0, 5); + QList<int> emptyReserved; + QList<int> nonEmpty; + QList<int> nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + nonEmpty << 0 << 1 << 2 << 3 << 4; + nonEmptyReserved << 0 << 1 << 2 << 3 << 4 << 5 << 6; + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + QTest::newRow("null") << null << 10; + QTest::newRow("null and 0 size") << null << 0; + QTest::newRow("empty") << empty << 10; + QTest::newRow("empty and 0 size") << empty << 0; + QTest::newRow("emptyReserved") << emptyReserved << 10; + QTest::newRow("nonEmpty") << nonEmpty << 10; + QTest::newRow("nonEmptyReserved") << nonEmptyReserved << 10; +} + +void tst_QList::resizePOD() const +{ + QFETCH(QList<int>, vector); + QFETCH(int, size); + + const int oldSize = vector.size(); + + vector.resize(size); + QCOMPARE(vector.size(), size); + QVERIFY(vector.capacity() >= size); + if (vector.isEmpty()) + QVERIFY(!vector.isDetached()); + + for (int i = oldSize; i < size; ++i) + QVERIFY(vector[i] == 0); // check initialization + + const int capacity = vector.capacity(); + + vector.clear(); + QCOMPARE(vector.size(), 0); + QVERIFY(vector.capacity() <= capacity); +} + +void tst_QList::resizeComplexMovable_data() const +{ + QTest::addColumn<QList<Movable> >("vector"); + QTest::addColumn<int>("size"); + + QVERIFY(QTypeInfo<Movable>::isComplex); + QVERIFY(QTypeInfo<Movable>::isRelocatable); + + QList<Movable> null; + QList<Movable> empty(0, 'Q'); + QList<Movable> emptyReserved; + QList<Movable> nonEmpty; + QList<Movable> nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + nonEmpty << '0' << '1' << '2' << '3' << '4'; + nonEmptyReserved << '0' << '1' << '2' << '3' << '4' << '5' << '6'; + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + QTest::newRow("null") << null << 10; + QTest::newRow("null and 0 size") << null << 0; + QTest::newRow("empty") << empty << 10; + QTest::newRow("empty and 0 size") << empty << 0; + QTest::newRow("emptyReserved") << emptyReserved << 10; + QTest::newRow("nonEmpty") << nonEmpty << 10; + QTest::newRow("nonEmptyReserved") << nonEmptyReserved << 10; +} + +void tst_QList::resizeComplexMovable() const +{ + const int items = Movable::counter.loadAcquire(); + { + QFETCH(QList<Movable>, vector); + QFETCH(int, size); + + const int oldSize = vector.size(); + + vector.resize(size); + QCOMPARE(vector.size(), size); + QVERIFY(vector.capacity() >= size); + if (vector.isEmpty()) + QVERIFY(!vector.isDetached()); + for (int i = oldSize; i < size; ++i) + QVERIFY(vector[i] == 'j'); // check initialization + + const int capacity = vector.capacity(); + + vector.resize(0); + QCOMPARE(vector.size(), 0); + QVERIFY(vector.capacity() <= capacity); + } + QCOMPARE(items, Movable::counter.loadAcquire()); +} + +void tst_QList::resizeComplex_data() const +{ + QTest::addColumn<QList<Custom> >("vector"); + QTest::addColumn<int>("size"); + + QVERIFY(QTypeInfo<Custom>::isComplex); + QVERIFY(!QTypeInfo<Custom>::isRelocatable); + + QList<Custom> null; + QList<Custom> empty(0, '0'); + QList<Custom> emptyReserved; + QList<Custom> nonEmpty; + QList<Custom> nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + nonEmpty << '0' << '1' << '2' << '3' << '4'; + nonEmptyReserved << '0' << '1' << '2' << '3' << '4' << '5' << '6'; + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + QTest::newRow("null") << null << 10; + QTest::newRow("null and 0 size") << null << 0; + QTest::newRow("empty") << empty << 10; + QTest::newRow("empty and 0 size") << empty << 0; + QTest::newRow("emptyReserved") << emptyReserved << 10; + QTest::newRow("nonEmpty") << nonEmpty << 10; + QTest::newRow("nonEmptyReserved") << nonEmptyReserved << 10; +} + +void tst_QList::resizeComplex() const +{ + const int items = Custom::counter.loadAcquire(); + { + QFETCH(QList<Custom>, vector); + QFETCH(int, size); + + int oldSize = vector.size(); + vector.resize(size); + QCOMPARE(vector.size(), size); + QVERIFY(vector.capacity() >= size); + if (vector.isEmpty()) + QVERIFY(!vector.isDetached()); + for (int i = oldSize; i < size; ++i) + QVERIFY(vector[i].i == 'j'); // check default initialization + + const int capacity = vector.capacity(); + + vector.resize(0); + QCOMPARE(vector.size(), 0); + QVERIFY(vector.isEmpty()); + QVERIFY(vector.capacity() <= capacity); + } + QCOMPARE(Custom::counter.loadAcquire(), items); +} + +void tst_QList::resizeCtorAndDtor() const +{ + const int items = Custom::counter.loadAcquire(); + { + QList<Custom> null; + QList<Custom> empty(0, '0'); + QList<Custom> emptyReserved; + QList<Custom> nonEmpty; + QList<Custom> nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + nonEmpty << '0' << '1' << '2' << '3' << '4'; + nonEmptyReserved << '0' << '1' << '2' << '3' << '4' << '5' << '6'; + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + // start playing with vectors + null.resize(21); + nonEmpty.resize(2); + emptyReserved.resize(0); + nonEmpty.resize(0); + nonEmptyReserved.resize(2); + } + QCOMPARE(Custom::counter.loadAcquire(), items); +} + +void tst_QList::resizeToZero() const +{ + QList<int> x; + QList<int> y; + x << 1 << 2; + y = x; + y.resize(0); + QCOMPARE(y.size(), 0); + // grow back + y.resize(x.size()); + QCOMPARE(y.size(), x.size()); + // default initialized + QCOMPARE(y[0], 0); + QCOMPARE(y[1], 0); +} + +void tst_QList::resizeToTheSameSize_data() +{ + QTest::addColumn<QList<int>>("x"); + QTest::newRow("size 2") << QList({ 1, 2 }); + QTest::newRow("size 0") << QList<int>(); +} + +void tst_QList::resizeToTheSameSize() const +{ + QFETCH(QList<int>, x); + QList<int> y; + y = x; + y.resize(x.size()); + QCOMPARE(y.size(), x.size()); +} + +void tst_QList::resizeForOverwrite() const +{ + constexpr int BUILD_COUNT = 42; + { + // Smoke test + QList<int> l(BUILD_COUNT, Qt::Uninitialized); + l.resizeForOverwrite(l.size() + BUILD_COUNT); + } + + { + const int beforeCounter = Movable::counter.loadRelaxed(); + QList<Movable> l(BUILD_COUNT, Qt::Uninitialized); + const int after1Counter = Movable::counter.loadRelaxed(); + QCOMPARE(after1Counter, beforeCounter + BUILD_COUNT); + + l.resizeForOverwrite(l.size() + BUILD_COUNT); + const int after2Counter = Movable::counter.loadRelaxed(); + QCOMPARE(after2Counter, after1Counter + BUILD_COUNT); + } + + struct QtInitializationSupport { + bool wasInitialized; + QtInitializationSupport() : wasInitialized(true) {} + explicit QtInitializationSupport(Qt::Initialization) : wasInitialized(false) {} + }; + + { + QList<QtInitializationSupport> l(BUILD_COUNT); + for (const auto &elem : l) + QVERIFY(elem.wasInitialized); + l.resize(l.size() + BUILD_COUNT); + for (const auto &elem : l) + QVERIFY(elem.wasInitialized); + } + + { + QList<QtInitializationSupport> l(BUILD_COUNT, Qt::Uninitialized); + for (const auto &elem : l) + QVERIFY(!elem.wasInitialized); + l.resizeForOverwrite(l.size() + BUILD_COUNT); + for (const auto &elem : l) + QVERIFY(!elem.wasInitialized); + } +} + +void tst_QList::iterators() const +{ + QList<int> v; + + QCOMPARE(v.begin(), v.end()); + QCOMPARE(v.rbegin(), v.rend()); + + qsizetype idx = 0; + for (; idx < 10; ++idx) + v.push_back(idx); + + // stl-style iterators + idx = 0; + auto it = v.begin(); + QCOMPARE(*it, idx); + // idx == 0 + + std::advance(it, 7); + idx += 7; + QCOMPARE(*it, idx); + // idx == 7 + + it++; + idx++; + QCOMPARE(*it, idx); + // idx == 8 + + ++it; + ++idx; + QCOMPARE(*it, idx); + // idx == 9 + + std::advance(it, -3); + idx -= 3; + QCOMPARE(*it, idx); + // idx == 6 + + it--; + idx--; + QCOMPARE(*it, idx); + // idx == 5 + + --it; + --idx; + QCOMPARE(*it, idx); + // idx == 4 + + it = it + 1; + idx = idx + 1; + QCOMPARE(*it, idx); + // idx == 5 + + it = it + ptrdiff_t(1); + idx = idx + 1; + QCOMPARE(*it, idx); + // idx == 6 + + it = it + qsizetype(1); + idx = idx + 1; + QCOMPARE(*it, idx); + // idx == 7 + + it = it - qsizetype(1); + idx = idx - 1; + QCOMPARE(*it, idx); + // idx == 6 + + it = it - ptrdiff_t(1); + idx = idx - 1; + QCOMPARE(*it, idx); + // idx == 5 + + it = it - 1; + idx = idx - 1; + QCOMPARE(*it, idx); + // idx == 4 + + it -= 1; + idx -= 1; + QCOMPARE(*it, idx); + // idx == 3 + + it -= qsizetype(1); + idx -= 1; + QCOMPARE(*it, idx); + // idx == 2 + + it -= ptrdiff_t(1); + idx -= 1; + QCOMPARE(*it, idx); + // idx == 1 + + it += ptrdiff_t(1); + idx += 1; + QCOMPARE(*it, idx); + // idx == 2 + + it += qsizetype(1); + idx += 1; + QCOMPARE(*it, idx); + // idx == 3 + + it += 1; + idx += 1; + QCOMPARE(*it, idx); + // idx == 4 + + *it = idx + 1; + QCOMPARE(*it, idx + 1); + *it = idx; + + // stl-style reverse iterators + idx = v.size() - 1; + auto rit = v.rbegin(); + QCOMPARE(*rit, idx); + + *rit = idx + 1; + QCOMPARE(*rit, idx + 1); + *rit = idx; + + std::advance(rit, 5); + idx -= 5; + QCOMPARE(*rit, idx); + + ++rit; + --idx; + QCOMPARE(*rit, idx); + + rit++; + idx--; + QCOMPARE(*rit, idx); + + std::advance(rit, -4); + idx += 4; + QCOMPARE(*rit, idx); + + --rit; + ++idx; + QCOMPARE(*rit, idx); + + rit--; + idx++; + QCOMPARE(*rit, idx); +} + +void tst_QList::constIterators() const +{ + const QList<int> constEmptyList; + QCOMPARE(constEmptyList.cbegin(), constEmptyList.cend()); + QCOMPARE(constEmptyList.begin(), constEmptyList.cbegin()); + QCOMPARE(constEmptyList.end(), constEmptyList.cend()); + QCOMPARE(constEmptyList.constBegin(), constEmptyList.constEnd()); + QCOMPARE(constEmptyList.constBegin(), constEmptyList.cbegin()); + QCOMPARE(constEmptyList.constEnd(), constEmptyList.cend()); + QVERIFY(!constEmptyList.isDetached()); + + const QList<int> v { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + // stl-style iterators + qsizetype idx = 0; + auto it = v.cbegin(); + QCOMPARE(*it, idx); + // idx == 0 + + std::advance(it, 7); + idx += 7; + QCOMPARE(*it, idx); + // idx == 7 + + it++; + idx++; + QCOMPARE(*it, idx); + // idx == 8 + + ++it; + ++idx; + QCOMPARE(*it, idx); + // idx == 9 + + std::advance(it, -3); + idx -= 3; + QCOMPARE(*it, idx); + // idx == 6 + + it--; + idx--; + QCOMPARE(*it, idx); + // idx == 5 + + --it; + --idx; + QCOMPARE(*it, idx); + // idx == 4 + + it = it + 1; + idx = idx + 1; + QCOMPARE(*it, idx); + // idx == 5 + + it = it + ptrdiff_t(1); + idx = idx + 1; + QCOMPARE(*it, idx); + // idx == 6 + + it = it + qsizetype(1); + idx = idx + 1; + QCOMPARE(*it, idx); + // idx == 7 + + it = it - qsizetype(1); + idx = idx - 1; + QCOMPARE(*it, idx); + // idx == 6 + + it = it - ptrdiff_t(1); + idx = idx - 1; + QCOMPARE(*it, idx); + // idx == 5 + + it = it - 1; + idx = idx - 1; + QCOMPARE(*it, idx); + // idx == 4 + + it -= 1; + idx -= 1; + QCOMPARE(*it, idx); + // idx == 3 + + it -= qsizetype(1); + idx -= 1; + QCOMPARE(*it, idx); + // idx == 2 + + it -= ptrdiff_t(1); + idx -= 1; + QCOMPARE(*it, idx); + // idx == 1 + + it += ptrdiff_t(1); + idx += 1; + QCOMPARE(*it, idx); + // idx == 2 + + it += qsizetype(1); + idx += 1; + QCOMPARE(*it, idx); + // idx == 3 + + it += 1; + idx += 1; + QCOMPARE(*it, idx); + // idx == 4 + + // stl-style reverse iterators + idx = v.size() - 1; + auto rit = v.crbegin(); + QCOMPARE(*rit, idx); + + std::advance(rit, 5); + idx -= 5; + QCOMPARE(*rit, idx); + + ++rit; + --idx; + QCOMPARE(*rit, idx); + + rit++; + idx--; + QCOMPARE(*rit, idx); + + std::advance(rit, -4); + idx += 4; + QCOMPARE(*rit, idx); + + --rit; + ++idx; + QCOMPARE(*rit, idx); + + rit--; + idx++; + QCOMPARE(*rit, idx); +} + +void tst_QList::reverseIterators() const +{ + QList<int> v; + v << 1 << 2 << 3 << 4; + QList<int> vr = v; + std::reverse(vr.begin(), vr.end()); + const QList<int> &cvr = vr; + QVERIFY(std::equal(v.begin(), v.end(), vr.rbegin())); + QVERIFY(std::equal(v.begin(), v.end(), vr.crbegin())); + QVERIFY(std::equal(v.begin(), v.end(), cvr.rbegin())); + QVERIFY(std::equal(vr.rbegin(), vr.rend(), v.begin())); + QVERIFY(std::equal(vr.crbegin(), vr.crend(), v.begin())); + QVERIFY(std::equal(cvr.rbegin(), cvr.rend(), v.begin())); +} + +template<typename T> +void tst_QList::size() const +{ + TST_QLIST_CHECK_LEAKS(T) + + // also verify that length() is an alias to size() + + // zero size + QList<T> myvec; + QVERIFY(myvec.size() == 0); + QCOMPARE(myvec.size(), myvec.size()); + QVERIFY(!myvec.isDetached()); + + // grow + myvec.append(SimpleValue<T>::at(0)); + QVERIFY(myvec.size() == 1); + QCOMPARE(myvec.size(), myvec.size()); + myvec.append(SimpleValue<T>::at(1)); + QVERIFY(myvec.size() == 2); + QCOMPARE(myvec.size(), myvec.size()); + + // shrink + myvec.remove(0); + QVERIFY(myvec.size() == 1); + QCOMPARE(myvec.size(), myvec.size()); + myvec.remove(0); + QVERIFY(myvec.size() == 0); + QCOMPARE(myvec.size(), myvec.size()); +} + +// ::squeeze() is tested in ::capacity(). + +void tst_QList::startsWith() const +{ + QList<int> myvec; + + // empty vector + QVERIFY(!myvec.startsWith(1)); + + // add the one, should work + myvec.prepend(1); + QVERIFY(myvec.startsWith(1)); + + // add something else, fails now + myvec.prepend(3); + QVERIFY(!myvec.startsWith(1)); + + // remove it again :) + myvec.remove(0); + QVERIFY(myvec.startsWith(1)); +} + +template<typename T> +void tst_QList::swap() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> v1, v2; + T val1 = SimpleValue<T>::at(0); + T val2 = SimpleValue<T>::at(1); + T val3 = SimpleValue<T>::at(2); + T val4 = SimpleValue<T>::at(3); + T val5 = SimpleValue<T>::at(4); + T val6 = SimpleValue<T>::at(5); + v1 << val1 << val2 << val3; + v2 << val4 << val5 << val6; + + v1.swap(v2); + QCOMPARE(v1,QList<T>() << val4 << val5 << val6); + QCOMPARE(v2,QList<T>() << val1 << val2 << val3); +} + +void tst_QList::toList() const +{ + QList<QString> myvec; + myvec << "A" << "B" << "C"; + + // make sure it converts and doesn't modify the original vector + QCOMPARE(myvec.toList(), QList<QString>() << "A" << "B" << "C"); + QCOMPARE(myvec, QList<QString>() << "A" << "B" << "C"); +} + +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) +void tst_QList::toStdVector() const +{ + QList<QString> myvec; + myvec << "A" << "B" << "C"; + + std::vector<QString> svec = myvec.toStdVector(); + QCOMPARE(svec.at(0), QLatin1String("A")); + QCOMPARE(svec.at(1), QLatin1String("B")); + QCOMPARE(svec.at(2), QLatin1String("C")); + + QCOMPARE(myvec, QList<QString>() << "A" << "B" << "C"); +} +#endif + +void tst_QList::value() const +{ + QList<QString> myvec; + + QCOMPARE(myvec.value(1), QString()); + QCOMPARE(myvec.value(-1, QLatin1String("default")), QLatin1String("default")); + QVERIFY(!myvec.isDetached()); + + myvec << "A" << "B" << "C"; + + // valid calls + QCOMPARE(myvec.value(0), QLatin1String("A")); + QCOMPARE(myvec.value(1), QLatin1String("B")); + QCOMPARE(myvec.value(2), QLatin1String("C")); + + // default calls + QCOMPARE(myvec.value(-1), QString()); + QCOMPARE(myvec.value(3), QString()); + + // test calls with a provided default, valid calls + QCOMPARE(myvec.value(0, QLatin1String("default")), QLatin1String("A")); + QCOMPARE(myvec.value(1, QLatin1String("default")), QLatin1String("B")); + QCOMPARE(myvec.value(2, QLatin1String("default")), QLatin1String("C")); + + // test calls with a provided default that will return the default + QCOMPARE(myvec.value(-1, QLatin1String("default")), QLatin1String("default")); + QCOMPARE(myvec.value(3, QLatin1String("default")), QLatin1String("default")); +} + +void tst_QList::testOperators() const +{ + QList<QString> myvec; + myvec << "A" << "B" << "C"; + QList<QString> myvectwo; + myvectwo << "D" << "E" << "F"; + QList<QString> combined; + combined << "A" << "B" << "C" << "D" << "E" << "F"; + + // != + QVERIFY(myvec != myvectwo); + + // + + QCOMPARE(myvec + myvectwo, combined); + QCOMPARE(myvec, QList<QString>() << "A" << "B" << "C"); + QCOMPARE(myvectwo, QList<QString>() << "D" << "E" << "F"); + + // += + myvec += myvectwo; + QCOMPARE(myvec, combined); + + // == + QVERIFY(myvec == combined); + + // <, >, <=, >= + QVERIFY(!(myvec < combined)); + QVERIFY(!(myvec > combined)); + QVERIFY( myvec <= combined); + QVERIFY( myvec >= combined); + combined.push_back("G"); + QVERIFY( myvec < combined); + QVERIFY(!(myvec > combined)); + QVERIFY( myvec <= combined); + QVERIFY(!(myvec >= combined)); + QVERIFY(combined > myvec); + QVERIFY(combined >= myvec); + + // [] + QCOMPARE(myvec[0], QLatin1String("A")); + QCOMPARE(myvec[1], QLatin1String("B")); + QCOMPARE(myvec[2], QLatin1String("C")); + QCOMPARE(myvec[3], QLatin1String("D")); + QCOMPARE(myvec[4], QLatin1String("E")); + QCOMPARE(myvec[5], QLatin1String("F")); +} + + +int fooCtor; +int fooDtor; + +struct Foo +{ + int *p; + + Foo() { p = new int; ++fooCtor; } + Foo(const Foo &other) { Q_UNUSED(other); p = new int; ++fooCtor; } + + void operator=(const Foo & /* other */) { } + + ~Foo() { delete p; ++fooDtor; } +}; + +void tst_QList::reserve() +{ + fooCtor = 0; + fooDtor = 0; + { + QList<Foo> a; + a.resize(2); + QCOMPARE(fooCtor, 2); + QList<Foo> b(a); + b.reserve(1); + QCOMPARE(b.size(), a.size()); + QCOMPARE(fooDtor, 0); + } + QCOMPARE(fooCtor, fooDtor); +} + +// This is a regression test for QTBUG-51758 +void tst_QList::reserveZero() +{ + QList<int> vec; + vec.detach(); + vec.reserve(0); // should not crash + QCOMPARE(vec.size(), 0); + QCOMPARE(vec.capacity(), 0); + vec.squeeze(); + QCOMPARE(vec.size(), 0); + QCOMPARE(vec.capacity(), 0); + vec.reserve(-1); + QCOMPARE(vec.size(), 0); + QCOMPARE(vec.capacity(), 0); + vec.append(42); + QCOMPARE(vec.size(), 1); + QVERIFY(vec.capacity() >= 1); + + QList<int> vec2; + vec2.reserve(0); // should not crash either + vec2.reserve(-1); + vec2.squeeze(); + QCOMPARE(vec2.size(), 0); + QCOMPARE(vec2.capacity(), 0); + QVERIFY(!vec2.isDetached()); +} + +template<typename T> +void tst_QList::initializeList() +{ + TST_QLIST_CHECK_LEAKS(T) + + T val1(SimpleValue<T>::at(1)); + T val2(SimpleValue<T>::at(2)); + T val3(SimpleValue<T>::at(3)); + T val4(SimpleValue<T>::at(4)); + + QList<T> v1 {val1, val2, val3}; + QCOMPARE(v1, QList<T>() << val1 << val2 << val3); + QCOMPARE(v1, (QList<T> {val1, val2, val3})); + + QList<QList<T>> v2{ v1, {val4}, QList<T>(), {val1, val2, val3} }; + QList<QList<T>> v3; + v3 << v1 << (QList<T>() << val4) << QList<T>() << v1; + QCOMPARE(v3, v2); + + QList<T> v4({}); + QCOMPARE(v4.size(), 0); +} + +void tst_QList::const_shared_null() +{ + QList<int> v2; + QVERIFY(!v2.isDetached()); +} + +template<typename T> +void tst_QList::detach() const +{ + TST_QLIST_CHECK_LEAKS(T) + + { + // detach an empty vector + QList<T> v; + v.detach(); + QVERIFY(!v.isDetached()); + QCOMPARE(v.size(), 0); + QCOMPARE(v.capacity(), 0); + } + { + // detach an empty referenced vector + QList<T> v; + QList<T> ref(v); + QVERIFY(!v.isDetached()); + v.detach(); + QVERIFY(!v.isDetached()); + QCOMPARE(v.size(), 0); + QCOMPARE(v.capacity(), 0); + } + { + // detach a not empty referenced vector + QList<T> v(31); + QList<T> ref(v); + QVERIFY(!v.isDetached()); + v.detach(); + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 31); + QCOMPARE(v.capacity(), 31); + } + { + // detach a not empty vector + QList<T> v(31); + QVERIFY(v.isDetached()); + v.detach(); // detaching a detached vector + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 31); + QCOMPARE(v.capacity(), 31); + } + { + // detach a not empty vector with preallocated space + QList<T> v(3); + v.reserve(8); + QList<T> ref(v); + QVERIFY(!v.isDetached()); + v.detach(); + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 3); + QCOMPARE(v.capacity(), 8); + } + { + // detach a not empty vector with preallocated space + QList<T> v(3); + v.reserve(8); + QVERIFY(v.isDetached()); + v.detach(); // detaching a detached vector + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 3); + QCOMPARE(v.capacity(), 8); + } + { + // detach a not empty, initialized vector + QList<T> v(7, SimpleValue<T>::at(1)); + QList<T> ref(v); + QVERIFY(!v.isDetached()); + v.detach(); + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 7); + for (int i = 0; i < v.size(); ++i) + QCOMPARE(v[i], SimpleValue<T>::at(1)); + } + { + // detach a not empty, initialized vector + QList<T> v(7, SimpleValue<T>::at(2)); + QVERIFY(v.isDetached()); + v.detach(); // detaching a detached vector + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 7); + for (int i = 0; i < v.size(); ++i) + QCOMPARE(v[i], SimpleValue<T>::at(2)); + } + { + // detach a not empty, initialized vector with preallocated space + QList<T> v(7, SimpleValue<T>::at(3)); + v.reserve(31); + QList<T> ref(v); + QVERIFY(!v.isDetached()); + v.detach(); + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 7); + QCOMPARE(v.capacity(), 31); + for (int i = 0; i < v.size(); ++i) + QCOMPARE(v[i], SimpleValue<T>::at(3)); + } +} + +static QAtomicPointer<QList<int> > detachThreadSafetyDataInt; +static QAtomicPointer<QList<Movable> > detachThreadSafetyDataMovable; +static QAtomicPointer<QList<Custom> > detachThreadSafetyDataCustom; + +template<typename T> QAtomicPointer<QList<T> > *detachThreadSafetyData(); +template<> QAtomicPointer<QList<int> > *detachThreadSafetyData() { return &detachThreadSafetyDataInt; } +template<> QAtomicPointer<QList<Movable> > *detachThreadSafetyData() { return &detachThreadSafetyDataMovable; } +template<> QAtomicPointer<QList<Custom> > *detachThreadSafetyData() { return &detachThreadSafetyDataCustom; } + +static QSemaphore detachThreadSafetyLock; + +template<typename T> +void tst_QList::detachThreadSafety() const +{ + delete detachThreadSafetyData<T>()->fetchAndStoreOrdered(new QList<T>(SimpleValue<T>::vector(400))); + + static const uint threadsCount = 5; + + struct : QThread { + void run() override + { + QList<T> copy(*detachThreadSafetyData<T>()->loadRelaxed()); + QVERIFY(!copy.isDetached()); + detachThreadSafetyLock.release(); + detachThreadSafetyLock.acquire(100); + copy.detach(); + } + } threads[threadsCount]; + + for (uint i = 0; i < threadsCount; ++i) + threads[i].start(); + QThread::yieldCurrentThread(); + detachThreadSafetyLock.acquire(threadsCount); + + // destroy static original data + delete detachThreadSafetyData<T>()->fetchAndStoreOrdered(0); + + QVERIFY(threadsCount < 100); + detachThreadSafetyLock.release(threadsCount * 100); + QThread::yieldCurrentThread(); + + for (uint i = 0; i < threadsCount; ++i) + threads[i].wait(); +} + +void tst_QList::detachThreadSafetyInt() const +{ + for (uint i = 0; i < 128; ++i) + detachThreadSafety<int>(); +} + +void tst_QList::detachThreadSafetyMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + for (uint i = 0; i < 128; ++i) { + detachThreadSafety<Movable>(); + QCOMPARE(Movable::counter.loadAcquire(), instancesCount); + } +} + +void tst_QList::detachThreadSafetyCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + for (uint i = 0; i < 128; ++i) { + detachThreadSafety<Custom>(); + QCOMPARE(Custom::counter.loadAcquire(), instancesCount); + } +} + +void tst_QList::insertMove() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + { + QList<Movable> vec; + vec.reserve(7); + Movable m0; + Movable m1; + Movable m2; + Movable m3; + Movable m4; + Movable m5; + Movable m6; + + vec.append(std::move(m3)); + QVERIFY(m3.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m3)); + vec.push_back(std::move(m4)); + QVERIFY(m4.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m3)); + QVERIFY(vec.at(1).wasConstructedAt(&m4)); + vec.prepend(std::move(m1)); + QVERIFY(m1.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m3)); + QVERIFY(vec.at(2).wasConstructedAt(&m4)); + vec.insert(1, std::move(m2)); + QVERIFY(m2.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m2)); + QVERIFY(vec.at(2).wasConstructedAt(&m3)); + QVERIFY(vec.at(3).wasConstructedAt(&m4)); + vec += std::move(m5); + QVERIFY(m5.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m2)); + QVERIFY(vec.at(2).wasConstructedAt(&m3)); + QVERIFY(vec.at(3).wasConstructedAt(&m4)); + QVERIFY(vec.at(4).wasConstructedAt(&m5)); + vec << std::move(m6); + QVERIFY(m6.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m2)); + QVERIFY(vec.at(2).wasConstructedAt(&m3)); + QVERIFY(vec.at(3).wasConstructedAt(&m4)); + QVERIFY(vec.at(4).wasConstructedAt(&m5)); + QVERIFY(vec.at(5).wasConstructedAt(&m6)); + vec.push_front(std::move(m0)); + QVERIFY(m0.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m0)); + QVERIFY(vec.at(1).wasConstructedAt(&m1)); + QVERIFY(vec.at(2).wasConstructedAt(&m2)); + QVERIFY(vec.at(3).wasConstructedAt(&m3)); + QVERIFY(vec.at(4).wasConstructedAt(&m4)); + QVERIFY(vec.at(5).wasConstructedAt(&m5)); + QVERIFY(vec.at(6).wasConstructedAt(&m6)); + + QCOMPARE(Movable::counter.loadAcquire(), instancesCount + 14); + } + QCOMPARE(Movable::counter.loadAcquire(), instancesCount); +} + +void tst_QList::swapItemsAt() const +{ + QList<int> v; + v << 0 << 1 << 2 << 3; + + v.swapItemsAt(0, 2); + QCOMPARE(v.at(0), 2); + QCOMPARE(v.at(2), 0); + + auto copy = v; + copy.swapItemsAt(0, 2); + QCOMPARE(v.at(0), 2); + QCOMPARE(v.at(2), 0); + QCOMPARE(copy.at(0), 0); + QCOMPARE(copy.at(2), 2); +} + +void tst_QList::emplaceReturnsIterator() +{ + QList<Movable> vec; + + vec.emplace(0, 'k')->i = 'p'; + + QCOMPARE(vec[0].i, 'p'); +} + +void tst_QList::emplaceFront() const +{ + QAtomicScopedValueRollback rollback(Movable::counter, 0); + + QList<Movable> vec; + vec.emplaceFront('b'); + QCOMPARE(Movable::counter, 1); + + vec.emplaceFront('a'); + QCOMPARE(Movable::counter, 2); + + QCOMPARE(vec, QList<Movable>({ 'a', 'b' })); +} + +void tst_QList::emplaceFrontReturnsRef() const +{ + QList<Movable> vec; + + QCOMPARE(vec.emplaceFront('c').i, 'c'); + + vec.emplaceFront('b').i = 'a'; + + QCOMPARE(vec.front().i, 'a'); +} + +void tst_QList::emplaceBack() +{ + QAtomicScopedValueRollback rollback(Movable::counter, 0); + + QList<Movable> vec; + + vec.emplaceBack('k'); + + QCOMPARE(Movable::counter, 1); +} + +void tst_QList::emplaceBackReturnsRef() +{ + QList<Movable> vec; + + vec.emplaceBack('k').i = 'p'; + + QCOMPARE(vec.at(0).i, 'p'); +} + +void tst_QList::emplaceWithElementFromTheSameContainer() +{ + QFETCH(int, elementPos); + QFETCH(int, insertPos); + QFETCH(bool, doCopy); + + QList<QString> vec {"a", "b", "c", "d", "e"}; + const QString e = vec[elementPos]; + + if (doCopy) + vec.emplace(insertPos, vec[elementPos]); + else + vec.emplace(insertPos, std::move(vec[elementPos])); + + QCOMPARE(vec[insertPos], e); +} + +void tst_QList::emplaceWithElementFromTheSameContainer_data() +{ + QTest::addColumn<int>("elementPos"); + QTest::addColumn<int>("insertPos"); + QTest::addColumn<bool>("doCopy"); + + for (int i = 0; i < 2; ++i) { + const bool doCopy = i == 0; + const char *opName = doCopy ? "copy" : "move"; + + QTest::addRow("%s: begin -> end" , opName) << 0 << 5 << doCopy; + QTest::addRow("%s: begin -> middle", opName) << 0 << 2 << doCopy; + QTest::addRow("%s: middle -> begin" , opName) << 2 << 0 << doCopy; + QTest::addRow("%s: middle -> end" , opName) << 2 << 5 << doCopy; + QTest::addRow("%s: end -> middle", opName) << 4 << 2 << doCopy; + QTest::addRow("%s: end -> begin" , opName) << 4 << 0 << doCopy; + } +} + +template<typename T> +void tst_QList::emplaceImpl() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> vec {'a', 'b', 'c', 'd'}; + + vec.emplace(2, 'k'); + + QCOMPARE(vec.size(), 5); // emplace adds new element + QCOMPARE(vec[2], T('k')); + + vec.emplace(vec.end(), T('f')); + + QCOMPARE(vec.size(), 6); + QCOMPARE(vec.back(), T('f')); + + // emplace() into empty container + { + QList<T> vec; + vec.emplace(vec.begin(), 'a'); + QCOMPARE(vec.size(), 1); + QCOMPARE(vec.front(), T('a')); + } + { + QList<T> vec; + vec.emplace(0, 'a'); + QCOMPARE(vec.size(), 1); + QCOMPARE(vec.front(), T('a')); + } +} + +template <typename T> +void tst_QList::replace() const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> vec { 'a', 'b', 'c', 'd' }; + T e = 'e'; + vec.replace(0, e); + QCOMPARE(vec[0], T('e')); + + T f = 'f'; + vec.replace(2, std::move(f)); + QCOMPARE(vec[2], T('f')); +} + +template <class T> +static void vecEq(const QList<T> &qVec, const std::vector<T> &stdVec) +{ + QCOMPARE(std::size_t(qVec.size()), stdVec.size()); + QVERIFY(std::equal(qVec.begin(), qVec.end(), stdVec.begin(), stdVec.end())); +} + +template <class T> +static void squeezeVec(QList<T> &qVec, std::vector<T> &stdVec) +{ + qVec.squeeze(); + stdVec.shrink_to_fit(); +} + +template<typename T> +void tst_QList::emplaceConsistentWithStdVectorImpl() const +{ + TST_QLIST_CHECK_LEAKS(T) + + // fast-patch to make QString work with the old logic + const auto convert = [] (char i) { + if constexpr (std::is_same_v<QString, T>) { + return QChar(i); + } else { + return i; + } + }; + + QList<T> qVec {convert('a'), convert('b'), convert('c'), convert('d'), convert('e')}; + std::vector<T> stdVec {convert('a'), convert('b'), convert('c'), convert('d'), convert('e')}; + vecEq(qVec, stdVec); + + qVec.emplaceBack(convert('f')); + stdVec.emplace_back(convert('f')); + vecEq(qVec, stdVec); + + qVec.emplace(3, convert('g')); + stdVec.emplace(stdVec.begin() + 3, convert('g')); + vecEq(qVec, stdVec); + + T t; + // while QList is safe with regards to emplacing elements moved from itself, it's UB + // for std::vector, so do the moving in two steps there. + qVec.emplaceBack(std::move(qVec[0])); + stdVec.emplace_back(std::move(t = std::move(stdVec[0]))); + vecEq(qVec, stdVec); + + squeezeVec(qVec, stdVec); + + qVec.emplaceBack(std::move(qVec[1])); + stdVec.emplace_back(std::move(t = std::move(stdVec[1]))); + vecEq(qVec, stdVec); + + squeezeVec(qVec, stdVec); + + qVec.emplace(3, std::move(qVec[5])); + stdVec.emplace(stdVec.begin() + 3, std::move(t = std::move(stdVec[5]))); + + vecEq(qVec, stdVec); + + qVec.emplaceBack(qVec[3]); + stdVec.emplace_back((t = stdVec[3])); + vecEq(qVec, stdVec); + + squeezeVec(qVec, stdVec); + + qVec.emplaceBack(qVec[4]); + stdVec.emplace_back((t = stdVec[4])); + vecEq(qVec, stdVec); + + squeezeVec(qVec, stdVec); + + qVec.emplace(5, qVec[7]); + stdVec.emplace(stdVec.begin() + 5, (t = stdVec[7])); + vecEq(qVec, stdVec); +} + +void tst_QList::fromReadOnlyData() const +{ + { + QVector<char> d = QVector<char>::fromReadOnlyData("ABCDEFGHIJ"); + QCOMPARE(d.capacity(), 0); + d.squeeze(); + QCOMPARE(d.capacity(), 0); + QCOMPARE(d.size(), 10u + 1u); + for (int i = 0; i < 10; ++i) + QCOMPARE(d.data()[i], char('A' + i)); + } + + { + // wchar_t is not necessarily 2-bytes + QVector<wchar_t> d = QVector<wchar_t>::fromReadOnlyData(L"ABCDEFGHIJ"); + QCOMPARE(d.size(), 10u + 1u); + for (int i = 0; i < 10; ++i) + QCOMPARE(d.data()[i], wchar_t('A' + i)); + QVERIFY(d.isDetached()); + } + + { + const char data[] = "ABCDEFGHIJ"; + const QVector<char> v = QVector<char>::fromReadOnlyData(data); + + QVERIFY(v.constData() == data); + QVERIFY(!v.isEmpty()); + QCOMPARE(v.size(), qsizetype(11)); + // v.capacity() is unspecified, for now + + QCOMPARE((void*)(v.constBegin() + v.size()).operator->(), (void*)v.constEnd().operator->()); + + for (int i = 0; i < 10; ++i) + QCOMPARE(v[i], char('A' + i)); + QCOMPARE(v[10], char('\0')); + } + + { + struct LiteralType { + int value; + constexpr LiteralType(int v = 0) : value(v) {} + }; + const LiteralType literal[] = {LiteralType(0), LiteralType(1), LiteralType(2)}; + + const QVector<LiteralType> d = QVector<LiteralType>::fromReadOnlyData(literal); + QCOMPARE(d.size(), 3); + for (int i = 0; i < 3; ++i) + QCOMPARE(d.data()[i].value, i); + } +} + +struct alignas(8) CustomAligned +{ + qint64 v = 0; + CustomAligned() = default; + CustomAligned(qint64 i) : v(i) { } + friend bool operator==(const CustomAligned &x, const CustomAligned &y) { return x.v == y.v; } +}; + +void tst_QList::reallocateCustomAlignedType_qtbug90359() const +{ + // Note: a very special test that could only fail for specific alignments + constexpr bool canFail = (alignof(QArrayData) == 4) && (sizeof(QArrayData) == 12); + if constexpr (!canFail) + qWarning() << "This test will always succeed on this system."; + if constexpr (alignof(CustomAligned) > alignof(std::max_align_t)) + QSKIP("The codepaths tested here wouldn't be executed."); + + const QList<CustomAligned> expected({ 0, 1, 2, 3, 4, 5, 6 }); + QList<CustomAligned> actual; + for (int i = 0; i < 7; ++i) { + actual.append(i); + QCOMPARE(actual.at(i), i); + } + QCOMPARE(actual, expected); +} + +template<typename T, typename Reinsert> +void tst_QList::reinsert(Reinsert op) const +{ + TST_QLIST_CHECK_LEAKS(T) + + QList<T> list(1); + // this constant is big enough for the QList to stop reallocating, after + // all, size is always less than 3 + const int maxIters = 128; + for (int i = 0; i < maxIters; ++i) { + op(list); + } + + // if QList continues to grow, it's an error + qsizetype capacity = list.capacity(); + for (int i = 0, enoughIters = int(capacity) * 2; i < enoughIters; ++i) { + op(list); + QCOMPARE(capacity, list.capacity()); + } +} + +template<typename T> +void tst_QList::stability_reserve() const +{ + TST_QLIST_CHECK_LEAKS(T) + + // NOTE: this test verifies that QList::constData() stays unchanged when + // inserting as much as requested by the reserve. This is specifically + // designed this way as in cases when QTypeInfo<T>::isRelocatable returns + // true, reallocation might use fast ::realloc() path which may in theory + // (and, actually, in practice) just expand the current memory area and thus + // keep QList::constData() unchanged, which means checks like + // QVERIFY(oldConstData != vec.constData()) are flaky. When + // QTypeInfo<T>::isRelocatable returns false, constData() will always change + // if a reallocation happens and this will fail the test. This should be + // sufficient on its own to test the stability requirements. + + { + QList<T> vec; + vec.reserve(64); + const T *ptr = vec.constData(); + vec.append(QList<T>(64)); + QCOMPARE(ptr, vec.constData()); + } + + { + QList<T> vec; + vec.prepend(SimpleValue<T>::at(0)); + vec.removeFirst(); + vec.reserve(64); + const T *ptr = vec.constData(); + vec.append(QList<T>(64)); + QCOMPARE(ptr, vec.constData()); + } + + { + QList<T> vec; + const T *ptr = vec.constData(); + vec.reserve(vec.capacity()); + QCOMPARE(ptr, vec.constData()); + vec.append(QList<T>(vec.capacity())); + QCOMPARE(ptr, vec.constData()); + } + + { + QList<T> vec; + vec.prepend(SimpleValue<T>::at(0)); + vec.removeFirst(); + vec.reserve(vec.capacity()); + const T *ptr = vec.constData(); + vec.append(QList<T>(vec.capacity())); + QCOMPARE(ptr, vec.constData()); + } + + { + QList<T> vec; + vec.append(SimpleValue<T>::at(0)); + vec.reserve(64); + const T *ptr = vec.constData(); + vec.append(QList<T>(64 - vec.size())); // 1 element is already in the container + QCOMPARE(ptr, vec.constData()); + QCOMPARE(vec.size(), 64); + QCOMPARE(vec.capacity(), 64); + const qsizetype oldCapacity = vec.capacity(); + vec.append(SimpleValue<T>::at(1)); // will reallocate as this exceeds 64 + QVERIFY(oldCapacity < vec.capacity()); + } + + { + QList<T> vec; + vec.prepend(SimpleValue<T>::at(0)); + vec.reserve(64); + const T *ptr = vec.constData(); + vec.append(QList<T>(64 - vec.size())); // 1 element is already in the container + QCOMPARE(ptr, vec.constData()); + QCOMPARE(vec.size(), 64); + QCOMPARE(vec.capacity(), 64); + const qsizetype oldCapacity = vec.capacity(); + vec.append(SimpleValue<T>::at(1)); // will reallocate as this exceeds 64 + QVERIFY(oldCapacity < vec.capacity()); + } +} + +template<typename T> +void tst_QList::stability_erase() const +{ + TST_QLIST_CHECK_LEAKS(T) + + // invalidated: [pos, end()) + for (int pos = 1; pos < 10; ++pos) { + QList<T> v(10); + int k = 0; + std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); }); + const auto ptr = v.constData(); + + auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.begin() + pos); + + v.remove(pos, 1); + QVERIFY(ptr == v.constData()); + for (int i = 0; i < copy.size(); ++i) + QCOMPARE(*reference[i], copy[i]); + } + + // 0 is a special case, because all values get invalidated + { + QList<T> v(10); + const auto ptr = v.constData(); + v.remove(0, 2); + QVERIFY(ptr != v.constData()); // can do fast removal from begin() + } + + // when erasing everything, leave the data pointer in place (not strictly + // required, but this makes more sense in general) + { + QList<T> v(10); + const auto ptr = v.constData(); + v.remove(0, v.size()); + QVERIFY(ptr == v.constData()); + } +} + +template<typename T> +void tst_QList::stability_append() const +{ + TST_QLIST_CHECK_LEAKS(T) + + { + QList<T> v(10); + int k = 0; + std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); }); + QList<T> src(1, SimpleValue<T>::at(0)); + v.append(src.begin(), src.end()); + QCOMPARE_LE(v.size(), v.capacity()); + + for (int i = 0; i < v.capacity() - v.size(); ++i) { + auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end()); + v.append(SimpleValue<T>::at(i)); + for (int i = 0; i < copy.size(); ++i) + QCOMPARE(*reference[i], copy[i]); + } + } + + { + QList<T> v; + v.reserve(10); + const qsizetype capacity = v.capacity(); + const T *ptr = v.constData(); + v.prepend(SimpleValue<T>::at(0)); + // here we abuse the internal details of QList. since there's enough + // free space, QList should've only rearranged the data in memory, + // without reallocating. + QCOMPARE(capacity, v.capacity()); // otherwise cannot rely on ptr + const qsizetype freeSpaceAtBegin = v.constData() - ptr; + const qsizetype freeSpaceAtEnd = v.capacity() - v.size() - freeSpaceAtBegin; + QVERIFY(freeSpaceAtEnd > 0); // otherwise this test is useless + QVERIFY(v.size() + freeSpaceAtBegin + freeSpaceAtEnd == v.capacity()); + + for (int i = 0; i < freeSpaceAtEnd; ++i) { + auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end()); + QList<T> src(1, SimpleValue<T>::at(i)); + v.append(src.begin(), src.end()); + for (int i = 0; i < copy.size(); ++i) + QCOMPARE(*reference[i], copy[i]); + } + } +} + +template<typename T, typename Insert> +void tst_QList::stability_insert(Insert op) const +{ + TST_QLIST_CHECK_LEAKS(T) + + // invalidated: [pos, end()) + for (int pos = 1; pos <= 10; ++pos) { + QList<T> v(10); + int k = 0; + std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); }); + v.append(SimpleValue<T>::at(0)); // causes growth + v.removeLast(); + QCOMPARE(v.size(), 10); + QVERIFY(v.size() < v.capacity()); + + auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.begin() + pos); + op(v, pos, SimpleValue<T>::at(0)); + for (int i = 0; i < pos; ++i) + QCOMPARE(*reference[i], copy[i]); + } + + for (int pos = 1; pos <= 10; ++pos) { + QList<T> v(10); + int k = 0; + std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); }); + v.prepend(SimpleValue<T>::at(0)); // causes growth and free space at begin > 0 + v.removeFirst(); + QCOMPARE(v.size(), 10); + QVERIFY(v.size() < v.capacity()); + + auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.begin() + pos); + op(v, pos, SimpleValue<T>::at(0)); + for (int i = 0; i < pos; ++i) + QCOMPARE(*reference[i], copy[i]); + } +} + +template<typename T> +void tst_QList::stability_resize() const +{ + TST_QLIST_CHECK_LEAKS(T) + + { + QList<T> v(10); + v.reserve(15); + QVERIFY(v.size() < v.capacity()); + int k = 0; + std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); }); + auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end()); + + v.resize(15); + for (int i = 0; i < copy.size(); ++i) + QCOMPARE(*reference[i], copy[i]); + } + + { + QList<T> v(10); + int k = 0; + std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); }); + auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end()); + + v.resize(10); + for (int i = 0; i < 10; ++i) + QCOMPARE(*reference[i], copy[i]); + } + + { + QList<T> v(10); + int k = 0; + std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); }); + auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end()); + + v.resize(5); + for (int i = 0; i < 5; ++i) + QCOMPARE(*reference[i], copy[i]); + } + + // special case due to prepend: + { + QList<T> v; + v.reserve(20); + const qsizetype capacity = v.capacity(); + const T *ptr = v.constData(); + v.prepend(SimpleValue<T>::at(0)); // now there's free space at begin + v.resize(10); + QVERIFY(v.size() < v.capacity()); + // here we abuse the internal details of QList. since there's enough + // free space, QList should've only rearranged the data in memory, + // without reallocating. + QCOMPARE(capacity, v.capacity()); // otherwise cannot rely on ptr + const qsizetype freeSpaceAtBegin = v.constData() - ptr; + const qsizetype freeSpaceAtEnd = v.capacity() - v.size() - freeSpaceAtBegin; + QVERIFY(freeSpaceAtEnd > 0); // otherwise this test is useless + QVERIFY(v.size() + freeSpaceAtBegin + freeSpaceAtEnd == v.capacity()); + int k = 0; + std::generate(v.begin(), v.end(), [&k]() { return SimpleValue<T>::at(k++); }); + auto [copy, reference] = qlistCopyAndReferenceFromRange(v.begin(), v.end()); + + v.resize(v.size() + freeSpaceAtEnd); + for (int i = 0; i < copy.size(); ++i) + QCOMPARE(*reference[i], copy[i]); + } +} + +QTEST_MAIN(tst_QList) +#include "tst_qlist.moc" diff --git a/tests/auto/corelib/tools/qmacautoreleasepool/CMakeLists.txt b/tests/auto/corelib/tools/qmacautoreleasepool/CMakeLists.txt new file mode 100644 index 0000000000..b968945ac6 --- /dev/null +++ b/tests/auto/corelib/tools/qmacautoreleasepool/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qmacautoreleasepool Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmacautoreleasepool LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qmacautoreleasepool + SOURCES + tst_qmacautoreleasepool.mm + LIBRARIES + Qt::CorePrivate + ${FWFoundation} +) diff --git a/tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro b/tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro deleted file mode 100644 index 8599596344..0000000000 --- a/tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qmacautoreleasepool -QT = core testlib -SOURCES = tst_qmacautoreleasepool.mm -LIBS += -framework Foundation diff --git a/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm b/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm index 8f1069f419..e7923b47f3 100644 --- a/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm +++ b/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm @@ -1,32 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include <QtTest/QtTest> +#include <QTest> + +#include <QtCore/private/qcore_mac_p.h> #include <Foundation/Foundation.h> @@ -37,7 +14,6 @@ private slots: void noPool(); void rootLevelPool(); void stackAllocatedPool(); - void heapAllocatedPool(); }; static id lastDeallocedObject = nil; @@ -86,26 +62,6 @@ void tst_QMacAutoreleasePool::stackAllocatedPool() [pool drain]; } -void tst_QMacAutoreleasePool::heapAllocatedPool() -{ - // The special case, a pool allocated on the heap, or as a member of a - // heap allocated object. This is not a supported use of QMacAutoReleasePool, - // and will result in warnings if the pool is prematurely drained. - - NSObject *allocedObject = nil; - { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - QMacAutoReleasePool *qtPool = nullptr; - { - qtPool = new QMacAutoReleasePool; - allocedObject = [[[DeallocTracker alloc] init] autorelease]; - } - [pool drain]; - delete qtPool; - } - QCOMPARE(lastDeallocedObject, allocedObject); -} - QTEST_APPLESS_MAIN(tst_QMacAutoreleasePool) #include "tst_qmacautoreleasepool.moc" diff --git a/tests/auto/corelib/tools/qmakearray/CMakeLists.txt b/tests/auto/corelib/tools/qmakearray/CMakeLists.txt new file mode 100644 index 0000000000..cec589628f --- /dev/null +++ b/tests/auto/corelib/tools/qmakearray/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qmakearray Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmakearray LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qmakearray + SOURCES + tst_qmakearray.cpp + LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/tools/qmakearray/qmakearray.pro b/tests/auto/corelib/tools/qmakearray/qmakearray.pro deleted file mode 100644 index abb3d9fdbc..0000000000 --- a/tests/auto/corelib/tools/qmakearray/qmakearray.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qmakearray -QT = core testlib core-private -SOURCES = $$PWD/tst_qmakearray.cpp diff --git a/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp b/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp index e0d3f52719..1d796452b0 100644 --- a/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp +++ b/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include <QtTest/QtTest> +#include <QTest> #include <private/qmakearray_p.h> diff --git a/tests/auto/corelib/tools/qmap/CMakeLists.txt b/tests/auto/corelib/tools/qmap/CMakeLists.txt new file mode 100644 index 0000000000..bddf9267f8 --- /dev/null +++ b/tests/auto/corelib/tools/qmap/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qmap Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmap LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qmap + SOURCES + tst_qmap.cpp +) + +qt_internal_undefine_global_definition(tst_qmap QT_NO_JAVA_STYLE_ITERATORS) diff --git a/tests/auto/corelib/tools/qmap/qmap.pro b/tests/auto/corelib/tools/qmap/qmap.pro deleted file mode 100644 index 2cc772720d..0000000000 --- a/tests/auto/corelib/tools/qmap/qmap.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qmap -QT = core testlib -SOURCES = $$PWD/tst_qmap.cpp - -DEFINES -= QT_NO_JAVA_STYLE_ITERATORS diff --git a/tests/auto/corelib/tools/qmap/tst_qmap.cpp b/tests/auto/corelib/tools/qmap/tst_qmap.cpp index 85733639b8..6950dcf705 100644 --- a/tests/auto/corelib/tools/qmap/tst_qmap.cpp +++ b/tests/auto/corelib/tools/qmap/tst_qmap.cpp @@ -1,42 +1,22 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qmap.h> -#include <QtTest/QtTest> +#include <QTest> + #include <QDebug> +#include <QScopeGuard> + +using namespace Qt::StringLiterals; +QT_WARNING_DISABLE_DEPRECATED class tst_QMap : public QObject { Q_OBJECT protected: - template <class KEY, class VALUE> - void sanityCheckTree(const QMap<KEY, VALUE> &m, int calledFromLine); + template <class Map> + void sanityCheckTree(const Map &m, int calledFromLine); public slots: void init(); private slots: @@ -46,6 +26,7 @@ private slots: void beginEnd(); void firstLast(); void key(); + void value(); void swap(); @@ -60,8 +41,13 @@ private slots: void take(); void iterators(); + void multimapIterators(); + void iteratorsInEmptyMap(); void keyIterator(); + void multimapKeyIterator(); void keyValueIterator(); + void multimapKeyValueIterator(); + void keyValueIteratorInEmptyMap(); void keys_values_uniqueKeys(); void qmultimap_specific(); @@ -70,11 +56,23 @@ private slots: void equal_range(); void insert(); + void insertMap(); void checkMostLeftNode(); void initializerList(); void testInsertWithHint(); void testInsertMultiWithHint(); void eraseValidIteratorOnSharedMap(); + void removeElementsInMap(); + void toStdMap(); + + void multiMapStoresInReverseInsertionOrder(); + + // Tests for deprecated APIs. +#if QT_DEPRECATED_SINCE(6, 0) + void deprecatedInsertMulti(); + void deprecatedIteratorApis(); + void deprecatedInsert(); +#endif // QT_DEPRECATED_SINCE(6, 0) }; struct IdentityTracker { @@ -111,21 +109,22 @@ public: int MyClass::count = 0; typedef QMap<QString, MyClass> MyMap; +typedef QMultiMap<QString, MyClass> MyMultiMap; QDebug operator << (QDebug d, const MyClass &c) { d << c.str; return d; } -template <class KEY, class VALUE> -void tst_QMap::sanityCheckTree(const QMap<KEY, VALUE> &m, int calledFromLine) +template <class Map> +void tst_QMap::sanityCheckTree(const Map &m, int calledFromLine) { QString possibleFrom; possibleFrom.setNum(calledFromLine); possibleFrom = "Called from line: " + possibleFrom; int count = 0; - typename QMap<KEY, VALUE>::const_iterator oldite = m.constBegin(); - for (typename QMap<KEY, VALUE>::const_iterator i = m.constBegin(); i != m.constEnd(); ++i) { + typename Map::const_iterator oldite = m.constBegin(); + for (typename Map::const_iterator i = m.constBegin(); i != m.constEnd(); ++i) { count++; bool oldIteratorIsLarger = i.key() < oldite.key(); QVERIFY2(!oldIteratorIsLarger, possibleFrom.toUtf8()); @@ -173,13 +172,21 @@ void tst_QMap::count() { MyMap map; MyMap map2( map ); - QCOMPARE( map.count(), 0 ); - QCOMPARE( map2.count(), 0 ); + QCOMPARE( map.size(), 0 ); + QCOMPARE( map2.size(), 0 ); QCOMPARE( MyClass::count, int(0) ); + QCOMPARE(map.count("key"), 0); + QCOMPARE(map.size(), 0); + QCOMPARE(map2.size(), 0); + QVERIFY(!map.isDetached()); + QVERIFY(!map2.isDetached()); // detach map2["Hallo"] = MyClass( "Fritz" ); - QCOMPARE( map.count(), 0 ); - QCOMPARE( map2.count(), 1 ); + QCOMPARE( map.size(), 0 ); + QCOMPARE( map.size(), 0 ); + QCOMPARE( map2.size(), 1 ); + QCOMPARE( map2.size(), 1 ); + QVERIFY(!map.isDetached()); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 1 ); #endif @@ -189,11 +196,11 @@ void tst_QMap::count() { typedef QMap<QString, MyClass> Map; Map map; - QCOMPARE( map.count(), 0); + QCOMPARE( map.size(), 0); map.insert( "Torben", MyClass("Weis") ); - QCOMPARE( map.count(), 1 ); + QCOMPARE( map.size(), 1 ); map.insert( "Claudia", MyClass("Sorg") ); - QCOMPARE( map.count(), 2 ); + QCOMPARE( map.size(), 2 ); map.insert( "Lars", MyClass("Linzbach") ); map.insert( "Matthias", MyClass("Ettrich") ); map.insert( "Sue", MyClass("Paludo") ); @@ -201,7 +208,7 @@ void tst_QMap::count() map.insert( "Haavard", MyClass("Nord") ); map.insert( "Arnt", MyClass("Gulbrandsen") ); map.insert( "Paul", MyClass("Tvete") ); - QCOMPARE( map.count(), 9 ); + QCOMPARE( map.size(), 9 ); map.insert( "Paul", MyClass("Tvete 1") ); map.insert( "Paul", MyClass("Tvete 2") ); map.insert( "Paul", MyClass("Tvete 3") ); @@ -209,69 +216,69 @@ void tst_QMap::count() map.insert( "Paul", MyClass("Tvete 5") ); map.insert( "Paul", MyClass("Tvete 6") ); - QCOMPARE( map.count(), 9 ); + QCOMPARE( map.size(), 9 ); QCOMPARE( map.count("Paul"), 1 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif Map map2( map ); - QVERIFY( map2.count() == 9 ); + QVERIFY( map2.size() == 9 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif map2.insert( "Kay", MyClass("Roemer") ); - QVERIFY( map2.count() == 10 ); - QVERIFY( map.count() == 9 ); + QVERIFY( map2.size() == 10 ); + QVERIFY( map.size() == 9 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 19 ); #endif map2 = map; - QVERIFY( map.count() == 9 ); - QVERIFY( map2.count() == 9 ); + QVERIFY( map.size() == 9 ); + QVERIFY( map2.size() == 9 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif map2.insert( "Kay", MyClass("Roemer") ); - QVERIFY( map2.count() == 10 ); + QVERIFY( map2.size() == 10 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 19 ); #endif map2.clear(); - QVERIFY( map.count() == 9 ); - QVERIFY( map2.count() == 0 ); + QVERIFY( map.size() == 9 ); + QVERIFY( map2.size() == 0 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif map2 = map; - QVERIFY( map.count() == 9 ); - QVERIFY( map2.count() == 9 ); + QVERIFY( map.size() == 9 ); + QVERIFY( map2.size() == 9 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif map2.clear(); - QVERIFY( map.count() == 9 ); - QVERIFY( map2.count() == 0 ); + QVERIFY( map.size() == 9 ); + QVERIFY( map2.size() == 0 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif map.remove( "Lars" ); - QVERIFY( map.count() == 8 ); - QVERIFY( map2.count() == 0 ); + QVERIFY( map.size() == 8 ); + QVERIFY( map2.size() == 0 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 8 ); #endif map.remove( "Mist" ); - QVERIFY( map.count() == 8 ); - QVERIFY( map2.count() == 0 ); + QVERIFY( map.size() == 8 ); + QVERIFY( map2.size() == 0 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 8 ); #endif @@ -285,22 +292,22 @@ void tst_QMap::count() #ifndef Q_CC_SUN QVERIFY( MyClass::count == 1 ); #endif - QVERIFY( map.count() == 1 ); + QVERIFY( map.size() == 1 ); (void)map["Torben"].str; (void)map["Lars"].str; #ifndef Q_CC_SUN QVERIFY( MyClass::count == 2 ); #endif - QVERIFY( map.count() == 2 ); + QVERIFY( map.size() == 2 ); const Map& cmap = map; (void)cmap["Depp"].str; #ifndef Q_CC_SUN QVERIFY( MyClass::count == 2 ); #endif - QVERIFY( map.count() == 2 ); - QVERIFY( cmap.count() == 2 ); + QVERIFY( map.size() == 2 ); + QVERIFY( cmap.size() == 2 ); } QCOMPARE( MyClass::count, 0 ); { @@ -313,6 +320,45 @@ void tst_QMap::count() QCOMPARE( MyClass::count, 0 ); } QCOMPARE( MyClass::count, 0 ); + + { + QMultiMap<int, MyClass> map; + QMultiMap<int, MyClass> map2(map); + QCOMPARE(map.size(), 0); + QCOMPARE(map2.size(), 0); + QCOMPARE(MyClass::count, 0); + QCOMPARE(map.count(1), 0); + QCOMPARE(map.size(), 0); + QCOMPARE(map2.size(), 0); + QVERIFY(!map.isDetached()); + QVERIFY(!map2.isDetached()); + + // detach + map2.insert(0, MyClass("value0")); + QCOMPARE(map.size(), 0); + QCOMPARE(map.size(), 0); + QCOMPARE(map2.size(), 1); + QCOMPARE(map2.size(), 1); + QVERIFY(!map.isDetached()); + QCOMPARE(MyClass::count, 1); + + map2.insert(1, MyClass("value1")); + map2.insert(2, MyClass("value2")); + QCOMPARE(map2.size(), 3); + QCOMPARE(MyClass::count, 3); + + map2.insert(0, MyClass("value0_1")); + map2.insert(0, MyClass("value0_2")); + QCOMPARE(map2.size(), 5); + QCOMPARE(map2.count(0), 3); + QCOMPARE(MyClass::count, 5); + + map2.clear(); + QCOMPARE(map2.size(), 0); + QCOMPARE(MyClass::count, 0); + + } + QCOMPARE(MyClass::count, 0); } void tst_QMap::clear() @@ -320,18 +366,40 @@ void tst_QMap::clear() { MyMap map; map.clear(); - QVERIFY( map.isEmpty() ); + QVERIFY(map.isEmpty()); + QVERIFY(!map.isDetached()); map.insert( "key", MyClass( "value" ) ); + QVERIFY(!map.isEmpty()); map.clear(); - QVERIFY( map.isEmpty() ); + QVERIFY(map.isEmpty()); map.insert( "key0", MyClass( "value0" ) ); map.insert( "key0", MyClass( "value1" ) ); map.insert( "key1", MyClass( "value2" ) ); + QVERIFY(!map.isEmpty()); map.clear(); sanityCheckTree(map, __LINE__); - QVERIFY( map.isEmpty() ); + QVERIFY(map.isEmpty()); } - QCOMPARE( MyClass::count, int(0) ); + QCOMPARE(MyClass::count, int(0)); + + { + MyMultiMap map; + map.clear(); + QVERIFY(map.isEmpty()); + QVERIFY(!map.isDetached()); + map.insert( "key", MyClass( "value" ) ); + QVERIFY(!map.isEmpty()); + map.clear(); + QVERIFY(map.isEmpty()); + map.insert( "key0", MyClass( "value0" ) ); + map.insert( "key0", MyClass( "value1" ) ); + map.insert( "key1", MyClass( "value2" ) ); + QVERIFY(!map.isEmpty()); + map.clear(); + sanityCheckTree(map, __LINE__); + QVERIFY(map.isEmpty()); + } + QCOMPARE(MyClass::count, int(0)); } void tst_QMap::beginEnd() @@ -367,7 +435,12 @@ void tst_QMap::beginEnd() // detach map2.insert( "2", "c" ); QVERIFY( map.constBegin() == map.constBegin() ); - QVERIFY( map.constBegin() != map2.constBegin() ); + + // comparing iterators between two different std::map is UB (and raises an + // assertion failure with MSVC debug-mode iterators), so we compare the + // elements' addresses. + QVERIFY(&map.constBegin().key() != &map2.constBegin().key()); + QVERIFY(&map.constBegin().value() != &map2.constBegin().value()); } void tst_QMap::firstLast() @@ -406,6 +479,7 @@ void tst_QMap::key() QMap<QString, int> map1; QCOMPARE(map1.key(1), QString()); QCOMPARE(map1.key(1, def), def); + QVERIFY(!map1.isDetached()); map1.insert("one", 1); QCOMPARE(map1.key(1), QLatin1String("one")); @@ -436,6 +510,7 @@ void tst_QMap::key() QMap<int, QString> map2; QCOMPARE(map2.key("one"), 0); QCOMPARE(map2.key("one", def), def); + QVERIFY(!map2.isDetached()); map2.insert(1, "one"); QCOMPARE(map2.key("one"), 1); @@ -467,6 +542,82 @@ void tst_QMap::key() QCOMPARE(map2.key("zero"), 0); QCOMPARE(map2.key("zero", def), 0); } + + { + int def = -1; + QMultiMap<int, QString> multiMap; + QCOMPARE(multiMap.key("value0"), 0); + QCOMPARE(multiMap.key("value0", def), def); + QVERIFY(!multiMap.isDetached()); + + multiMap.insert(1, "value1"); + multiMap.insert(2, "value2"); + multiMap.insert(1, "value1_1"); + + QCOMPARE(multiMap.key("value1"), 1); + QCOMPARE(multiMap.key("value1", def), 1); + QCOMPARE(multiMap.key("value1_1"), 1); + QCOMPARE(multiMap.key("value2"), 2); + QCOMPARE(multiMap.key("value3"), 0); + QCOMPARE(multiMap.key("value3", def), def); + } +} + +void tst_QMap::value() +{ + const QString def = "default value"; + { + QMap<int, QString> map; + QCOMPARE(map.value(1), QString()); + QCOMPARE(map.value(1, def), def); + QVERIFY(!map.isDetached()); + + map.insert(1, "value1"); + QCOMPARE(map.value(1), "value1"); + QCOMPARE(map[1], "value1"); + QCOMPARE(map.value(2), QString()); + QCOMPARE(map.value(2, def), def); + QCOMPARE(map[2], QString()); + QCOMPARE(map.size(), 2); + + map.insert(2, "value2"); + QCOMPARE(map.value(2), "value2"); + QCOMPARE(map[2], "value2"); + + map.insert(1, "value3"); + QCOMPARE(map.value(1), "value3"); + QCOMPARE(map.value(1, def), "value3"); + QCOMPARE(map[1], "value3"); + + const QMap<int, QString> constMap; + QVERIFY(!constMap.isDetached()); + QCOMPARE(constMap.value(1, def), def); + QCOMPARE(constMap[1], QString()); + QCOMPARE(constMap.size(), 0); + QVERIFY(!constMap.isDetached()); + } + { + QMultiMap<int, QString> map; + QCOMPARE(map.value(1), QString()); + QCOMPARE(map.value(1, def), def); + QVERIFY(!map.isDetached()); + + map.insert(1, "value1"); + QCOMPARE(map.value(1), "value1"); + QCOMPARE(map.value(2), QString()); + QCOMPARE(map.value(2, def), def); + + map.insert(2, "value2"); + QCOMPARE(map.value(2), "value2"); + + map.insert(1, "value3"); + // If multiple values exist, the most recently added is returned. + QCOMPARE(map.value(1), "value3"); + QCOMPARE(map.value(1, def), "value3"); + + map.remove(1, "value3"); + QCOMPARE(map.value(1), "value1"); + } } void tst_QMap::swap() @@ -489,16 +640,19 @@ void tst_QMap::operator_eq() QMap<int, int> b; QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); a.insert(1,1); b.insert(1,1); QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); a.insert(0,1); b.insert(0,1); QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); // compare for inequality: @@ -521,6 +675,7 @@ void tst_QMap::operator_eq() QMap<QString, QString> b; QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); a.insert("Hello", "World"); @@ -529,6 +684,7 @@ void tst_QMap::operator_eq() b.insert("Hello", "World"); QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); a.insert("Goodbye", "cruel world"); @@ -545,6 +701,7 @@ void tst_QMap::operator_eq() // empty keys and null keys match: b.insert(QString(""), QString()); QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); } @@ -559,64 +716,135 @@ void tst_QMap::operator_eq() } } -void tst_QMap::empty() +template <typename T> +void emptyTestMethod() { - QMap<int, QString> map1; + T map; - QVERIFY(map1.isEmpty()); + QVERIFY(map.isEmpty()); + QVERIFY(map.empty()); + QVERIFY(!map.isDetached()); - map1.insert(1, "one"); - QVERIFY(!map1.isEmpty()); + map.insert(1, "one"); + QVERIFY(!map.isEmpty()); + QVERIFY(!map.empty()); - map1.clear(); - QVERIFY(map1.isEmpty()); + map.clear(); + QVERIFY(map.isEmpty()); + QVERIFY(map.empty()); +} +void tst_QMap::empty() +{ + emptyTestMethod<QMap<int, QString>>(); + if (QTest::currentTestFailed()) + return; + + emptyTestMethod<QMultiMap<int, QString>>(); } void tst_QMap::contains() { - QMap<int, QString> map1; - int i; + { + QMap<int, QString> map1; + int i; - map1.insert(1, "one"); - QVERIFY(map1.contains(1)); + QVERIFY(!map1.contains(1)); + QVERIFY(!map1.isDetached()); - for(i=2; i < 100; ++i) - map1.insert(i, "teststring"); - for(i=99; i > 1; --i) - QVERIFY(map1.contains(i)); + map1.insert(1, "one"); + QVERIFY(map1.contains(1)); - map1.remove(43); - QVERIFY(!map1.contains(43)); + for (i = 2; i < 100; ++i) + map1.insert(i, "teststring"); + for (i = 99; i > 1; --i) + QVERIFY(map1.contains(i)); + + map1.remove(43); + QVERIFY(!map1.contains(43)); + } + + { + QMultiMap<int, QString> multiMap; + QVERIFY(!multiMap.contains(1)); + QVERIFY(!multiMap.contains(1, "value1")); + QVERIFY(!multiMap.isDetached()); + + multiMap.insert(1, "value1"); + multiMap.insert(2, "value2"); + multiMap.insert(1, "value1_1"); + + QVERIFY(multiMap.contains(1)); + QVERIFY(multiMap.contains(1, "value1")); + QVERIFY(multiMap.contains(1, "value1_1")); + QVERIFY(multiMap.contains(2)); + QVERIFY(multiMap.contains(2, "value2")); + QVERIFY(!multiMap.contains(2, "invalid_value")); + + QVERIFY(!multiMap.contains(3)); + multiMap.insert(3, "value3"); + QVERIFY(multiMap.contains(3)); + + multiMap.remove(3); + QVERIFY(!multiMap.contains(3)); + } } void tst_QMap::find() { - QMap<int, QString> map1; + { + const QMap<int, QString> constMap; + QCOMPARE(constMap.find(1), constMap.end()); + QVERIFY(!constMap.isDetached()); + + QMap<int, QString> map; + QCOMPARE(map.find(1), map.end()); + + map.insert(1, "value1"); + map.insert(2, "value2"); + map.insert(5, "value5"); + map.insert(1, "value0"); + + QCOMPARE(map.find(1).value(), u"value0"); + QCOMPARE(map.find(5).value(), u"value5"); + QCOMPARE(map.find(2).value(), u"value2"); + QCOMPARE(map.find(4), map.end()); + } + + const QMultiMap<int, QString> constMap; + QCOMPARE(constMap.find(1), constMap.end()); + QCOMPARE(constMap.find(1, "value"), constMap.end()); + QVERIFY(!constMap.isDetached()); + + QMultiMap<int, QString> map; QString testString="Teststring %0"; QString compareString; int i,count=0; - QVERIFY(map1.find(1) == map1.end()); + QCOMPARE(map.find(1), map.end()); + QCOMPARE(map.find(1, "value1"), map.end()); - map1.insert(1,"Mensch"); - map1.insert(1,"Mayer"); - map1.insert(2,"Hej"); + map.insert(1,"Mensch"); + map.insert(1,"Mayer"); + map.insert(2,"Hej"); - QCOMPARE(map1.find(1).value(), QLatin1String("Mayer")); - QCOMPARE(map1.find(2).value(), QLatin1String("Hej")); + QCOMPARE(map.find(1).value(), QLatin1String("Mayer")); + QCOMPARE(map.find(2).value(), QLatin1String("Hej")); + QCOMPARE(map.find(1, "Mensch").value(), QLatin1String("Mensch")); + QCOMPARE(map.find(1, "Unknown Value"), map.end()); - for(i = 3; i < 10; ++i) { + for (i = 3; i < 10; ++i) { compareString = testString.arg(i); - map1.insertMulti(4, compareString); - QCOMPARE(map1.count(4), i - 2); + map.insert(4, compareString); + QCOMPARE(map.count(4), i - 2); } - QMap<int, QString>::const_iterator it=map1.constFind(4); + QMultiMap<int, QString>::iterator it = map.find(4); - for(i = 9; i > 2 && it != map1.constEnd() && it.key() == 4; --i) { + for (i = 9; i > 2 && it != map.end() && it.key() == 4; --i) { compareString = testString.arg(i); QVERIFY(it.value() == compareString); + QCOMPARE(map.find(4, compareString), it); ++it; ++count; } @@ -625,32 +853,53 @@ void tst_QMap::find() void tst_QMap::constFind() { - QMap<int, QString> map1; + { + QMap<int, QString> map; + QCOMPARE(map.constFind(1), map.constEnd()); + QVERIFY(!map.isDetached()); + + map.insert(1, "value1"); + map.insert(2, "value2"); + map.insert(5, "value5"); + map.insert(1, "value0"); + + QCOMPARE(map.constFind(1).value(), QLatin1String("value0")); + QCOMPARE(map.constFind(5).value(), QLatin1String("value5")); + QCOMPARE(map.constFind(2).value(), QLatin1String("value2")); + QCOMPARE(map.constFind(4), map.constEnd()); + } + + QMultiMap<int, QString> map; QString testString="Teststring %0"; QString compareString; int i,count=0; - QVERIFY(map1.constFind(1) == map1.constEnd()); + QCOMPARE(map.constFind(1), map.constEnd()); + QCOMPARE(map.constFind(1, "value"), map.constEnd()); + QVERIFY(!map.isDetached()); - map1.insert(1,"Mensch"); - map1.insert(1,"Mayer"); - map1.insert(2,"Hej"); + map.insert(1,"Mensch"); + map.insert(1,"Mayer"); + map.insert(2,"Hej"); - QVERIFY(map1.constFind(4) == map1.constEnd()); + QVERIFY(map.constFind(4) == map.constEnd()); - QCOMPARE(map1.constFind(1).value(), QLatin1String("Mayer")); - QCOMPARE(map1.constFind(2).value(), QLatin1String("Hej")); + QCOMPARE(map.constFind(1).value(), QLatin1String("Mayer")); + QCOMPARE(map.constFind(2).value(), QLatin1String("Hej")); + QCOMPARE(map.constFind(1, "Mensch").value(), QLatin1String("Mensch")); + QCOMPARE(map.constFind(1, "Invalid Value"), map.constEnd()); - for(i = 3; i < 10; ++i) { + for (i = 3; i < 10; ++i) { compareString = testString.arg(i); - map1.insertMulti(4, compareString); + map.insert(4, compareString); } - QMap<int, QString>::const_iterator it=map1.constFind(4); + QMultiMap<int, QString>::const_iterator it = map.constFind(4); - for(i = 9; i > 2 && it != map1.constEnd() && it.key() == 4; --i) { + for (i = 9; i > 2 && it != map.constEnd() && it.key() == 4; --i) { compareString = testString.arg(i); QVERIFY(it.value() == compareString); + QCOMPARE(map.constFind(4, compareString), it); ++it; ++count; } @@ -659,58 +908,128 @@ void tst_QMap::constFind() void tst_QMap::lowerUpperBound() { - QMap<int, QString> map1; + { + const QMap<int, QString> emptyConstMap; + QCOMPARE(emptyConstMap.lowerBound(1), emptyConstMap.constEnd()); + QCOMPARE(emptyConstMap.upperBound(1), emptyConstMap.constEnd()); + QVERIFY(!emptyConstMap.isDetached()); + + const QMap<int, QString> constMap { qMakePair(1, "one"), + qMakePair(5, "five"), + qMakePair(10, "ten") }; + + QCOMPARE(constMap.lowerBound(-1).key(), 1); + QCOMPARE(constMap.lowerBound(1).key(), 1); + QCOMPARE(constMap.lowerBound(3).key(), 5); + QCOMPARE(constMap.lowerBound(12), constMap.constEnd()); + + QCOMPARE(constMap.upperBound(-1).key(), 1); + QCOMPARE(constMap.upperBound(1).key(), 5); + QCOMPARE(constMap.upperBound(3).key(), 5); + QCOMPARE(constMap.upperBound(12), constMap.constEnd()); + + QMap<int, QString> map; + + map.insert(1, "one"); + map.insert(5, "five"); + map.insert(10, "ten"); + map.insert(3, "three"); + map.insert(7, "seven"); + + QCOMPARE(map.lowerBound(0).key(), 1); + QCOMPARE(map.lowerBound(1).key(), 1); + QCOMPARE(map.lowerBound(2).key(), 3); + QCOMPARE(map.lowerBound(3).key(), 3); + QCOMPARE(map.lowerBound(4).key(), 5); + QCOMPARE(map.lowerBound(5).key(), 5); + QCOMPARE(map.lowerBound(6).key(), 7); + QCOMPARE(map.lowerBound(7).key(), 7); + QCOMPARE(map.lowerBound(10).key(), 10); + QCOMPARE(map.lowerBound(999), map.end()); + + QCOMPARE(map.upperBound(0).key(), 1); + QCOMPARE(map.upperBound(1).key(), 3); + QCOMPARE(map.upperBound(2).key(), 3); + QCOMPARE(map.upperBound(3).key(), 5); + QCOMPARE(map.upperBound(7).key(), 10); + QCOMPARE(map.upperBound(10), map.end()); + QCOMPARE(map.upperBound(999), map.end()); + } + + const QMultiMap<int, QString> emptyConstMap; + QCOMPARE(emptyConstMap.lowerBound(1), emptyConstMap.constEnd()); + QCOMPARE(emptyConstMap.upperBound(1), emptyConstMap.constEnd()); + QVERIFY(!emptyConstMap.isDetached()); - map1.insert(1, "one"); - map1.insert(5, "five"); - map1.insert(10, "ten"); + const QMultiMap<int, QString> constMap { qMakePair(1, "one"), + qMakePair(5, "five"), + qMakePair(10, "ten") }; + QCOMPARE(constMap.lowerBound(-1).key(), 1); + QCOMPARE(constMap.lowerBound(1).key(), 1); + QCOMPARE(constMap.lowerBound(3).key(), 5); + QCOMPARE(constMap.lowerBound(12), constMap.constEnd()); + + QCOMPARE(constMap.upperBound(-1).key(), 1); + QCOMPARE(constMap.upperBound(1).key(), 5); + QCOMPARE(constMap.upperBound(3).key(), 5); + QCOMPARE(constMap.upperBound(12), constMap.constEnd()); + + QMultiMap<int, QString> map; + + map.insert(1, "one"); + map.insert(5, "five"); + map.insert(10, "ten"); //Copied from documentation - QCOMPARE(map1.upperBound(0).key(), 1); // returns iterator to (1, "one") - QCOMPARE(map1.upperBound(1).key(), 5); // returns iterator to (5, "five") - QCOMPARE(map1.upperBound(2).key(), 5); // returns iterator to (5, "five") - QVERIFY(map1.upperBound(10) == map1.end()); // returns end() - QVERIFY(map1.upperBound(999) == map1.end()); // returns end() - - QCOMPARE(map1.lowerBound(0).key(), 1); // returns iterator to (1, "one") - QCOMPARE(map1.lowerBound(1).key(), 1); // returns iterator to (1, "one") - QCOMPARE(map1.lowerBound(2).key(), 5); // returns iterator to (5, "five") - QCOMPARE(map1.lowerBound(10).key(), 10); // returns iterator to (10, "ten") - QVERIFY(map1.lowerBound(999) == map1.end()); // returns end() - - map1.insert(3, "three"); - map1.insert(7, "seven"); - map1.insertMulti(7, "seven_2"); - - QCOMPARE(map1.upperBound(0).key(), 1); - QCOMPARE(map1.upperBound(1).key(), 3); - QCOMPARE(map1.upperBound(2).key(), 3); - QCOMPARE(map1.upperBound(3).key(), 5); - QCOMPARE(map1.upperBound(7).key(), 10); - QVERIFY(map1.upperBound(10) == map1.end()); - QVERIFY(map1.upperBound(999) == map1.end()); - - QCOMPARE(map1.lowerBound(0).key(), 1); - QCOMPARE(map1.lowerBound(1).key(), 1); - QCOMPARE(map1.lowerBound(2).key(), 3); - QCOMPARE(map1.lowerBound(3).key(), 3); - QCOMPARE(map1.lowerBound(4).key(), 5); - QCOMPARE(map1.lowerBound(5).key(), 5); - QCOMPARE(map1.lowerBound(6).key(), 7); - QCOMPARE(map1.lowerBound(7).key(), 7); - QCOMPARE(map1.lowerBound(6).value(), QLatin1String("seven_2")); - QCOMPARE(map1.lowerBound(7).value(), QLatin1String("seven_2")); - QCOMPARE((++map1.lowerBound(6)).value(), QLatin1String("seven")); - QCOMPARE((++map1.lowerBound(7)).value(), QLatin1String("seven")); - QCOMPARE(map1.lowerBound(10).key(), 10); - QVERIFY(map1.lowerBound(999) == map1.end()); + QCOMPARE(map.upperBound(0).key(), 1); // returns iterator to (1, "one") + QCOMPARE(map.upperBound(1).key(), 5); // returns iterator to (5, "five") + QCOMPARE(map.upperBound(2).key(), 5); // returns iterator to (5, "five") + QVERIFY(map.upperBound(10) == map.end()); // returns end() + QVERIFY(map.upperBound(999) == map.end()); // returns end() + + QCOMPARE(map.lowerBound(0).key(), 1); // returns iterator to (1, "one") + QCOMPARE(map.lowerBound(1).key(), 1); // returns iterator to (1, "one") + QCOMPARE(map.lowerBound(2).key(), 5); // returns iterator to (5, "five") + QCOMPARE(map.lowerBound(10).key(), 10); // returns iterator to (10, "ten") + QVERIFY(map.lowerBound(999) == map.end()); // returns end() + + map.insert(3, "three"); + map.insert(7, "seven"); + map.insert(7, "seven_2"); + + QCOMPARE(map.upperBound(0).key(), 1); + QCOMPARE(map.upperBound(1).key(), 3); + QCOMPARE(map.upperBound(2).key(), 3); + QCOMPARE(map.upperBound(3).key(), 5); + QCOMPARE(map.upperBound(7).key(), 10); + QVERIFY(map.upperBound(10) == map.end()); + QVERIFY(map.upperBound(999) == map.end()); + + QCOMPARE(map.lowerBound(0).key(), 1); + QCOMPARE(map.lowerBound(1).key(), 1); + QCOMPARE(map.lowerBound(2).key(), 3); + QCOMPARE(map.lowerBound(3).key(), 3); + QCOMPARE(map.lowerBound(4).key(), 5); + QCOMPARE(map.lowerBound(5).key(), 5); + QCOMPARE(map.lowerBound(6).key(), 7); + QCOMPARE(map.lowerBound(7).key(), 7); + QCOMPARE(map.lowerBound(6).value(), QLatin1String("seven_2")); + QCOMPARE(map.lowerBound(7).value(), QLatin1String("seven_2")); + QCOMPARE((++map.lowerBound(6)).value(), QLatin1String("seven")); + QCOMPARE((++map.lowerBound(7)).value(), QLatin1String("seven")); + QCOMPARE(map.lowerBound(10).key(), 10); + QVERIFY(map.lowerBound(999) == map.end()); } void tst_QMap::mergeCompare() { - QMap<int, QString> map1, map2, map3, map1b, map2b; + QMultiMap<int, QString> map1, map2, map3, map1b, map2b, map4; + + // unite with an empty map does nothing + map1.unite(map2); + QVERIFY(!map1.isDetached()); map1.insert(1,"ett"); map1.insert(3,"tre"); @@ -740,18 +1059,43 @@ void tst_QMap::mergeCompare() map3.insert(4, "fyra"); map3.insert(5, "fem"); - QVERIFY(map1 == map3); + QCOMPARE(map1, map3); + + map4.unite(map3); + QCOMPARE(map4, map3); } void tst_QMap::take() { - QMap<int, QString> map; + { + QMap<int, QString> map; + QCOMPARE(map.take(1), QString()); + QVERIFY(!map.isDetached()); - map.insert(2, "zwei"); - map.insert(3, "drei"); + map.insert(2, "zwei"); + map.insert(3, "drei"); - QCOMPARE(map.take(3), QLatin1String("drei")); - QVERIFY(!map.contains(3)); + QCOMPARE(map.take(3), QLatin1String("drei")); + QVERIFY(!map.contains(3)); + } + + { + QMultiMap<int, QString> multiMap; + QCOMPARE(multiMap.take(1), QString()); + QVERIFY(!multiMap.isDetached()); + + multiMap.insert(0, "value0"); + multiMap.insert(1, "value1"); + multiMap.insert(0, "value0_1"); + multiMap.insert(0, "value0_2"); + + // The most recently inserted value is returned + QCOMPARE(multiMap.take(0), u"value0_2"); + QCOMPARE(multiMap.take(0), u"value0_1"); + QCOMPARE(multiMap.take(0), u"value0"); + QCOMPARE(multiMap.take(0), QString()); + QVERIFY(!multiMap.contains(0)); + } } void tst_QMap::iterators() @@ -768,13 +1112,13 @@ void tst_QMap::iterators() QMap<int, QString>::iterator stlIt = map.begin(); QCOMPARE(stlIt.value(), QLatin1String("Teststring 1")); - stlIt+=5; + std::advance(stlIt, 5); QCOMPARE(stlIt.value(), QLatin1String("Teststring 6")); stlIt++; QCOMPARE(stlIt.value(), QLatin1String("Teststring 7")); - stlIt-=3; + std::advance(stlIt, -3); QCOMPARE(stlIt.value(), QLatin1String("Teststring 4")); stlIt--; @@ -789,13 +1133,13 @@ void tst_QMap::iterators() QMap<int, QString>::const_iterator cstlIt = map.constBegin(); QCOMPARE(cstlIt.value(), QLatin1String("Teststring 1")); - cstlIt+=5; + std::advance(cstlIt, 5); QCOMPARE(cstlIt.value(), QLatin1String("Teststring 6")); cstlIt++; QCOMPARE(cstlIt.value(), QLatin1String("Teststring 7")); - cstlIt-=3; + std::advance(cstlIt, -3); QCOMPARE(cstlIt.value(), QLatin1String("Teststring 4")); cstlIt--; @@ -831,14 +1175,125 @@ void tst_QMap::iterators() } } +void tst_QMap::multimapIterators() +{ + QMultiMap<int, QString> map; + const QString testString = "Teststring %1-%2"; + + for (int i = 0; i < 5; ++i) { + // reverse order, because the last added is returned first. + for (int j = 4; j >= 0; --j) + map.insert(i, testString.arg(i).arg(j)); + } + + // STL-style iterators + auto stlIt = map.begin(); + QCOMPARE(stlIt.value(), u"Teststring 0-0"); + + stlIt++; + QCOMPARE(stlIt.value(), u"Teststring 0-1"); + + std::advance(stlIt, 10); + QCOMPARE(stlIt.value(), u"Teststring 2-1"); + + std::advance(stlIt, -4); + QCOMPARE(stlIt.value(), u"Teststring 1-2"); + + stlIt--; + QCOMPARE(stlIt.value(), u"Teststring 1-1"); + + // STL-style const iterators + auto cstlIt = map.cbegin(); + QCOMPARE(cstlIt.value(), u"Teststring 0-0"); + + cstlIt++; + QCOMPARE(cstlIt.value(), u"Teststring 0-1"); + + std::advance(cstlIt, 16); + QCOMPARE(cstlIt.value(), u"Teststring 3-2"); + + std::advance(cstlIt, -6); + QCOMPARE(cstlIt.value(), u"Teststring 2-1"); + + cstlIt--; + QCOMPARE(cstlIt.value(), u"Teststring 2-0"); + + // Java-style iterator + QMultiMapIterator javaIt(map); + int i = 0; + int j = 0; + while (javaIt.hasNext()) { + javaIt.next(); + QCOMPARE(javaIt.value(), testString.arg(i).arg(j)); + if (++j == 5) { + j = 0; + i++; + } + } + + i = 4; + j = 4; + while (javaIt.hasPrevious()) { + javaIt.previous(); + QCOMPARE(javaIt.value(), testString.arg(i).arg(j)); + if (--j < 0) { + j = 4; + i--; + } + } +} + +template <typename T> +void iteratorsInEmptyMapTestMethod() +{ + T map; + using ConstIter = typename T::const_iterator; + ConstIter it1 = map.cbegin(); + ConstIter it2 = map.constBegin(); + QVERIFY(it1 == it2 && it2 == ConstIter()); + QVERIFY(!map.isDetached()); + + ConstIter it3 = map.cend(); + ConstIter it4 = map.constEnd(); + QVERIFY(it3 == it4 && it4 == ConstIter()); + QVERIFY(!map.isDetached()); + + // to call const overloads of begin() and end() + const T map2; + ConstIter it5 = map2.begin(); + ConstIter it6 = map2.end(); + QVERIFY(it5 == it6 && it6 == ConstIter()); + QVERIFY(!map2.isDetached()); + + using Iter = typename T::iterator; + Iter it7 = map.begin(); + Iter it8 = map.end(); + QVERIFY(it7 == it8); +} + +void tst_QMap::iteratorsInEmptyMap() +{ + iteratorsInEmptyMapTestMethod<QMap<int, int>>(); + if (QTest::currentTestFailed()) + return; + + iteratorsInEmptyMapTestMethod<QMultiMap<int, int>>(); +} + void tst_QMap::keyIterator() { QMap<int, int> map; + using KeyIterator = QMap<int, int>::key_iterator; + KeyIterator it1 = map.keyBegin(); + KeyIterator it2 = map.keyEnd(); + QVERIFY(it1 == it2 && it2 == KeyIterator()); + QVERIFY(!map.isDetached()); + for (int i = 0; i < 100; ++i) map.insert(i, i*100); - QMap<int, int>::key_iterator key_it = map.keyBegin(); + KeyIterator key_it = map.keyBegin(); QMap<int, int>::const_iterator it = map.cbegin(); for (int i = 0; i < 100; ++i) { QCOMPARE(*key_it, it.key()); @@ -859,8 +1314,48 @@ void tst_QMap::keyIterator() QCOMPARE(std::count(map.keyBegin(), map.keyEnd(), 99), 1); // DefaultConstructible test - typedef QMap<int, int>::key_iterator keyIterator; - Q_STATIC_ASSERT(std::is_default_constructible<keyIterator>::value); + static_assert(std::is_default_constructible<KeyIterator>::value); +} + +void tst_QMap::multimapKeyIterator() +{ + QMultiMap<int, int> map; + + using KeyIterator = QMultiMap<int, int>::key_iterator; + KeyIterator it1 = map.keyBegin(); + KeyIterator it2 = map.keyEnd(); + QVERIFY(it1 == it2 && it2 == KeyIterator()); + QVERIFY(!map.isDetached()); + + for (int i = 0; i < 5; ++i) { + for (int j = 4; j >= 0; --j) + map.insert(i, 100 * i + j); + } + + KeyIterator keyIt = map.keyBegin(); + QMultiMap<int, int>::const_iterator it = map.cbegin(); + for (int i = 0; i < 5; ++i) { + for (int j = 4; j >= 0; --j) { + QCOMPARE(*keyIt, it.key()); + ++keyIt; + ++it; + } + } + + keyIt = std::find(map.keyBegin(), map.keyEnd(), 3); + it = std::find(map.cbegin(), map.cend(), 3 * 100); + + QVERIFY(keyIt != map.keyEnd()); + QCOMPARE(*keyIt, it.key()); + QCOMPARE(*(keyIt++), (it++).key()); + QCOMPARE(*(keyIt--), (it--).key()); + QCOMPARE(*(++keyIt), (++it).key()); + QCOMPARE(*(--keyIt), (--it).key()); + + QCOMPARE(std::count(map.keyBegin(), map.keyEnd(), 2), 5); + + // DefaultConstructible test + static_assert(std::is_default_constructible<KeyIterator>::value); } void tst_QMap::keyValueIterator() @@ -880,6 +1375,12 @@ void tst_QMap::keyValueIterator() entry_type pair(it.key(), it.value()); QCOMPARE(*key_value_it, pair); + QCOMPARE(key_value_it->first, pair.first); + QCOMPARE(key_value_it->second, pair.second); + QCOMPARE(&(*key_value_it).first, &it.key()); + QCOMPARE(&key_value_it->first, &it.key()); + QCOMPARE(&(*key_value_it).second, &it.value()); + QCOMPARE(&key_value_it->second, &it.value()); ++key_value_it; ++it; } @@ -916,32 +1417,168 @@ void tst_QMap::keyValueIterator() QCOMPARE(std::count(map.constKeyValueBegin(), map.constKeyValueEnd(), entry_type(key, value)), 1); } +void tst_QMap::multimapKeyValueIterator() +{ + QMultiMap<int, int> map; + using EntryType = QMultiMap<int, int>::const_key_value_iterator::value_type; + + for (int i = 0; i < 5; ++i) { + for (int j = 4; j >= 0; --j) + map.insert(i, 100 * i + j); + } + + auto keyValueIt = map.constKeyValueBegin(); + auto it = map.cbegin(); + + for (int i = 0; i < map.size(); ++i) { + QVERIFY(keyValueIt != map.constKeyValueEnd()); + QVERIFY(it != map.cend()); + + EntryType pair(it.key(), it.value()); + QCOMPARE(*keyValueIt, pair); + QCOMPARE(keyValueIt->first, pair.first); + QCOMPARE(keyValueIt->second, pair.second); + ++keyValueIt; + ++it; + } + + QVERIFY(keyValueIt == map.constKeyValueEnd()); + QVERIFY(it == map.cend()); + + int key = 3; + int value = 100 * 3; + keyValueIt = std::find(map.constKeyValueBegin(), map.constKeyValueEnd(), EntryType(key, value)); + it = std::find(map.cbegin(), map.cend(), value); + + QVERIFY(keyValueIt != map.constKeyValueEnd()); + QCOMPARE(*keyValueIt, EntryType(it.key(), it.value())); + + ++it; + ++keyValueIt; + QCOMPARE(*keyValueIt, EntryType(it.key(), it.value())); + + --it; + --keyValueIt; + QCOMPARE(*keyValueIt, EntryType(it.key(), it.value())); + + std::advance(it, 5); + std::advance(keyValueIt, 5); + QCOMPARE(*keyValueIt, EntryType(it.key(), it.value())); + + std::advance(it, -5); + std::advance(keyValueIt, -5); + QCOMPARE(*keyValueIt, EntryType(it.key(), it.value())); + + key = 2; + value = 100 * 2 + 2; + auto cnt = std::count(map.constKeyValueBegin(), map.constKeyValueEnd(), EntryType(key, value)); + QCOMPARE(cnt, 1); +} + +template <typename T> +void keyValueIteratorInEmptyMapTestMethod() +{ + T map; + using ConstKeyValueIter = typename T::const_key_value_iterator; + + ConstKeyValueIter it1 = map.constKeyValueBegin(); + ConstKeyValueIter it2 = map.constKeyValueEnd(); + QVERIFY(it1 == it2 && it2 == ConstKeyValueIter()); + QVERIFY(!map.isDetached()); + + const T map2; + ConstKeyValueIter it3 = map2.keyValueBegin(); + ConstKeyValueIter it4 = map2.keyValueEnd(); + QVERIFY(it3 == it4 && it4 == ConstKeyValueIter()); + QVERIFY(!map2.isDetached()); + + using KeyValueIter = typename T::key_value_iterator; + + KeyValueIter it5 = map.keyValueBegin(); + KeyValueIter it6 = map.keyValueEnd(); + QVERIFY(it5 == it6); +} + +void tst_QMap::keyValueIteratorInEmptyMap() +{ + keyValueIteratorInEmptyMapTestMethod<QMap<int, int>>(); + if (QTest::currentTestFailed()) + return; + + keyValueIteratorInEmptyMapTestMethod<QMultiMap<int, int>>(); +} + void tst_QMap::keys_values_uniqueKeys() { - QMap<QString, int> map; - QVERIFY(map.uniqueKeys().isEmpty()); + { + QMap<QString, int> map; + QVERIFY(map.keys().isEmpty()); + QVERIFY(map.keys(1).isEmpty()); + QVERIFY(map.values().isEmpty()); + QVERIFY(!map.isDetached()); + + map.insert("one", 1); + QCOMPARE(map.keys(), QStringList({ "one" })); + QCOMPARE(map.keys(1), QStringList({ "one" })); + QCOMPARE(map.values(), QList<int>({ 1 })); + + map.insert("two", 2); + QCOMPARE(map.keys(), QStringList({ "one", "two" })); + QCOMPARE(map.keys(1), QStringList({ "one" })); + QCOMPARE(map.values(), QList<int>({ 1, 2 })); + + map.insert("three", 2); + QCOMPARE(map.keys(), QStringList({ "one", "three", "two" })); + QCOMPARE(map.keys(2), QStringList({ "three", "two" })); + QCOMPARE(map.values(), QList<int>({ 1, 2, 2 })); + + map.insert("one", 0); + QCOMPARE(map.keys(), QStringList({ "one", "three", "two" })); + QCOMPARE(map.keys(1), QStringList()); + QCOMPARE(map.keys(0), QStringList({ "one" })); + QCOMPARE(map.keys(2), QStringList({ "three", "two" })); + QCOMPARE(map.values(), QList<int>({ 0, 2, 2 })); + } + + QMultiMap<QString, int> map; QVERIFY(map.keys().isEmpty()); + QVERIFY(map.keys(1).isEmpty()); + QVERIFY(map.uniqueKeys().isEmpty()); QVERIFY(map.values().isEmpty()); + QVERIFY(map.values("key").isEmpty()); + QVERIFY(!map.isDetached()); - map.insertMulti("alpha", 1); + map.insert("alpha", 1); QVERIFY(map.keys() == (QList<QString>() << "alpha")); - QVERIFY(map.uniqueKeys() == map.keys()); QVERIFY(map.values() == (QList<int>() << 1)); + QVERIFY(map.uniqueKeys() == QList<QString>({ "alpha" })); - map.insertMulti("beta", -2); + map.insert("beta", -2); QVERIFY(map.keys() == (QList<QString>() << "alpha" << "beta")); - QVERIFY(map.keys() == map.uniqueKeys()); QVERIFY(map.values() == (QList<int>() << 1 << -2)); + QVERIFY(map.uniqueKeys() == QList<QString>({ "alpha", "beta" })); - map.insertMulti("alpha", 2); - QVERIFY(map.uniqueKeys() == (QList<QString>() << "alpha" << "beta")); + map.insert("alpha", 2); QVERIFY(map.keys() == (QList<QString>() << "alpha" << "alpha" << "beta")); QVERIFY(map.values() == (QList<int>() << 2 << 1 << -2)); + QVERIFY(map.uniqueKeys() == QList<QString>({ "alpha", "beta" })); + QVERIFY(map.values("alpha") == QList<int>({ 2, 1 })); - map.insertMulti("beta", 4); - QVERIFY(map.uniqueKeys() == (QList<QString>() << "alpha" << "beta")); + map.insert("beta", 4); QVERIFY(map.keys() == (QList<QString>() << "alpha" << "alpha" << "beta" << "beta")); QVERIFY(map.values() == (QList<int>() << 2 << 1 << 4 << -2)); + QVERIFY(map.uniqueKeys() == QList<QString>({ "alpha", "beta" })); + QVERIFY(map.values("alpha") == QList<int>({ 2, 1 })); + QVERIFY(map.values("beta") == QList<int>({ 4, -2 })); + + map.insert("gamma", 2); + QVERIFY(map.keys() == QList<QString>({ "alpha", "alpha", "beta", "beta", "gamma" })); + QVERIFY(map.values() == QList<int>({ 2, 1, 4, -2, 2 })); + QVERIFY(map.uniqueKeys() == QList<QString>({ "alpha", "beta", "gamma" })); + QVERIFY(map.values("alpha") == QList<int>({ 2, 1 })); + QVERIFY(map.values("beta") == QList<int>({ 4, -2 })); + QVERIFY(map.values("gamma") == QList<int>({ 2 })); + QVERIFY(map.keys(2) == QList<QString>({ "alpha", "gamma" })); } void tst_QMap::qmultimap_specific() @@ -964,26 +1601,26 @@ void tst_QMap::qmultimap_specific() } QVERIFY(map1.contains(9, 99)); - QCOMPARE(map1.count(), 45); + QCOMPARE(map1.size(), 45); map1.remove(9, 99); QVERIFY(!map1.contains(9, 99)); - QCOMPARE(map1.count(), 44); + QCOMPARE(map1.size(), 44); map1.remove(9, 99); QVERIFY(!map1.contains(9, 99)); - QCOMPARE(map1.count(), 44); + QCOMPARE(map1.size(), 44); map1.remove(1, 99); - QCOMPARE(map1.count(), 44); + QCOMPARE(map1.size(), 44); map1.insert(1, 99); map1.insert(1, 99); - QCOMPARE(map1.count(), 46); + QCOMPARE(map1.size(), 46); map1.remove(1, 99); - QCOMPARE(map1.count(), 44); + QCOMPARE(map1.size(), 44); map1.remove(1, 99); - QCOMPARE(map1.count(), 44); + QCOMPARE(map1.size(), 44); { QMultiMap<int, int>::const_iterator i = map1.constFind(1, 11); @@ -1042,7 +1679,7 @@ void tst_QMap::qmultimap_specific() map2.insert(42, 1); map2.insert(10, 2); map2.insert(48, 3); - QCOMPARE(map1.count(), map2.count()); + QCOMPARE(map1.size(), map2.size()); QVERIFY(map1.remove(42,5)); QVERIFY(map2.remove(42,5)); QVERIFY(map1 == map2); @@ -1069,14 +1706,45 @@ void tst_QMap::const_shared_null() void tst_QMap::equal_range() { - QMap<int, QString> map; - const QMap<int, QString> &cmap = map; + { + const QMap<int, QString> constMap; + QCOMPARE(constMap.equal_range(1), qMakePair(constMap.constEnd(), constMap.constEnd())); + QVERIFY(!constMap.isDetached()); + + QMap<int, QString> map; + QCOMPARE(map.equal_range(1), qMakePair(map.end(), map.end())); + + map.insert(1, "value1"); + map.insert(5, "value5"); + map.insert(1, "value0"); + + auto pair = map.equal_range(1); + QCOMPARE(pair.first.value(), "value0"); + QCOMPARE(pair.second.value(), "value5"); + auto b = map.find(1); + auto e = map.find(5); + QCOMPARE(pair, qMakePair(b, e)); + + pair = map.equal_range(3); + QCOMPARE(pair.first.value(), "value5"); + QCOMPARE(pair.second.value(), "value5"); + QCOMPARE(pair, qMakePair(e, e)); + + QCOMPARE(map.equal_range(10), qMakePair(map.end(), map.end())); + } + + const QMultiMap<int, QString> constMap; + QCOMPARE(constMap.equal_range(1), qMakePair(constMap.constEnd(), constMap.constEnd())); + QVERIFY(!constMap.isDetached()); - QPair<QMap<int, QString>::iterator, QMap<int, QString>::iterator> result = map.equal_range(0); + QMultiMap<int, QString> map; + const QMultiMap<int, QString> &cmap = map; + + QPair<QMultiMap<int, QString>::iterator, QMultiMap<int, QString>::iterator> result = map.equal_range(0); QCOMPARE(result.first, map.end()); QCOMPARE(result.second, map.end()); - QPair<QMap<int, QString>::const_iterator, QMap<int, QString>::const_iterator> cresult = cmap.equal_range(0); + QPair<QMultiMap<int, QString>::const_iterator, QMultiMap<int, QString>::const_iterator> cresult = cmap.equal_range(0); QCOMPARE(cresult.first, cmap.cend()); QCOMPARE(cresult.second, cmap.cend()); @@ -1133,7 +1801,7 @@ void tst_QMap::equal_range() QCOMPARE(cresult.first, cmap.find(2)); QCOMPARE(cresult.second, cmap.find(4)); - map.insertMulti(1, "another one"); + map.insert(1, "another one"); result = map.equal_range(1); QCOMPARE(result.first, map.find(1)); @@ -1146,19 +1814,13 @@ void tst_QMap::equal_range() QCOMPARE(map.count(1), 2); } -template <class T> -const T &const_(const T &t) -{ - return t; -} - void tst_QMap::insert() { QMap<QString, float> map; map.insert("cs/key1", 1); map.insert("cs/key2", 2); map.insert("cs/key1", 3); - QCOMPARE(map.count(), 2); + QCOMPARE(map.size(), 2); QMap<int, int> intMap; for (int i = 0; i < 1000; ++i) { @@ -1202,6 +1864,226 @@ void tst_QMap::insert() } } +template <template <typename K, typename T> typename Map> +void testDetachWhenInsert() +{ + const Map<int, int> referenceSource = { + { 0, 0 }, + { 1, 1 }, + { 2, 2 } + }; + + const Map<int, int> referenceDestination = { + { 0, 0 }, + { 1, 1 }, + { 2, 2 }, + { 3, 3 } + }; + + // copy insertion of non-shared map + { + Map<int, int> source; + source.insert(0, 0); + source.insert(1, 1); + source.insert(2, 2); + + Map<int, int> dest; + dest.insert(3, 3); + Map<int, int> destCopy = dest; + + if constexpr (std::is_same_v<decltype(dest), QMap<int, int>>) + dest.insert(source); // QMap + else + dest.unite(source); // QMultiMap + + QCOMPARE(source, referenceSource); + QCOMPARE(dest, referenceDestination); + + QCOMPARE(destCopy.size(), 1); // unchanged + } + + // copy insertion of shared map + { + Map<int, int> source; + source.insert(0, 0); + source.insert(1, 1); + source.insert(2, 2); + Map<int, int> sourceCopy = source; + + Map<int, int> dest; + dest.insert(3, 3); + Map<int, int> destCopy = dest; + + if constexpr (std::is_same_v<decltype(dest), QMap<int, int>>) + dest.insert(source); // QMap + else + dest.unite(source); // QMultiMap + + QCOMPARE(source, referenceSource); + QCOMPARE(sourceCopy, referenceSource); + + QCOMPARE(dest, referenceDestination); + QCOMPARE(destCopy.size(), 1); // unchanged + } + + // move insertion of non-shared map + { + Map<int, int> source; + source.insert(0, 0); + source.insert(1, 1); + source.insert(2, 2); + + Map<int, int> dest; + dest.insert(3, 3); + Map<int, int> destCopy = dest; + + if constexpr (std::is_same_v<decltype(dest), QMap<int, int>>) + dest.insert(source); // QMap + else + dest.unite(source); // QMultiMap + + QCOMPARE(dest, referenceDestination); + QCOMPARE(destCopy.size(), 1); // unchanged + } + + // move insertion of shared map + { + Map<int, int> source; + source.insert(0, 0); + source.insert(1, 1); + source.insert(2, 2); + Map<int, int> sourceCopy = source; + + Map<int, int> dest; + dest.insert(3, 3); + Map<int, int> destCopy = dest; + + if constexpr (std::is_same_v<decltype(dest), QMap<int, int>>) + dest.insert(std::move(source)); // QMap + else + dest.unite(std::move(source)); // QMultiMap + + QCOMPARE(sourceCopy, referenceSource); + + QCOMPARE(dest, referenceDestination); + QCOMPARE(destCopy.size(), 1); // unchanged + } +}; + +void tst_QMap::insertMap() +{ + { + QMap<int, int> map1; + QMap<int, int> map2; + QVERIFY(map1.isEmpty()); + QVERIFY(map2.isEmpty()); + + map1.insert(map2); + QVERIFY(map1.isEmpty()); + QVERIFY(map2.isEmpty()); + QVERIFY(!map1.isDetached()); + QVERIFY(!map2.isDetached()); + } + { + QMap<int, int> map; + map.insert(1, 1); + map.insert(2, 2); + map.insert(0, -1); + + QMap<int, int> map2; + map2.insert(0, 0); + map2.insert(3, 3); + map2.insert(4, 4); + + map.insert(map2); + + QCOMPARE(map.size(), 5); + for (int i = 0; i < 5; ++i) + QCOMPARE(map[i], i); + } + { + QMap<int, int> map; + for (int i = 0; i < 10; ++i) + map.insert(i * 3, i); + + QMap<int, int> map2; + for (int i = 0; i < 10; ++i) + map2.insert(i * 4, i); + + map.insert(map2); + + QCOMPARE(map.size(), 17); + for (int i = 0; i < 10; ++i) { + // i * 3 == i except for i = 4, 8 + QCOMPARE(map[i * 3], (i && i % 4 == 0) ? i - (i / 4) : i); + QCOMPARE(map[i * 4], i); + } + + auto it = map.cbegin(); + int prev = it.key(); + ++it; + for (auto end = map.cend(); it != end; ++it) { + QVERIFY(prev < it.key()); + prev = it.key(); + } + } + { + QMap<int, int> map; + map.insert(1, 1); + + QMap<int, int> map2; + + map.insert(map2); + QCOMPARE(map.size(), 1); + QCOMPARE(map[1], 1); + } + { + QMap<int, int> map; + QMap<int, int> map2; + map2.insert(1, 1); + + map.insert(map2); + QCOMPARE(map.size(), 1); + QCOMPARE(map[1], 1); + + QMap<int, int> map3; + map3.insert(std::move(map2)); + QCOMPARE(map3, map); + } + { + QMap<int, int> map; + map.insert(0, 0); + map.insert(1, 1); + map.insert(2, 2); + + // Test inserting into self, nothing should happen + map.insert(map); + + QCOMPARE(map.size(), 3); + for (int i = 0; i < 3; ++i) + QCOMPARE(map[i], i); + } + { + // Here we use a QMultiMap and insert that into QMap, + // since it has multiple values with the same key the + // ordering is undefined so we won't test that, but + // make sure this isn't adding multiple entries with the + // same key to the QMap. + QMap<int, int> map; + map.insert(0, 0); + + QMap<int, int> map2; + map2.insert(0, 1); + + map.insert(map2); + + QCOMPARE(map.size(), 1); + } + + testDetachWhenInsert<QMap>(); + testDetachWhenInsert<QMultiMap>(); +} + void tst_QMap::checkMostLeftNode() { QMap<int, int> map; @@ -1257,7 +2139,7 @@ void tst_QMap::checkMostLeftNode() void tst_QMap::initializerList() { QMap<int, QString> map = {{1, "bar"}, {1, "hello"}, {2, "initializer_list"}}; - QCOMPARE(map.count(), 2); + QCOMPARE(map.size(), 2); QCOMPARE(map[1], QString("hello")); QCOMPARE(map[2], QString("initializer_list")); @@ -1267,9 +2149,9 @@ void tst_QMap::initializerList() // QCOMPARE(stdm[1], QString("bar")); QMultiMap<QString, int> multiMap{{"il", 1}, {"il", 2}, {"il", 3}}; - QCOMPARE(multiMap.count(), 3); + QCOMPARE(multiMap.size(), 3); QList<int> values = multiMap.values("il"); - QCOMPARE(values.count(), 3); + QCOMPARE(values.size(), 3); QMap<int, int> emptyMap{}; QVERIFY(emptyMap.isEmpty()); @@ -1349,64 +2231,64 @@ void tst_QMap::testInsertWithHint() void tst_QMap::testInsertMultiWithHint() { - QMap<int, int> map; + QMultiMap<int, int> map; - map.insertMulti(map.end(), 64, 65); - map[128] = 129; - map[256] = 257; + map.insert(map.end(), 64, 65); + map.insert(128, 129); + map.insert(256, 257); sanityCheckTree(map, __LINE__); - map.insertMulti(map.end(), 512, 513); - map.insertMulti(map.end(), 512, 513 * 2); + map.insert(map.end(), 512, 513); + map.insert(map.end(), 512, 513 * 2); sanityCheckTree(map, __LINE__); QCOMPARE(map.size(), 5); - map.insertMulti(map.end(), 256, 258); // wrong hint + map.insert(map.end(), 256, 258); // wrong hint sanityCheckTree(map, __LINE__); QCOMPARE(map.size(), 6); - QMap<int, int>::iterator i = map.insertMulti(map.constBegin(), 256, 259); // wrong hint + QMultiMap<int, int>::iterator i = map.insert(map.constBegin(), 256, 259); // wrong hint sanityCheckTree(map, __LINE__); QCOMPARE(map.size(), 7); - QMap<int, int>::iterator j = map.insertMulti(map.constBegin(), 69, 66); + QMultiMap<int, int>::iterator j = map.insert(map.constBegin(), 69, 66); sanityCheckTree(map, __LINE__); QCOMPARE(map.size(), 8); - j = map.insertMulti(j, 68, 259); + j = map.insert(j, 68, 259); sanityCheckTree(map, __LINE__); QCOMPARE(map.size(), 9); - j = map.insertMulti(j, 67, 67); + j = map.insert(j, 67, 67); sanityCheckTree(map, __LINE__); QCOMPARE(map.size(), 10); - i = map.insertMulti(i, 256, 259); + i = map.insert(i, 256, 259); sanityCheckTree(map, __LINE__); QCOMPARE(map.size(), 11); - i = map.insertMulti(i, 256, 260); + i = map.insert(i, 256, 260); sanityCheckTree(map, __LINE__); QCOMPARE(map.size(), 12); - map.insertMulti(i, 64, 67); + map.insert(i, 64, 67); sanityCheckTree(map, __LINE__); QCOMPARE(map.size(), 13); - map.insertMulti(map.constBegin(), 20, 20); + map.insert(map.constBegin(), 20, 20); sanityCheckTree(map, __LINE__); QCOMPARE(map.size(), 14); } void tst_QMap::eraseValidIteratorOnSharedMap() { - QMap<int, int> a, b; + QMultiMap<int, int> a, b; a.insert(10, 10); - a.insertMulti(10, 40); - a.insertMulti(10, 25); - a.insertMulti(10, 30); + a.insert(10, 40); + a.insert(10, 25); + a.insert(10, 30); a.insert(20, 20); - QMap<int, int>::iterator i = a.begin(); + QMultiMap<int, int>::iterator i = a.begin(); while (i.value() != 25) ++i; @@ -1426,12 +2308,12 @@ void tst_QMap::eraseValidIteratorOnSharedMap() QCOMPARE(itemsWith10, 4); // Border cases - QMap <QString, QString> ms1, ms2, ms3; + QMultiMap <QString, QString> ms1, ms2, ms3; + ms1.insert("foo", "bar"); + ms1.insert("foo", "quux"); ms1.insert("foo", "bar"); - ms1.insertMulti("foo", "quux"); - ms1.insertMulti("foo", "bar"); - QMap <QString, QString>::iterator si = ms1.begin(); + QMultiMap <QString, QString>::iterator si = ms1.begin(); ms2 = ms1; ms1.erase(si); si = ms1.begin(); @@ -1454,5 +2336,314 @@ void tst_QMap::eraseValidIteratorOnSharedMap() QCOMPARE(ms3.size(), 3); } +void tst_QMap::removeElementsInMap() +{ + // A class that causes an almost certain crash if its operator< is + // called on a destroyed object + struct SharedInt { + QSharedPointer<int> m_int; + explicit SharedInt(int i) : m_int(QSharedPointer<int>::create(i)) {} + bool operator<(const SharedInt &other) const { return *m_int < *other.m_int; } + }; + + { + QMap<int, int> map; + QCOMPARE(map.remove(1), 0); + QVERIFY(!map.isDetached()); + + auto cnt = map.removeIf([](QMap<int, int>::iterator) { return true; }); + QCOMPARE(cnt, 0); + } + { + QMap<SharedInt, int> map { + { SharedInt(1), 1 }, + { SharedInt(2), 2 }, + { SharedInt(3), 3 }, + { SharedInt(4), 4 }, + { SharedInt(5), 5 }, + }; + QCOMPARE(map.size(), 5); + + map.remove(SharedInt(1)); + QCOMPARE(map.size(), 4); + + map.remove(SharedInt(-1)); + QCOMPARE(map.size(), 4); + + QMap<SharedInt, int> map2 = map; + QCOMPARE(map.size(), 4); + QCOMPARE(map2.size(), 4); + + map.remove(SharedInt(3)); + QCOMPARE(map.size(), 3); + QCOMPARE(map2.size(), 4); + + map.remove(SharedInt(-1)); + QCOMPARE(map.size(), 3); + QCOMPARE(map2.size(), 4); + + map = map2; + QCOMPARE(map.size(), 4); + QCOMPARE(map2.size(), 4); + + map.remove(SharedInt(-1)); + QCOMPARE(map.size(), 4); + QCOMPARE(map2.size(), 4); + + map.remove(map.firstKey()); + QCOMPARE(map.size(), 3); + QCOMPARE(map2.size(), 4); + + map.remove(map.lastKey()); + QCOMPARE(map.size(), 2); + QCOMPARE(map2.size(), 4); + + map = map2; + QCOMPARE(map.size(), 4); + QCOMPARE(map2.size(), 4); + + auto size = map.size(); + for (auto it = map.begin(); it != map.end(); ) { + const auto oldIt = it++; + size -= map.remove(oldIt.key()); + QCOMPARE(map.size(), size); + QCOMPARE(map2.size(), 4); + } + + QCOMPARE(map.size(), 0); + QCOMPARE(map2.size(), 4); + + auto cnt = map2.removeIf([](auto it) { return (*it % 2) == 0; }); + QCOMPARE(cnt, 2); + QCOMPARE(map2.size(), 2); + } + + { + QMultiMap<int, int> map; + QCOMPARE(map.remove(1), 0); + QVERIFY(!map.isDetached()); + + auto cnt = map.removeIf([](QMultiMap<int, int>::iterator) { return true; }); + QCOMPARE(cnt, 0); + } + { + QMultiMap<SharedInt, int> multimap { + { SharedInt(1), 10 }, + { SharedInt(1), 11 }, + { SharedInt(2), 2 }, + { SharedInt(3), 30 }, + { SharedInt(3), 31 }, + { SharedInt(3), 32 }, + { SharedInt(4), 4 }, + { SharedInt(5), 5 }, + { SharedInt(6), 60 }, + { SharedInt(6), 61 }, + { SharedInt(6), 60 }, + { SharedInt(6), 62 }, + { SharedInt(6), 60 }, + { SharedInt(7), 7 }, + }; + + QCOMPARE(multimap.size(), 14); + + multimap.remove(SharedInt(1)); + QCOMPARE(multimap.size(), 12); + + multimap.remove(SharedInt(-1)); + QCOMPARE(multimap.size(), 12); + + QMultiMap<SharedInt, int> multimap2 = multimap; + QCOMPARE(multimap.size(), 12); + QCOMPARE(multimap2.size(), 12); + + multimap.remove(SharedInt(3)); + QCOMPARE(multimap.size(), 9); + QCOMPARE(multimap2.size(), 12); + + multimap.remove(SharedInt(4)); + QCOMPARE(multimap.size(), 8); + QCOMPARE(multimap2.size(), 12); + + multimap.remove(SharedInt(-1)); + QCOMPARE(multimap.size(), 8); + QCOMPARE(multimap2.size(), 12); + + multimap = multimap2; + QCOMPARE(multimap.size(), 12); + QCOMPARE(multimap2.size(), 12); + + multimap.remove(SharedInt(-1)); + QCOMPARE(multimap.size(), 12); + QCOMPARE(multimap2.size(), 12); + + multimap.remove(SharedInt(6), 60); + QCOMPARE(multimap.size(), 9); + QCOMPARE(multimap2.size(), 12); + + multimap = multimap2; + QCOMPARE(multimap.size(), 12); + QCOMPARE(multimap2.size(), 12); + + multimap.remove(SharedInt(6), 62); + QCOMPARE(multimap.size(), 11); + QCOMPARE(multimap2.size(), 12); + + multimap.remove(multimap.firstKey()); + QCOMPARE(multimap.size(), 10); + QCOMPARE(multimap2.size(), 12); + + multimap.remove(multimap.lastKey()); + QCOMPARE(multimap.size(), 9); + QCOMPARE(multimap2.size(), 12); + + multimap = multimap2; + QCOMPARE(multimap.size(), 12); + QCOMPARE(multimap2.size(), 12); + + auto itFor6 = multimap.find(SharedInt(6)); + QVERIFY(itFor6 != multimap.end()); + QCOMPARE(itFor6.value(), 60); + multimap.remove(itFor6.key(), itFor6.value()); + QCOMPARE(multimap.size(), 9); + QCOMPARE(multimap2.size(), 12); + + multimap = multimap2; + QCOMPARE(multimap.size(), 12); + QCOMPARE(multimap2.size(), 12); + + auto size = multimap.size(); + for (auto it = multimap.begin(); it != multimap.end();) { + const auto range = multimap.equal_range(it.key()); + const auto oldIt = it; + it = range.second; + size -= multimap.remove(oldIt.key()); + QCOMPARE(multimap.size(), size); + QCOMPARE(multimap2.size(), 12); + } + + QCOMPARE(multimap.size(), 0); + QCOMPARE(multimap2.size(), 12); + + auto cnt = multimap2.removeIf([](auto it) { return (*it % 2) == 0; }); + QCOMPARE(cnt, 8); + QCOMPARE(multimap2.size(), 4); + } +} + +template <typename QtMap, typename StdMap> +void toStdMapTestMethod(const StdMap &expectedMap) +{ + QtMap map; + QVERIFY(map.isEmpty()); + auto stdMap = map.toStdMap(); + QVERIFY(stdMap.empty()); + QVERIFY(!map.isDetached()); + + map.insert(1, "value1"); + map.insert(2, "value2"); + map.insert(3, "value3"); + map.insert(1, "value0"); + + stdMap = map.toStdMap(); + QCOMPARE(stdMap, expectedMap); +} + +void tst_QMap::toStdMap() +{ + const std::map<int, QString> expectedMap { {1, "value0"}, {2, "value2"}, {3, "value3"} }; + toStdMapTestMethod<QMap<int, QString>>(expectedMap); + if (QTest::currentTestFailed()) + return; + + const std::multimap<int, QString> expectedMultiMap { + {1, "value0"}, {1, "value1"}, {2, "value2"}, {3, "value3"} }; + toStdMapTestMethod<QMultiMap<int, QString>>(expectedMultiMap); +} + +void tst_QMap::multiMapStoresInReverseInsertionOrder() +{ + const QString strings[] = { + u"zero"_s, + u"null"_s, + u"nada"_s, + }; + { + QMultiMap<int, QString> map; + for (const QString &string : strings) + map.insert(0, string); + auto printOnFailure = qScopeGuard([&] { qDebug() << map; }); + QVERIFY(std::equal(map.begin(), map.end(), + std::rbegin(strings), std::rend(strings))); + printOnFailure.dismiss(); + } +} + +#if QT_DEPRECATED_SINCE(6, 0) +void tst_QMap::deprecatedInsertMulti() +{ + QMultiMap<int, QString> referenceMap; + referenceMap.insert(1, "value1"); + referenceMap.insert(2, "value2"); + referenceMap.insert(3, "value3"); + referenceMap.insert(1, "value1_2"); + referenceMap.insert(referenceMap.find(2), 2, "value2_2"); + referenceMap.insert(referenceMap.end(), 1, "value1_3"); + + QMultiMap<int, QString> deprecatedMap; +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED + deprecatedMap.insertMulti(1, "value1"); + deprecatedMap.insertMulti(2, "value2"); + deprecatedMap.insertMulti(3, "value3"); + deprecatedMap.insertMulti(1, "value1_2"); + deprecatedMap.insertMulti(deprecatedMap.find(2), 2, "value2_2"); + deprecatedMap.insertMulti(deprecatedMap.end(), 1, "value1_3"); +QT_WARNING_POP + + QCOMPARE(deprecatedMap, referenceMap); +} + +void tst_QMap::deprecatedIteratorApis() +{ + QMap<int, QString> map; + QString testString = "Teststring %1"; + for (int i = 1; i < 100; ++i) + map.insert(i, testString.arg(i)); + + auto it = map.begin(); + QCOMPARE(it.value(), QLatin1String("Teststring 1")); + QT_IGNORE_DEPRECATIONS(it += 5;) + QCOMPARE(it.value(), QLatin1String("Teststring 6")); + QT_IGNORE_DEPRECATIONS(it = it - 3;) + QCOMPARE(it.value(), QLatin1String("Teststring 3")); + + auto cit = map.constBegin(); + QCOMPARE(cit.value(), QLatin1String("Teststring 1")); + QT_IGNORE_DEPRECATIONS(cit += 5;) + QCOMPARE(cit.value(), QLatin1String("Teststring 6")); + QT_IGNORE_DEPRECATIONS(cit = cit - 3;) + QCOMPARE(cit.value(), QLatin1String("Teststring 3")); +} + +void tst_QMap::deprecatedInsert() +{ + QMultiMap<int, QString> refMap; + refMap.insert(1, "value1"); + refMap.insert(2, "value2"); + refMap.insert(3, "value3"); + + QMultiMap<int, QString> depMap = refMap; + + QMultiMap<int, QString> otherMap; + otherMap.insert(1, "value1_2"); + otherMap.insert(3, "value3_2"); + otherMap.insert(4, "value4"); + + refMap.unite(otherMap); + QT_IGNORE_DEPRECATIONS(depMap.insert(otherMap);) + + QCOMPARE(refMap, depMap); +} +#endif // QT_DEPRECATED_SINCE(6, 0) + QTEST_APPLESS_MAIN(tst_QMap) #include "tst_qmap.moc" diff --git a/tests/auto/corelib/tools/qmargins/CMakeLists.txt b/tests/auto/corelib/tools/qmargins/CMakeLists.txt new file mode 100644 index 0000000000..2e0ea797ff --- /dev/null +++ b/tests/auto/corelib/tools/qmargins/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qmargins Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmargins LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qmargins + SOURCES + tst_qmargins.cpp +) diff --git a/tests/auto/corelib/tools/qmargins/qmargins.pro b/tests/auto/corelib/tools/qmargins/qmargins.pro deleted file mode 100644 index 696f9374a2..0000000000 --- a/tests/auto/corelib/tools/qmargins/qmargins.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qmargins -QT = core testlib -SOURCES = tst_qmargins.cpp diff --git a/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp b/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp index 2a35162ef0..2611f62f01 100644 --- a/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp +++ b/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp @@ -1,34 +1,40 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QMargins> +#ifdef QVARIANT_H +# error "This test requires qmargins.h to not include qvariant.h" +#endif + +// don't assume <type_traits> +template <typename T, typename U> +constexpr inline bool my_is_same_v = false; +template <typename T> +constexpr inline bool my_is_same_v<T, T> = true; + +#define CHECK(cvref) \ + static_assert(my_is_same_v<decltype(get<0>(std::declval<QMargins cvref >())), int cvref >); \ + static_assert(my_is_same_v<decltype(get<1>(std::declval<QMargins cvref >())), int cvref >); \ + static_assert(my_is_same_v<decltype(get<2>(std::declval<QMargins cvref >())), int cvref >); \ + static_assert(my_is_same_v<decltype(get<3>(std::declval<QMargins cvref >())), int cvref >); \ + \ + static_assert(my_is_same_v<decltype(get<0>(std::declval<QMarginsF cvref >())), qreal cvref >); \ + static_assert(my_is_same_v<decltype(get<1>(std::declval<QMarginsF cvref >())), qreal cvref >); \ + static_assert(my_is_same_v<decltype(get<2>(std::declval<QMarginsF cvref >())), qreal cvref >); \ + static_assert(my_is_same_v<decltype(get<3>(std::declval<QMarginsF cvref >())), qreal cvref >) + +CHECK(&); +CHECK(const &); +CHECK(&&); +CHECK(const &&); + +#undef CHECK + +#include <QTest> #include <qmargins.h> +#include <array> + Q_DECLARE_METATYPE(QMargins) class tst_QMargins : public QObject @@ -36,12 +42,27 @@ class tst_QMargins : public QObject Q_OBJECT private slots: void getSetCheck(); +#ifndef QT_NO_DATASTREAM void dataStreamCheck(); +#endif void operators(); +#ifndef QT_NO_DEBUG_STREAM + void debugStreamCheck(); +#endif void getSetCheckF(); +#ifndef QT_NO_DATASTREAM void dataStreamCheckF(); +#endif void operatorsF(); +#ifndef QT_NO_DEBUG_STREAM + void debugStreamCheckF(); +#endif + + void structuredBinding(); + + void toMarginsF_data(); + void toMarginsF(); }; // Testing get/set functions @@ -123,6 +144,19 @@ void tst_QMargins::operators() QCOMPARE(-m3, QMargins(-10, -11, -12, -13)); } +#ifndef QT_NO_DEBUG_STREAM +// Testing QDebug operators +void tst_QMargins::debugStreamCheck() +{ + QMargins m(10, 11, 12, 13); + const QString expected = "QMargins(10, 11, 12, 13)"; + QString result; + QDebug(&result).nospace() << m; + QCOMPARE(result, expected); +} +#endif + +#ifndef QT_NO_DATASTREAM // Testing QDataStream operators void tst_QMargins::dataStreamCheck() { @@ -147,6 +181,7 @@ void tst_QMargins::dataStreamCheck() QCOMPARE(marginsIn.bottom(), 6852); } } +#endif // Testing get/set functions void tst_QMargins::getSetCheckF() @@ -220,6 +255,7 @@ void tst_QMargins::operatorsF() QCOMPARE(-m3, QMarginsF(-10.3, -11.4, -12.5, -13.6)); } +#ifndef QT_NO_DATASTREAM // Testing QDataStream operators void tst_QMargins::dataStreamCheckF() { @@ -244,6 +280,102 @@ void tst_QMargins::dataStreamCheckF() QCOMPARE(marginsIn.bottom(), 4.4); } } +#endif + +#ifndef QT_NO_DEBUG_STREAM +// Testing QDebug operators +void tst_QMargins::debugStreamCheckF() +{ + QMarginsF m(10.1, 11.2, 12.3, 13.4); + const QString expected = "QMarginsF(10.1, 11.2, 12.3, 13.4)"; + QString result; + QDebug(&result).nospace() << m; + QCOMPARE(result, expected); +} +#endif + +void tst_QMargins::structuredBinding() +{ + { + QMargins m(1, 2, 3, 4); + auto [left, top, right, bottom] = m; + QCOMPARE(left, 1); + QCOMPARE(top, 2); + QCOMPARE(right, 3); + QCOMPARE(bottom, 4); + } + { + QMargins m(1, 2, 3, 4); + auto &[left, top, right, bottom] = m; + QCOMPARE(left, 1); + QCOMPARE(top, 2); + QCOMPARE(right, 3); + QCOMPARE(bottom, 4); + + left = 10; + top = 20; + right = 30; + bottom = 40; + QCOMPARE(m.left(), 10); + QCOMPARE(m.top(), 20); + QCOMPARE(m.right(), 30); + QCOMPARE(m.bottom(), 40); + } + { + QMarginsF m(1.0, 2.0, 3.0, 4.0); + auto [left, top, right, bottom] = m; + QCOMPARE(left, 1.0); + QCOMPARE(top, 2.0); + QCOMPARE(right, 3.0); + QCOMPARE(bottom, 4.0); + } + { + QMarginsF m(1.0, 2.0, 3.0, 4.0); + auto &[left, top, right, bottom] = m; + QCOMPARE(left, 1.0); + QCOMPARE(top, 2.0); + QCOMPARE(right, 3.0); + QCOMPARE(bottom, 4.0); + + left = 10.0; + top = 20.0; + right = 30.0; + bottom = 40.0; + QCOMPARE(m.left(), 10.0); + QCOMPARE(m.top(), 20.0); + QCOMPARE(m.right(), 30.0); + QCOMPARE(m.bottom(), 40.0); + } +} + +void tst_QMargins::toMarginsF_data() +{ + QTest::addColumn<QMargins>("input"); + QTest::addColumn<QMarginsF>("result"); + + auto row = [](int x1, int y1, int x2, int y2) { + QTest::addRow("(%d, %d, %d, %d)", x1, y1, x2, y2) + << QMargins(x1, y1, x2, y2) << QMarginsF(x1, y1, x2, y2); + }; + constexpr std::array samples = {-1, 0, 1}; + for (int x1 : samples) { + for (int y1 : samples) { + for (int x2 : samples) { + for (int y2 : samples) { + row(x1, y1, x2, y2); + } + } + } + } +} + +void tst_QMargins::toMarginsF() +{ + QFETCH(const QMargins, input); + QFETCH(const QMarginsF, result); + + QCOMPARE(input.toMarginsF(), result); +} QTEST_APPLESS_MAIN(tst_QMargins) #include "tst_qmargins.moc" diff --git a/tests/auto/corelib/tools/qmessageauthenticationcode/CMakeLists.txt b/tests/auto/corelib/tools/qmessageauthenticationcode/CMakeLists.txt new file mode 100644 index 0000000000..a21481b7ba --- /dev/null +++ b/tests/auto/corelib/tools/qmessageauthenticationcode/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qmessageauthenticationcode Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmessageauthenticationcode LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qmessageauthenticationcode + SOURCES + tst_qmessageauthenticationcode.cpp +) diff --git a/tests/auto/corelib/tools/qmessageauthenticationcode/qmessageauthenticationcode.pro b/tests/auto/corelib/tools/qmessageauthenticationcode/qmessageauthenticationcode.pro deleted file mode 100644 index a62b702f22..0000000000 --- a/tests/auto/corelib/tools/qmessageauthenticationcode/qmessageauthenticationcode.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qmessageauthenticationcode -QT = core testlib -SOURCES = tst_qmessageauthenticationcode.cpp - diff --git a/tests/auto/corelib/tools/qmessageauthenticationcode/tst_qmessageauthenticationcode.cpp b/tests/auto/corelib/tools/qmessageauthenticationcode/tst_qmessageauthenticationcode.cpp index 2f8052fd4a..9e94ad77e9 100644 --- a/tests/auto/corelib/tools/qmessageauthenticationcode/tst_qmessageauthenticationcode.cpp +++ b/tests/auto/corelib/tools/qmessageauthenticationcode/tst_qmessageauthenticationcode.cpp @@ -1,47 +1,75 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Ruslan Nigmatullin <euroelessar@yandex.ru> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2013 Ruslan Nigmatullin <euroelessar@yandex.ru> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QCoreApplication> -#include <QtTest/QtTest> +#include <QTest> +#include <QCryptographicHash> +#include <QMessageAuthenticationCode> +#include <QBuffer> class tst_QMessageAuthenticationCode : public QObject { Q_OBJECT private slots: + void repeated_setKey_data(); + void repeated_setKey(); void result_data(); void result(); void result_incremental_data(); void result_incremental(); + void addData_overloads_data(); + void addData_overloads(); + void move(); + void swap(); }; Q_DECLARE_METATYPE(QCryptographicHash::Algorithm) +void tst_QMessageAuthenticationCode::repeated_setKey_data() +{ + using A = QCryptographicHash::Algorithm; + QTest::addColumn<A>("algo"); + + const auto me = QMetaEnum::fromType<A>(); + for (int i = 0, value; (value = me.value(i)) != -1; ++i) + QTest::addRow("%s", me.key(i)) << A(value); +} + +void tst_QMessageAuthenticationCode::repeated_setKey() +{ + QFETCH(const QCryptographicHash::Algorithm, algo); + + if (!QCryptographicHash::supportsAlgorithm(algo)) + QSKIP("QCryptographicHash doesn't support this algorithm"); + + // GIVEN: two long keys, so we're sure the key needs to be hashed in order + // to fit into the hash algorithm's block + + static const QByteArray key1(1024, 'a'); + static const QByteArray key2(2048, 'b'); + + // WHEN: processing the same message + + QMessageAuthenticationCode macX(algo); + QMessageAuthenticationCode mac1(algo, key1); + QMessageAuthenticationCode mac2(algo, key2); + + const auto check = [](QMessageAuthenticationCode &mac) { + mac.addData("This is nonsense, ignore it, please."); + return mac.result(); + }; + + macX.setKey(key1); + QCOMPARE(check(macX), check(mac1)); + + // THEN: the result does not depend on whether a new QMAC instance was used + // or an old one re-used (iow: setKey() reset()s) + + macX.setKey(key2); + QCOMPARE(check(macX), check(mac2)); +} + void tst_QMessageAuthenticationCode::result_data() { QTest::addColumn<QCryptographicHash::Algorithm>("algo"); @@ -62,6 +90,15 @@ void tst_QMessageAuthenticationCode::result_data() << QByteArray() << QByteArray() << QByteArray::fromHex("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"); + QTest::newRow("sha384-empty") << QCryptographicHash::Sha384 << QByteArray() << QByteArray() + << QByteArray::fromHex( + "6c1f2ee938fad2e24bd91298474382ca218c75db3d83e114b3d43" + "67776d14d3551289e75e8209cd4b792302840234adc"); + QTest::newRow("sha512-empty") + << QCryptographicHash::Sha512 << QByteArray() << QByteArray() + << QByteArray::fromHex( + "b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b" + "327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d47"); // Some not-empty QTest::newRow("md5") << QCryptographicHash::Md5 @@ -76,6 +113,17 @@ void tst_QMessageAuthenticationCode::result_data() << QByteArray("key") << QByteArray("The quick brown fox jumps over the lazy dog") << QByteArray::fromHex("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"); + QTest::newRow("sha384") << QCryptographicHash::Sha384 << QByteArray("key") + << QByteArray("The quick brown fox jumps over the lazy dog") + << QByteArray::fromHex( + "d7f4727e2c0b39ae0f1e40cc96f60242d5b7801841cea6fc592c5d3e1ae" + "50700582a96cf35e1e554995fe4e03381c237"); + QTest::newRow("sha512") + << QCryptographicHash::Sha512 << QByteArray("key") + << QByteArray("The quick brown fox jumps over the lazy dog") + << QByteArray::fromHex( + "b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549f" + "7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a"); // Some from rfc-2104 QTest::newRow("rfc-md5-1") << QCryptographicHash::Md5 @@ -99,11 +147,13 @@ void tst_QMessageAuthenticationCode::result() QFETCH(QByteArray, message); QFETCH(QByteArray, code); - QMessageAuthenticationCode mac(algo); - mac.setKey(key); + QMessageAuthenticationCode mac(algo, key); mac.addData(message); - QByteArray result = mac.result(); + QByteArrayView resultView = mac.resultView(); + + QCOMPARE(resultView, code); + const auto result = QMessageAuthenticationCode::hash(message, key, algo); QCOMPARE(result, code); } @@ -119,20 +169,98 @@ void tst_QMessageAuthenticationCode::result_incremental() QFETCH(QByteArray, message); QFETCH(QByteArray, code); - int index = message.length() / 2; + int index = message.size() / 2; QByteArray leftPart(message.mid(0, index)); QByteArray rightPart(message.mid(index)); QCOMPARE(leftPart + rightPart, message); - QMessageAuthenticationCode mac(algo); - mac.setKey(key); + QMessageAuthenticationCode mac(algo, key); mac.addData(leftPart); mac.addData(rightPart); - QByteArray result = mac.result(); + QByteArrayView result = mac.resultView(); QCOMPARE(result, code); } +void tst_QMessageAuthenticationCode::addData_overloads_data() +{ + result_data(); +} + +void tst_QMessageAuthenticationCode::addData_overloads() +{ + QFETCH(QCryptographicHash::Algorithm, algo); + QFETCH(QByteArray, key); + QFETCH(QByteArray, message); + QFETCH(QByteArray, code); + + // overload using const char* and length + { + QMessageAuthenticationCode mac(algo); + mac.setKey(key); + mac.addData(message.constData(), message.size()); + QByteArrayView result = mac.resultView(); + + QCOMPARE(result, code); + } + + // overload using QIODevice + { + QBuffer buffer(&message); + buffer.open(QIODevice::ReadOnly); + QMessageAuthenticationCode mac(algo); + mac.setKey(key); + QVERIFY(mac.addData(&buffer)); + QByteArrayView result = mac.resultView(); + buffer.close(); + + QCOMPARE(result, code); + } +} + +void tst_QMessageAuthenticationCode::move() +{ + const QByteArray key = "123"; + + QMessageAuthenticationCode src(QCryptographicHash::Sha1, key); + src.addData("a"); + + // move constructor + auto intermediary = std::move(src); + intermediary.addData("b"); + + // move assign operator + QMessageAuthenticationCode dst(QCryptographicHash::Sha256, key); + dst.addData("no effect on the end result"); + dst = std::move(intermediary); + dst.addData("c"); + + QCOMPARE(dst.resultView(), + QMessageAuthenticationCode::hash("abc", key, QCryptographicHash::Sha1)); +} + +void tst_QMessageAuthenticationCode::swap() +{ + const QByteArray key1 = "123"; + const QByteArray key2 = "abcdefg"; + + QMessageAuthenticationCode mac1(QCryptographicHash::Sha1, key1); + QMessageAuthenticationCode mac2(QCryptographicHash::Sha256, key2); + + mac1.addData("da"); + mac2.addData("te"); + + mac1.swap(mac2); + + mac2.addData("ta"); + mac1.addData("st"); + + QCOMPARE(mac2.resultView(), + QMessageAuthenticationCode::hash("data", key1, QCryptographicHash::Sha1)); + QCOMPARE(mac1.resultView(), + QMessageAuthenticationCode::hash("test", key2, QCryptographicHash::Sha256)); +} + QTEST_MAIN(tst_QMessageAuthenticationCode) #include "tst_qmessageauthenticationcode.moc" diff --git a/tests/auto/corelib/tools/qoffsetstringarray/CMakeLists.txt b/tests/auto/corelib/tools/qoffsetstringarray/CMakeLists.txt new file mode 100644 index 0000000000..d0205cfa15 --- /dev/null +++ b/tests/auto/corelib/tools/qoffsetstringarray/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qoffsetstringarray Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qoffsetstringarray LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qoffsetstringarray + SOURCES + tst_qoffsetstringarray.cpp + LIBRARIES + Qt::CorePrivate +) + +if (CLANG) + target_compile_options(tst_qoffsetstringarray + PUBLIC -fbracket-depth=512) +elseif (GCC) + # fconstexpr-depth= defaults to 512 +endif() diff --git a/tests/auto/corelib/tools/qoffsetstringarray/qoffsetstringarray.pro b/tests/auto/corelib/tools/qoffsetstringarray/qoffsetstringarray.pro deleted file mode 100644 index c8e6a8e05a..0000000000 --- a/tests/auto/corelib/tools/qoffsetstringarray/qoffsetstringarray.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qoffsetstringarray -QT = core testlib core-private -CONFIG += c++11 -CONFIG += strict_c++ -SOURCES = $$PWD/tst_qoffsetstringarray.cpp diff --git a/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp b/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp index dfa0450b18..dbb24e7af4 100644 --- a/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp +++ b/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <private/qoffsetstringarray_p.h> @@ -38,6 +13,7 @@ class tst_QOffsetStringArray : public QObject private slots: void init(); void access(); + void contains(); }; @@ -46,8 +22,7 @@ constexpr const auto messages = qOffsetStringArray( "level - 1", "level - 2", "level - 3", - "level - 4", - "" + "level - 4" ); constexpr const auto messages257 = qOffsetStringArray( @@ -90,19 +65,17 @@ constexpr const auto messagesBigOffsets = qOffsetStringArray( void tst_QOffsetStringArray::init() { - static_assert(messages.sizeString == 51, "message.sizeString"); - static_assert(messages.sizeOffsets == 6, "message.sizeOffsets"); - static_assert(std::is_same<decltype(messages)::Type, quint8>::value, "messages::Type != quint8"); - - static_assert(messages257.sizeOffsets == 257, "messages257.sizeOffsets"); - static_assert(messages257.sizeString == 260, "messages257.sizeString"); - static_assert(std::is_same<decltype(messages257)::Type, quint16>::value, - "messages257::Type != quint16"); - - static_assert(messagesBigOffsets.sizeOffsets == 4, "messagesBigOffsets.sizeOffsets"); - static_assert(messagesBigOffsets.sizeString == 364, "messagesBigOffsets.sizeString"); - static_assert(std::is_same<decltype(messagesBigOffsets)::Type, quint16>::value, - "messagesBigOffsets::Type != quint16"); + static_assert(messages.m_string.size() == 50); + static_assert(messages.m_offsets.size() == 6); + static_assert(std::is_same_v<decltype(messages.m_offsets)::value_type, quint8>); + + static_assert(messages257.m_offsets.size() == 258); + static_assert(messages257.m_string.size() == 260); + static_assert(std::is_same_v<decltype(messages257.m_offsets)::value_type, quint16>); + + static_assert(messagesBigOffsets.m_offsets.size() == 5); + static_assert(messagesBigOffsets.m_string.size() == 364); + static_assert(std::is_same_v<decltype(messagesBigOffsets.m_offsets)::value_type, quint16>); } void tst_QOffsetStringArray::access() @@ -112,10 +85,21 @@ void tst_QOffsetStringArray::access() QCOMPARE(messages[2], "level - 2"); QCOMPARE(messages[3], "level - 3"); QCOMPARE(messages[4], "level - 4"); + // out of bounds returns empty strings: QCOMPARE(messages[5], ""); QCOMPARE(messages[6], ""); } +void tst_QOffsetStringArray::contains() +{ + QVERIFY(!messages.contains("")); + QVERIFY( messages.contains("level - 0")); + std::string l2 = "level - 2"; // make sure we don't compare pointer values + QVERIFY( messages.contains(l2)); + QByteArray L4 = "Level - 4"; + QVERIFY( messages.contains(L4, Qt::CaseInsensitive)); + QVERIFY(!messages.contains(L4, Qt::CaseSensitive)); +} QTEST_APPLESS_MAIN(tst_QOffsetStringArray) #include "tst_qoffsetstringarray.moc" diff --git a/tests/auto/corelib/tools/qpair/CMakeLists.txt b/tests/auto/corelib/tools/qpair/CMakeLists.txt new file mode 100644 index 0000000000..2dd048e015 --- /dev/null +++ b/tests/auto/corelib/tools/qpair/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qpair Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qpair LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qpair + SOURCES + tst_qpair.cpp +) + +## Scopes: +##################################################################### diff --git a/tests/auto/corelib/tools/qpair/qpair.pro b/tests/auto/corelib/tools/qpair/qpair.pro deleted file mode 100644 index d684a24a57..0000000000 --- a/tests/auto/corelib/tools/qpair/qpair.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -TARGET = tst_qpair -QT = core testlib -SOURCES = tst_qpair.cpp - -# Force C++17 if available (needed due to Q_COMPILER_DEDUCTION_GUIDES) -contains(QT_CONFIG, c++1z): CONFIG += c++1z diff --git a/tests/auto/corelib/tools/qpair/tst_qpair.cpp b/tests/auto/corelib/tools/qpair/tst_qpair.cpp index 3c972329bc..0c9d87bb01 100644 --- a/tests/auto/corelib/tools/qpair/tst_qpair.cpp +++ b/tests/auto/corelib/tools/qpair/tst_qpair.cpp @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <QPair> #include <QSize> @@ -36,18 +11,19 @@ class tst_QPair : public QObject Q_OBJECT private Q_SLOTS: void pairOfReferences(); + void structuredBindings(); void testConstexpr(); void testConversions(); void taskQTBUG_48780_pairContainingCArray(); - void testDeducationRules(); + void testDeductionRules(); }; -class C { C() {} char _[4]; }; -class M { M() {} char _[4]; }; -class P { char _[4]; }; +class C { C() {} ~C() {} Q_DECL_UNUSED_MEMBER char _[4]; }; +class M { M() {} Q_DECL_UNUSED_MEMBER char _[4]; }; +class P { Q_DECL_UNUSED_MEMBER char _[4]; }; QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(M, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(M, Q_RELOCATABLE_TYPE); Q_DECLARE_TYPEINFO(P, Q_PRIMITIVE_TYPE); QT_END_NAMESPACE @@ -62,35 +38,34 @@ typedef QPair<P,C> QPairPC; typedef QPair<P,M> QPairPM; typedef QPair<P,P> QPairPP; -Q_STATIC_ASSERT( QTypeInfo<QPairCC>::isComplex); -Q_STATIC_ASSERT( QTypeInfo<QPairCC>::isStatic ); +static_assert( QTypeInfo<QPairCC>::isComplex); +static_assert( !QTypeInfo<QPairCC>::isRelocatable ); -Q_STATIC_ASSERT( QTypeInfo<QPairCM>::isComplex); -Q_STATIC_ASSERT( QTypeInfo<QPairCM>::isStatic ); +static_assert( QTypeInfo<QPairCM>::isComplex); +static_assert( !QTypeInfo<QPairCM>::isRelocatable ); -Q_STATIC_ASSERT( QTypeInfo<QPairCP>::isComplex); -Q_STATIC_ASSERT( QTypeInfo<QPairCP>::isStatic ); +static_assert( QTypeInfo<QPairCP>::isComplex); +static_assert( !QTypeInfo<QPairCP>::isRelocatable ); -Q_STATIC_ASSERT( QTypeInfo<QPairMC>::isComplex); -Q_STATIC_ASSERT( QTypeInfo<QPairMC>::isStatic ); +static_assert( QTypeInfo<QPairMC>::isComplex); +static_assert( !QTypeInfo<QPairMC>::isRelocatable ); -Q_STATIC_ASSERT( QTypeInfo<QPairMM>::isComplex); -Q_STATIC_ASSERT(!QTypeInfo<QPairMM>::isStatic ); +static_assert( QTypeInfo<QPairMM>::isComplex); +static_assert( QTypeInfo<QPairMM>::isRelocatable ); -Q_STATIC_ASSERT( QTypeInfo<QPairMP>::isComplex); -Q_STATIC_ASSERT(!QTypeInfo<QPairMP>::isStatic ); +static_assert( QTypeInfo<QPairMP>::isComplex); +static_assert( QTypeInfo<QPairMP>::isRelocatable ); -Q_STATIC_ASSERT( QTypeInfo<QPairPC>::isComplex); -Q_STATIC_ASSERT( QTypeInfo<QPairPC>::isStatic ); +static_assert( QTypeInfo<QPairPC>::isComplex); +static_assert( !QTypeInfo<QPairPC>::isRelocatable ); -Q_STATIC_ASSERT( QTypeInfo<QPairPM>::isComplex); -Q_STATIC_ASSERT(!QTypeInfo<QPairPM>::isStatic ); +static_assert( QTypeInfo<QPairPM>::isComplex); +static_assert( QTypeInfo<QPairPM>::isRelocatable ); -Q_STATIC_ASSERT(!QTypeInfo<QPairPP>::isComplex); -Q_STATIC_ASSERT(!QTypeInfo<QPairPP>::isStatic ); +static_assert(!QTypeInfo<QPairPP>::isComplex); +static_assert( QTypeInfo<QPairPP>::isRelocatable ); -Q_STATIC_ASSERT(!QTypeInfo<QPairPP>::isDummy ); -Q_STATIC_ASSERT(!QTypeInfo<QPairPP>::isPointer); +static_assert(!std::is_pointer_v<QPairPP>); void tst_QPair::pairOfReferences() @@ -122,17 +97,67 @@ void tst_QPair::pairOfReferences() QCOMPARE(p.second, QLatin1String("World")); } +void tst_QPair::structuredBindings() +{ + using PV = QPair<int, QString>; + using PR = QPair<int&, const QString&>; + + { + PV pv = {42, "Hello"}; + PR pr = {pv.first, pv.second}; + + auto [fv, sv] = pv; + + fv = 24; + sv = "World"; + QCOMPARE(fv, 24); + QCOMPARE(sv, "World"); + QCOMPARE(pv.first, 42); + QCOMPARE(pv.second, "Hello"); + + auto [fr, sr] = pr; + + fr = 2424; + // sr = "World"; // const + QCOMPARE(fr, 2424); + QCOMPARE(pv.first, 2424); + } + + { + PV pv = {42, "Hello"}; + PR pr = {pv.first, pv.second}; + + auto& [fv, sv] = pv; + + fv = 24; + sv = "World"; + QCOMPARE(fv, 24); + QCOMPARE(sv, "World"); + QCOMPARE(pv.first, 24); + QCOMPARE(pv.second, "World"); + + auto& [fr, sr] = pr; + + fr = 4242; + //sr = "2World"; // const + + QCOMPARE(fr, 4242); + QCOMPARE(pr.first, 4242); + QCOMPARE(pv.first, 4242); + } +} + void tst_QPair::testConstexpr() { - Q_CONSTEXPR QPair<int, double> pID = qMakePair(0, 0.0); + constexpr QPair<int, double> pID = qMakePair(0, 0.0); Q_UNUSED(pID); - Q_CONSTEXPR QPair<double, double> pDD = qMakePair(0.0, 0.0); - Q_CONSTEXPR QPair<double, double> pDD2 = qMakePair(0, 0.0); // involes (rvalue) conversion ctor - Q_CONSTEXPR bool equal = pDD2 == pDD; + constexpr QPair<double, double> pDD = qMakePair(0.0, 0.0); + constexpr QPair<double, double> pDD2 = qMakePair(0, 0.0); // involes (rvalue) conversion ctor + constexpr bool equal = pDD2 == pDD; QVERIFY(equal); - Q_CONSTEXPR QPair<QSize, int> pSI = qMakePair(QSize(4, 5), 6); + constexpr QPair<QSize, int> pSI = qMakePair(QSize(4, 5), 6); Q_UNUSED(pSI); } @@ -203,9 +228,9 @@ void tst_QPair::taskQTBUG_48780_pairContainingCArray() Q_UNUSED(pair); } -void tst_QPair::testDeducationRules() +void tst_QPair::testDeductionRules() { -#if defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201606 +#if defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201907L QPair p1{1, 2}; static_assert(std::is_same<decltype(p1)::first_type, decltype(1)>::value); static_assert(std::is_same<decltype(p1)::second_type, decltype(2)>::value); @@ -224,7 +249,7 @@ void tst_QPair::testDeducationRules() QCOMPARE(p3.first, "string"); QCOMPARE(p3.second, 2); #else - QSKIP("Unsupported"); + QSKIP("Unsupported (requires C++20's CTAD for aliases)"); #endif } diff --git a/tests/auto/corelib/tools/qpoint/CMakeLists.txt b/tests/auto/corelib/tools/qpoint/CMakeLists.txt new file mode 100644 index 0000000000..f1402d8815 --- /dev/null +++ b/tests/auto/corelib/tools/qpoint/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qpoint Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qpoint LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qpoint + SOURCES + tst_qpoint.cpp +) diff --git a/tests/auto/corelib/tools/qpoint/qpoint.pro b/tests/auto/corelib/tools/qpoint/qpoint.pro deleted file mode 100644 index 8321d08fe0..0000000000 --- a/tests/auto/corelib/tools/qpoint/qpoint.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qpoint -QT = core testlib -SOURCES = tst_qpoint.cpp diff --git a/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp b/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp index f25492d2db..7fea787131 100644 --- a/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp +++ b/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp @@ -1,35 +1,35 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QPoint> +#ifdef QVARIANT_H +# error "This test requires qpoint.h to not include qvariant.h" +#endif + +// don't assume <type_traits> +template <typename T, typename U> +constexpr inline bool my_is_same_v = false; +template <typename T> +constexpr inline bool my_is_same_v<T, T> = true; + +#define CHECK(cvref) \ + static_assert(my_is_same_v<decltype(get<0>(std::declval<QPoint cvref >())), int cvref >); \ + static_assert(my_is_same_v<decltype(get<1>(std::declval<QPoint cvref >())), int cvref >) + +CHECK(&); +CHECK(const &); +CHECK(&&); +CHECK(const &&); + +#undef CHECK + +#include <QTest> +#include <QBuffer> #include <qpoint.h> +#include <array> + class tst_QPoint : public QObject { Q_OBJECT @@ -44,6 +44,9 @@ private slots: void transposed(); + void toPointF_data(); + void toPointF(); + void rx(); void ry(); @@ -75,6 +78,8 @@ private slots: void stream_data(); void stream(); #endif + + void structuredBinding(); }; void tst_QPoint::isNull() @@ -128,6 +133,30 @@ void tst_QPoint::getSet() QCOMPARE(point.y(), i); } +void tst_QPoint::toPointF_data() +{ + QTest::addColumn<QPoint>("input"); + QTest::addColumn<QPointF>("result"); + + auto row = [](int x, int y) { + QTest::addRow("(%d, %d)", x, y) << QPoint(x, y) << QPointF(x, y); + }; + constexpr std::array samples = {-1, 0, 1}; + for (int x : samples) { + for (int y : samples) { + row(x, y); + } + } +} + +void tst_QPoint::toPointF() +{ + QFETCH(const QPoint, input); + QFETCH(const QPointF, result); + + QCOMPARE(input.toPointF(), result); +} + void tst_QPoint::transposed() { QCOMPARE(QPoint(1, 2).transposed(), QPoint(2, 1)); @@ -346,6 +375,9 @@ void tst_QPoint::operator_eq() QCOMPARE(equal, expectEqual); bool notEqual = point1 != point2; QCOMPARE(notEqual, !expectEqual); + + if (equal) + QCOMPARE(qHash(point1), qHash(point2)); } #ifndef QT_NO_DATASTREAM @@ -378,5 +410,62 @@ void tst_QPoint::stream() } #endif +void tst_QPoint::structuredBinding() +{ + { + QPoint p(1, 2); + auto [x, y] = p; + QCOMPARE(x, 1); + QCOMPARE(y, 2); + + p.setX(42); + QCOMPARE(x, 1); + QCOMPARE(y, 2); + + p.setY(-123); + QCOMPARE(x, 1); + QCOMPARE(y, 2); + } + { + QPoint p(1, 2); + + auto &[x, y] = p; + QCOMPARE(x, 1); + QCOMPARE(y, 2); + + x = 42; + QCOMPARE(x, 42); + QCOMPARE(p.x(), 42); + QCOMPARE(p.rx(), 42); + QCOMPARE(y, 2); + QCOMPARE(p.y(), 2); + QCOMPARE(p.ry(), 2); + + y = -123; + QCOMPARE(x, 42); + QCOMPARE(p.x(), 42); + QCOMPARE(p.rx(), 42); + QCOMPARE(y, -123); + QCOMPARE(p.y(), -123); + QCOMPARE(p.ry(), -123); + + p.setX(0); + QCOMPARE(x, 0); + QCOMPARE(p.x(), 0); + QCOMPARE(p.rx(), 0); + QCOMPARE(y, -123); + QCOMPARE(p.y(), -123); + QCOMPARE(p.ry(), -123); + + p.ry() = 10; + QCOMPARE(x, 0); + QCOMPARE(p.x(), 0); + QCOMPARE(p.rx(), 0); + QCOMPARE(y, 10); + QCOMPARE(p.y(), 10); + QCOMPARE(p.ry(), 10); + } +} + QTEST_MAIN(tst_QPoint) #include "tst_qpoint.moc" diff --git a/tests/auto/corelib/tools/qpointf/CMakeLists.txt b/tests/auto/corelib/tools/qpointf/CMakeLists.txt new file mode 100644 index 0000000000..16e5a9036a --- /dev/null +++ b/tests/auto/corelib/tools/qpointf/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qpointf Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qpointf LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qpointf + SOURCES + tst_qpointf.cpp +) diff --git a/tests/auto/corelib/tools/qpointf/qpointf.pro b/tests/auto/corelib/tools/qpointf/qpointf.pro deleted file mode 100644 index 5715b95d96..0000000000 --- a/tests/auto/corelib/tools/qpointf/qpointf.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qpointf -QT = core testlib -SOURCES = tst_qpointf.cpp diff --git a/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp b/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp index e78a8e3082..392c22c70a 100644 --- a/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp +++ b/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp @@ -1,32 +1,30 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QPointF> +#ifdef QVARIANT_H +# error "This test requires qpoint.h to not include qvariant.h" +#endif + +// don't assume <type_traits> +template <typename T, typename U> +constexpr inline bool my_is_same_v = false; +template <typename T> +constexpr inline bool my_is_same_v<T, T> = true; + +#define CHECK(cvref) \ + static_assert(my_is_same_v<decltype(get<0>(std::declval<QPointF cvref >())), qreal cvref >); \ + static_assert(my_is_same_v<decltype(get<1>(std::declval<QPointF cvref >())), qreal cvref >) + +CHECK(&); +CHECK(const &); +CHECK(&&); +CHECK(const &&); + +#undef CHECK + +#include <QTest> +#include <QBuffer> #include <qpoint.h> @@ -86,6 +84,8 @@ private slots: void stream(); #endif + void structuredBinding(); + private: const qreal QREAL_MIN; const qreal QREAL_MAX; @@ -381,7 +381,7 @@ void tst_QPointF::toPoint_data() QTest::newRow("(0.0, 0.0) ==> (0, 0)") << QPointF(0, 0) << QPoint(0, 0); QTest::newRow("(0.5, 0.5) ==> (1, 1)") << QPointF(0.5, 0.5) << QPoint(1, 1); - QTest::newRow("(-0.5, -0.5) ==> (0, 0)") << QPointF(-0.5, -0.5) << QPoint(0, 0); + QTest::newRow("(-0.5, -0.5) ==> (-1, -1)") << QPointF(-0.5, -0.5) << QPoint(-1, -1); } void tst_QPointF::toPoint() @@ -462,5 +462,63 @@ void tst_QPointF::compare() QVERIFY(QPointF(1.9543e-14, -32.0) == QPointF(0.0, -32.0)); } + +void tst_QPointF::structuredBinding() +{ + { + QPointF p(1.5, 2.25); + auto [x, y] = p; + QCOMPARE(x, 1.5); + QCOMPARE(y, 2.25); + + p.setX(42); + QCOMPARE(x, 1.5); + QCOMPARE(y, 2.25); + + p.setY(-123); + QCOMPARE(x, 1.5); + QCOMPARE(y, 2.25); + } + { + QPointF p(1.5, 2.25); + + auto &[x, y] = p; + QCOMPARE(x, 1.5); + QCOMPARE(y, 2.25); + + x = 42.0; + QCOMPARE(x, 42.0); + QCOMPARE(p.x(), 42.0); + QCOMPARE(p.rx(), 42.0); + QCOMPARE(y, 2.25); + QCOMPARE(p.y(), 2.25); + QCOMPARE(p.ry(), 2.25); + + y = -123.5; + QCOMPARE(x, 42.0); + QCOMPARE(p.x(), 42.0); + QCOMPARE(p.rx(), 42.0); + QCOMPARE(y, -123.5); + QCOMPARE(p.y(), -123.5); + QCOMPARE(p.ry(), -123.5); + + p.setX(0.0); + QCOMPARE(x, 0.0); + QCOMPARE(p.x(), 0.0); + QCOMPARE(p.rx(), 0.0); + QCOMPARE(y, -123.5); + QCOMPARE(p.y(), -123.5); + QCOMPARE(p.ry(), -123.5); + + p.ry() = 10.5; + QCOMPARE(x, 0.0); + QCOMPARE(p.x(), 0.0); + QCOMPARE(p.rx(), 0.0); + QCOMPARE(y, 10.5); + QCOMPARE(p.y(), 10.5); + QCOMPARE(p.ry(), 10.5); + } +} + QTEST_MAIN(tst_QPointF) #include "tst_qpointf.moc" diff --git a/tests/auto/corelib/tools/qqueue/CMakeLists.txt b/tests/auto/corelib/tools/qqueue/CMakeLists.txt new file mode 100644 index 0000000000..bf229eee6a --- /dev/null +++ b/tests/auto/corelib/tools/qqueue/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qqueue Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqueue LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qqueue + SOURCES + tst_qqueue.cpp +) diff --git a/tests/auto/corelib/tools/qqueue/qqueue.pro b/tests/auto/corelib/tools/qqueue/qqueue.pro deleted file mode 100644 index 55ceb65fbd..0000000000 --- a/tests/auto/corelib/tools/qqueue/qqueue.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qqueue -QT = core testlib -SOURCES = tst_qqueue.cpp diff --git a/tests/auto/corelib/tools/qqueue/tst_qqueue.cpp b/tests/auto/corelib/tools/qqueue/tst_qqueue.cpp index 6c391bd764..44d4c34768 100644 --- a/tests/auto/corelib/tools/qqueue/tst_qqueue.cpp +++ b/tests/auto/corelib/tools/qqueue/tst_qqueue.cpp @@ -1,33 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include <QtTest/QtTest> +#include <QTest> #include <qqueue.h> class tst_QQueue : public QObject diff --git a/tests/auto/corelib/tools/qrect/CMakeLists.txt b/tests/auto/corelib/tools/qrect/CMakeLists.txt new file mode 100644 index 0000000000..a02e1c33a5 --- /dev/null +++ b/tests/auto/corelib/tools/qrect/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qrect Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qrect LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qrect + SOURCES + tst_qrect.cpp +) diff --git a/tests/auto/corelib/tools/qrect/qrect.pro b/tests/auto/corelib/tools/qrect/qrect.pro deleted file mode 100644 index 8ec31db215..0000000000 --- a/tests/auto/corelib/tools/qrect/qrect.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qrect -QT = core testlib -SOURCES = tst_qrect.cpp diff --git a/tests/auto/corelib/tools/qrect/tst_qrect.cpp b/tests/auto/corelib/tools/qrect/tst_qrect.cpp index 1c2221ec29..0f3dd1a0ef 100644 --- a/tests/auto/corelib/tools/qrect/tst_qrect.cpp +++ b/tests/auto/corelib/tools/qrect/tst_qrect.cpp @@ -1,37 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <qrect.h> #include <qmargins.h> #include <limits.h> #include <qdebug.h> +#include <array> class tst_QRect : public QObject { @@ -124,6 +100,9 @@ private slots: void margins(); void marginsf(); + void toRectF_data(); + void toRectF(); + void translate_data(); void translate(); @@ -164,14 +143,17 @@ private slots: void intersectsRectF(); void containsRect_data(); void containsRect(); + void containsRectNormalized(); void containsRectF_data(); void containsRectF(); void containsPoint_data(); void containsPoint(); + void containsPointNormalized(); void containsPointF_data(); void containsPointF(); void smallRects() const; void toRect(); + void span(); }; // Used to work around some floating point precision problems. @@ -372,17 +354,20 @@ void tst_QRect::normalized_data() QTest::newRow( "LargestCoordQRect" ) << getQRectCase( LargestCoordQRect ) << getQRectCase( LargestCoordQRect ); // overflow QTest::newRow( "RandomQRect" ) << getQRectCase( RandomQRect ) << QRect( 100, 200, 11, 16 ); - QTest::newRow( "NegativeSizeQRect" ) << getQRectCase( NegativeSizeQRect ) << QRect(QPoint(-10,-10),QPoint(1,1)); + QTest::newRow( "NegativeSizeQRect" ) << getQRectCase( NegativeSizeQRect ) << QRect(-9, -9, 10, 10); QTest::newRow( "NegativePointQRect" ) << getQRectCase( NegativePointQRect ) << QRect( -10, -10, 5, 5 ); QTest::newRow( "NullQRect" ) << getQRectCase( NullQRect ) << getQRectCase( NullQRect ); QTest::newRow( "EmptyQRect" ) << getQRectCase( EmptyQRect ) << getQRectCase( EmptyQRect ); QTest::newRow( "ZeroWidth" ) << QRect(100, 200, 100, 0) << QRect(100, 200, 100, 0); + QTest::newRow( "ZeroHeight" ) << QRect(100, 200, 0, 100) << QRect(100, 200, 0, 100); // Since "NegativeSizeQRect passes, I expect both of these to pass too. // This passes, since height() returns -1 before normalization - QTest::newRow( "NegativeHeight") << QRect(QPoint(100,201), QPoint(199,199)) << QRect(QPoint(100,199), QPoint(199,201)); + QTest::newRow( "NegativeWidth") << QRect(QPoint(200,100), QSize(-1,100)) << QRect(QPoint(199,100), QSize(1,100)); + QTest::newRow( "NegativeHeight") << QRect(QPoint(100,200), QSize(100,-1)) << QRect(QPoint(100,199), QSize(100,1)); + QTest::newRow( "NegativeWidth2") << QRect(QPoint(200,100), QPoint(198,199)) << QRect(QPoint(199,100), QPoint(199,199)); // This, on the other hand height() returns 0 before normalization. - QTest::newRow( "ZeroHeight1" ) << QRect(QPoint(100,200), QPoint(199,199)) << QRect(QPoint(100,199), QPoint(199,200)); - QTest::newRow( "ZeroHeight2" ) << QRect(QPoint(263,113), QPoint(136,112)) << QRect(QPoint(136,113), QPoint(263,112)); + QTest::newRow( "ZeroHeight1" ) << QRect(QPoint(100,200), QPoint(199,199)) << QRect(QPoint(100,200), QPoint(199,199)); + QTest::newRow( "ZeroHeight2" ) << QRect(QPoint(263,113), QPoint(136,112)) << QRect(QPoint(137,113), QPoint(262,112)); } void tst_QRect::normalized() @@ -390,7 +375,6 @@ void tst_QRect::normalized() QFETCH(QRect, r); QFETCH(QRect, nr); - QEXPECT_FAIL("ZeroHeight1", "due to broken QRect definition (not possible to change, see QTBUG-22934)", Continue); QCOMPARE(r.normalized(), nr); } @@ -478,6 +462,9 @@ void tst_QRect::right() if (isLarge(r.width())) return; + // width overflow + if (r.left() < r.right() && r.width() < 0) + return; QCOMPARE(QRectF(r).right(), qreal(right+1)); } @@ -510,6 +497,9 @@ void tst_QRect::bottom() if (isLarge(r.height())) return; + // height overflow + if (r.top() < r.bottom() && r.height() < 0) + return; QCOMPARE(QRectF(r).bottom(), qreal(bottom + 1)); } @@ -2515,16 +2505,11 @@ void tst_QRect::newMoveLeft_data() { // QTest::newRow( "LargestCoordQRect_MinimumInt" ) -- Not tested as it would cause an overflow - QTest::newRow( "LargestCoordQRect_MiddleNegativeInt" ) << getQRectCase( LargestCoordQRect ) << getIntCase( MiddleNegativeInt ) - << QRect( QPoint( INT_MIN/2, INT_MIN ), QPoint(INT_MIN/2-1, INT_MAX ) ); - QTest::newRow( "LargestCoordQRect_ZeroInt" ) << getQRectCase( LargestCoordQRect ) << getIntCase( ZeroInt ) - << QRect( QPoint( 0, INT_MIN ), QPoint(-1, INT_MAX ) ); - QTest::newRow( "LargestCoordQRect_MiddlePositiveInt" ) << getQRectCase( LargestCoordQRect ) << getIntCase( MiddlePositiveInt ) - << QRect( QPoint( INT_MAX/2, INT_MIN ), QPoint(INT_MAX/2-1, INT_MAX ) ); - QTest::newRow( "LargestCoordQRect_MaximumInt" ) << getQRectCase( LargestCoordQRect ) << getIntCase( MaximumInt ) - << QRect( QPoint( INT_MAX, INT_MIN ), QPoint(INT_MAX-1, INT_MAX ) ); - QTest::newRow( "LargestCoordQRect_RandomInt" ) << getQRectCase( LargestCoordQRect ) << getIntCase( RandomInt ) - << QRect( QPoint( 4953, INT_MIN ), QPoint(4952, INT_MAX ) ); + // QTest::newRow( "LargestCoordQRect_MiddleNegativeInt" ) -- Not tested as it would cause an overflow + // QTest::newRow( "LargestCoordQRect_ZeroInt" ) -- Not tested as it would cause an overflow + // QTest::newRow( "LargestCoordQRect_MiddlePositiveInt" ) -- Not tested as it would cause an overflow + // QTest::newRow( "LargestCoordQRect_MaximumInt" ) -- Not tested as it would cause an overflow + // QTest::newRow( "LargestCoordQRect_RandomInt" ) -- Not tested as it would cause an overflow } { @@ -2684,16 +2669,11 @@ void tst_QRect::newMoveTop_data() { // QTest::newRow( "LargestCoordQRect_MinimumInt" ) -- Not tested as it would cause an overflow - QTest::newRow( "LargestCoordQRect_MiddleNegativeInt" ) << getQRectCase( LargestCoordQRect ) << getIntCase( MiddleNegativeInt ) - << QRect( QPoint(INT_MIN,INT_MIN/2), QPoint(INT_MAX,INT_MIN/2-1) ); - QTest::newRow( "LargestCoordQRect_ZeroInt" ) << getQRectCase( LargestCoordQRect ) << getIntCase( ZeroInt ) - << QRect( QPoint(INT_MIN,0), QPoint(INT_MAX,-1) ); - QTest::newRow( "LargestCoordQRect_MiddlePositiveInt" ) << getQRectCase( LargestCoordQRect ) << getIntCase( MiddlePositiveInt ) - << QRect( QPoint(INT_MIN,INT_MAX/2), QPoint(INT_MAX,INT_MAX/2-1) ); - QTest::newRow( "LargestCoordQRect_MaximumInt" ) << getQRectCase( LargestCoordQRect ) << getIntCase( MaximumInt ) - << QRect( QPoint(INT_MIN,INT_MAX), QPoint(INT_MAX,INT_MAX-1) ); - QTest::newRow( "LargestCoordQRect_RandomInt" ) << getQRectCase( LargestCoordQRect ) << getIntCase( RandomInt ) - << QRect( QPoint(INT_MIN,4953), QPoint(INT_MAX,4952) ); + // QTest::newRow( "LargestCoordQRect_MiddleNegativeInt" ) -- Not tested as it would cause an overflow + // QTest::newRow( "LargestCoordQRect_ZeroInt" ) -- Not tested as it would cause an overflow + // QTest::newRow( "LargestCoordQRect_MiddlePositiveInt" ) -- Not tested as it would cause an overflow + // QTest::newRow( "LargestCoordQRect_MaximumInt" ) -- Not tested as it would cause an overflow + // QTest::newRow( "LargestCoordQRect_RandomInt" ) -- Not tested as it would cause an overflow } { @@ -3525,6 +3505,39 @@ void tst_QRect::marginsf() QCOMPARE(a, rectangle.marginsRemoved(margins)); } +void tst_QRect::toRectF_data() +{ + QTest::addColumn<QRect>("input"); + QTest::addColumn<QRectF>("result"); + + auto row = [](int x1, int y1, int w, int h) { + // QRectF -> QRect conversion tries to maintain size(), not bottomRight(), + // so compare in (topLeft(), size()) space + QTest::addRow("((%d, %d) (%dx%d))", x1, y1, w, h) + << QRect({x1, y1}, QSize{w, h}) << QRectF(QPointF(x1, y1), QSizeF(w, h)); + }; + constexpr std::array samples = {-1, 0, 1}; + for (int x1 : samples) { + for (int y1 : samples) { + for (int w : samples) { + for (int h : samples) { + row(x1, y1, w, h); + } + } + } + } +} + +void tst_QRect::toRectF() +{ + QFETCH(const QRect, input); + QFETCH(const QRectF, result); + + QCOMPARE(result.toRect(), input); // consistency check + QCOMPARE(input.toRectF(), result); +} + + void tst_QRect::translate_data() { QTest::addColumn<QRect>("r"); @@ -3931,15 +3944,15 @@ void tst_QRect::intersectedRect_data() QTest::newRow("test 03") << QRect(0, 0, 10, 10) << QRect( 2, 2, 10, 10) << QRect(2, 2, 8, 8); QTest::newRow("test 04") << QRect(0, 0, 10, 10) << QRect(20, 20, 10, 10) << QRect(); - QTest::newRow("test 05") << QRect(9, 9, -8, -8) << QRect( 2, 2, 6, 6) << QRect(2, 2, 6, 6); - QTest::newRow("test 06") << QRect(9, 9, -8, -8) << QRect( 0, 0, 10, 10) << QRect(0, 0, 10, 10); - QTest::newRow("test 07") << QRect(9, 9, -8, -8) << QRect( 2, 2, 10, 10) << QRect(2, 2, 8, 8); - QTest::newRow("test 08") << QRect(9, 9, -8, -8) << QRect(20, 20, 10, 10) << QRect(); + QTest::newRow("test 05") << QRect(10, 10, -10, -10) << QRect( 2, 2, 6, 6) << QRect(2, 2, 6, 6); + QTest::newRow("test 06") << QRect(10, 10, -10, -10) << QRect( 0, 0, 10, 10) << QRect(0, 0, 10, 10); + QTest::newRow("test 07") << QRect(10, 10, -10, -10) << QRect( 2, 2, 10, 10) << QRect(2, 2, 8, 8); + QTest::newRow("test 08") << QRect(10, 10, -10, -10) << QRect(20, 20, 10, 10) << QRect(); - QTest::newRow("test 09") << QRect(0, 0, 10, 10) << QRect( 7, 7, -4, -4) << QRect(2, 2, 6, 6); - QTest::newRow("test 10") << QRect(0, 0, 10, 10) << QRect( 9, 9, -8, -8) << QRect(0, 0, 10, 10); - QTest::newRow("test 11") << QRect(0, 0, 10, 10) << QRect(11, 11, -8, -8) << QRect(2, 2, 8, 8); - QTest::newRow("test 12") << QRect(0, 0, 10, 10) << QRect(29, 29, -8, -8) << QRect(); + QTest::newRow("test 09") << QRect(0, 0, 10, 10) << QRect( 6, 6, -4, -4) << QRect(2, 2, 4, 4); + QTest::newRow("test 10") << QRect(0, 0, 10, 10) << QRect(10, 10, -10, -10) << QRect(0, 0, 10, 10); + QTest::newRow("test 11") << QRect(0, 0, 10, 10) << QRect(12, 12, -10, -10) << QRect(2, 2, 8, 8); + QTest::newRow("test 12") << QRect(0, 0, 10, 10) << QRect(30, 30, -10, -10) << QRect(); QTest::newRow("test 13") << QRect(0, 0, 10, 10) << QRect() << QRect(); QTest::newRow("test 14") << QRect() << QRect(0, 0, 10, 10) << QRect(); @@ -4016,15 +4029,15 @@ void tst_QRect::unitedRect_data() QTest::newRow("test 03") << QRect(0, 0, 10, 10) << QRect( 2, 2, 10, 10) << QRect(0, 0, 12, 12); QTest::newRow("test 04") << QRect(0, 0, 10, 10) << QRect(20, 20, 10, 10) << QRect(0, 0, 30, 30); - QTest::newRow("test 05") << QRect(9, 9, -8, -8) << QRect( 2, 2, 6, 6) << QRect(0, 0, 10, 10); - QTest::newRow("test 06") << QRect(9, 9, -8, -8) << QRect( 0, 0, 10, 10) << QRect(0, 0, 10, 10); - QTest::newRow("test 07") << QRect(9, 9, -8, -8) << QRect( 2, 2, 10, 10) << QRect(0, 0, 12, 12); - QTest::newRow("test 08") << QRect(9, 9, -8, -8) << QRect(20, 20, 10, 10) << QRect(0, 0, 30, 30); + QTest::newRow("test 05") << QRect(10, 10, -10, -10) << QRect( 2, 2, 6, 6) << QRect(0, 0, 10, 10); + QTest::newRow("test 06") << QRect(10, 10, -10, -10) << QRect( 0, 0, 10, 10) << QRect(0, 0, 10, 10); + QTest::newRow("test 07") << QRect(10, 10, -10, -10) << QRect( 2, 2, 10, 10) << QRect(0, 0, 12, 12); + QTest::newRow("test 08") << QRect(10, 10, -10, -10) << QRect(20, 20, 10, 10) << QRect(0, 0, 30, 30); QTest::newRow("test 09") << QRect(0, 0, 10, 10) << QRect( 7, 7, -4, -4) << QRect(0, 0, 10, 10); QTest::newRow("test 10") << QRect(0, 0, 10, 10) << QRect( 9, 9, -8, -8) << QRect(0, 0, 10, 10); - QTest::newRow("test 11") << QRect(0, 0, 10, 10) << QRect(11, 11, -8, -8) << QRect(0, 0, 12, 12); - QTest::newRow("test 12") << QRect(0, 0, 10, 10) << QRect(29, 29, -8, -8) << QRect(0, 0, 30, 30); + QTest::newRow("test 11") << QRect(0, 0, 10, 10) << QRect(12, 12, -8, -8) << QRect(0, 0, 12, 12); + QTest::newRow("test 12") << QRect(0, 0, 10, 10) << QRect(30, 30, -8, -8) << QRect(0, 0, 30, 30); QTest::newRow("test 13") << QRect() << QRect(10, 10, 10, 10) << QRect(10, 10, 10, 10); QTest::newRow("test 14") << QRect(10, 10, 10, 10) << QRect() << QRect(10, 10, 10, 10); @@ -4166,10 +4179,10 @@ void tst_QRect::containsRect_data() QTest::newRow("test 03") << QRect(0, 0, 10, 10) << QRect( 2, 2, 10, 10) << false; QTest::newRow("test 04") << QRect(0, 0, 10, 10) << QRect(20, 20, 10, 10) << false; - QTest::newRow("test 05") << QRect(9, 9, -8, -8) << QRect( 2, 2, 6, 6) << true; - QTest::newRow("test 06") << QRect(9, 9, -8, -8) << QRect( 0, 0, 10, 10) << true; - QTest::newRow("test 07") << QRect(9, 9, -8, -8) << QRect( 2, 2, 10, 10) << false; - QTest::newRow("test 08") << QRect(9, 9, -8, -8) << QRect(20, 20, 10, 10) << false; + QTest::newRow("test 05") << QRect(9, 9, -9, -9) << QRect( 2, 2, 6, 6) << true; + QTest::newRow("test 06") << QRect(9, 9, -9, -9) << QRect( 0, 0, 9, 9) << true; + QTest::newRow("test 07") << QRect(9, 9, -9, -9) << QRect( 2, 2, 9, 9) << false; + QTest::newRow("test 08") << QRect(9, 9, -9, -9) << QRect(20, 20, 10, 10) << false; QTest::newRow("test 09") << QRect(0, 0, 10, 10) << QRect( 7, 7, -4, -4) << true; QTest::newRow("test 10") << QRect(0, 0, 10, 10) << QRect( 9, 9, -8, -8) << true; @@ -4190,6 +4203,18 @@ void tst_QRect::containsRect() QVERIFY(rect1.contains(rect2) == contains); } +void tst_QRect::containsRectNormalized() +{ + QRect rect(QPoint(10, 10), QPoint(0,0)); + QRect normalized = rect.normalized(); + for (int i = -2 ; i < 12; ++i) { + for (int j = -2 ; j < 12; ++j) { + for (int k = -2 ; k <= 2; ++k) + QCOMPARE(rect.contains(QRect(i,j,k,k)), normalized.contains(QRect(i,j,k,k))); + } + } +} + void tst_QRect::containsRectF_data() { QTest::addColumn<QRectF>("rect1"); @@ -4245,18 +4270,18 @@ void tst_QRect::containsPoint_data() QTest::newRow("test 11") << QRect(0, 0, 10, 10) << QPoint( 1, 8) << true << true; QTest::newRow("test 12") << QRect(0, 0, 10, 10) << QPoint( 8, 8) << true << true; - QTest::newRow("test 13") << QRect(9, 9, -8, -8) << QPoint( 0, 0) << true << false; - QTest::newRow("test 14") << QRect(9, 9, -8, -8) << QPoint( 0, 10) << false << false; - QTest::newRow("test 15") << QRect(9, 9, -8, -8) << QPoint(10, 0) << false << false; - QTest::newRow("test 16") << QRect(9, 9, -8, -8) << QPoint(10, 10) << false << false; - QTest::newRow("test 17") << QRect(9, 9, -8, -8) << QPoint( 0, 9) << true << false; - QTest::newRow("test 18") << QRect(9, 9, -8, -8) << QPoint( 9, 0) << true << false; - QTest::newRow("test 19") << QRect(9, 9, -8, -8) << QPoint( 9, 9) << true << false; - QTest::newRow("test 20") << QRect(9, 9, -8, -8) << QPoint( 1, 0) << true << false; - QTest::newRow("test 21") << QRect(9, 9, -8, -8) << QPoint( 9, 1) << true << false; - QTest::newRow("test 22") << QRect(9, 9, -8, -8) << QPoint( 1, 1) << true << true; - QTest::newRow("test 23") << QRect(9, 9, -8, -8) << QPoint( 1, 8) << true << true; - QTest::newRow("test 24") << QRect(9, 9, -8, -8) << QPoint( 8, 8) << true << true; + QTest::newRow("test 13") << QRect(9, 9, -9, -9) << QPoint( 0, 0) << true << false; + QTest::newRow("test 14") << QRect(9, 9, -9, -9) << QPoint( 0, 9) << false << false; + QTest::newRow("test 15") << QRect(9, 9, -9, -9) << QPoint( 9, 0) << false << false; + QTest::newRow("test 16") << QRect(9, 9, -9, -9) << QPoint( 9, 9) << false << false; + QTest::newRow("test 17") << QRect(9, 9, -9, -9) << QPoint( 0, 8) << true << false; + QTest::newRow("test 18") << QRect(9, 9, -9, -9) << QPoint( 8, 0) << true << false; + QTest::newRow("test 19") << QRect(9, 9, -9, -9) << QPoint( 8, 8) << true << false; + QTest::newRow("test 20") << QRect(9, 9, -9, -9) << QPoint( 1, 0) << true << false; + QTest::newRow("test 21") << QRect(9, 9, -9, -9) << QPoint( 8, 1) << true << false; + QTest::newRow("test 22") << QRect(9, 9, -9, -9) << QPoint( 1, 1) << true << true; + QTest::newRow("test 23") << QRect(9, 9, -9, -9) << QPoint( 1, 7) << true << true; + QTest::newRow("test 24") << QRect(9, 9, -9, -9) << QPoint( 7, 7) << true << true; QTest::newRow("test 25") << QRect(-1, 1, 10, 10) << QPoint() << false << false; QTest::newRow("test 26") << QRect() << QPoint(1, 1) << false << false; @@ -4274,14 +4299,22 @@ void tst_QRect::containsPoint() QVERIFY(rect.contains(point, true) == containsProper); } +void tst_QRect::containsPointNormalized() +{ + QRect rect(QPoint(10, 10), QPoint(0,0)); + QRect normalized = rect.normalized(); + for (int i = 0 ; i < 10; ++i) { + for (int j = 0 ; j < 10; ++j) + QCOMPARE(rect.contains(QPoint(i,j)), normalized.contains(QPoint(i,j))); + } +} + void tst_QRect::containsPointF_data() { QTest::addColumn<QRectF>("rect"); QTest::addColumn<QPointF>("point"); QTest::addColumn<bool>("contains"); - QTest::newRow("test 27") << QRectF() << QPointF() << false; - QTest::newRow("test 01") << QRectF(0, 0, 10, 10) << QPointF( 0, 0) << true; QTest::newRow("test 02") << QRectF(0, 0, 10, 10) << QPointF( 0, 10) << true; QTest::newRow("test 03") << QRectF(0, 0, 10, 10) << QPointF(10, 0) << true; @@ -4340,12 +4373,12 @@ void tst_QRect::toRect() for (qreal h = 1.0; h < 2.0; h += 0.25) { const QRectF rectf(x, y, w, h); const QRectF rect = rectf.toRect(); - QVERIFY(qAbs(rect.x() - rectf.x()) < 1.0); - QVERIFY(qAbs(rect.y() - rectf.y()) < 1.0); - QVERIFY(qAbs(rect.width() - rectf.width()) < 1.0); - QVERIFY(qAbs(rect.height() - rectf.height()) < 1.0); - QVERIFY(qAbs(rect.right() - rectf.right()) < 1.0); - QVERIFY(qAbs(rect.bottom() - rectf.bottom()) < 1.0); + QVERIFY(qAbs(rect.x() - rectf.x()) <= 0.75); + QVERIFY(qAbs(rect.y() - rectf.y()) <= 0.75); + QVERIFY(qAbs(rect.width() - rectf.width()) <= 0.75); + QVERIFY(qAbs(rect.height() - rectf.height()) <= 0.75); + QVERIFY(qAbs(rect.right() - rectf.right()) <= 0.75); + QVERIFY(qAbs(rect.bottom() - rectf.bottom()) <= 0.75); const QRectF arect = rectf.toAlignedRect(); QVERIFY(qAbs(arect.x() - rectf.x()) < 1.0); @@ -4363,5 +4396,16 @@ void tst_QRect::toRect() } } +void tst_QRect::span() +{ + QCOMPARE(QRect::span(QPoint( 0, 1), QPoint(9, 10)), QRect(QPoint(0, 1), QPoint( 9, 10))); + + QCOMPARE(QRect::span(QPoint(10, 9), QPoint(1, 0)), QRect(QPoint(1, 0), QPoint(10, 9))); + + QCOMPARE(QRect::span(QPoint(10, 1), QPoint(0, 9)), QRect(QPoint(0, 1), QPoint(10, 9))); + + QCOMPARE(QRect::span(QPoint( 1, 10), QPoint(9, 0)), QRect(QPoint(1, 0), QPoint( 9, 10))); +} + QTEST_MAIN(tst_QRect) #include "tst_qrect.moc" diff --git a/tests/auto/corelib/tools/qringbuffer/CMakeLists.txt b/tests/auto/corelib/tools/qringbuffer/CMakeLists.txt new file mode 100644 index 0000000000..cfb7c6f461 --- /dev/null +++ b/tests/auto/corelib/tools/qringbuffer/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qringbuffer Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qringbuffer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qringbuffer + SOURCES + tst_qringbuffer.cpp + LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/tools/qringbuffer/qringbuffer.pro b/tests/auto/corelib/tools/qringbuffer/qringbuffer.pro deleted file mode 100644 index c63544035a..0000000000 --- a/tests/auto/corelib/tools/qringbuffer/qringbuffer.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qringbuffer -QT = core-private testlib -SOURCES = tst_qringbuffer.cpp diff --git a/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp b/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp index e355a7fcfb..c7b79cfae1 100644 --- a/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp +++ b/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp @@ -1,35 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QBuffer> +#include <QVarLengthArray> #include <private/qringbuffer_p.h> -#include <qvector.h> +#include <qlist.h> class tst_QRingBuffer : public QObject { @@ -37,6 +14,7 @@ class tst_QRingBuffer : public QObject private slots: void constructing(); void usingInVector(); + void usingInVarLengthArray(); void readPointerAtPositionWriteRead(); void readPointerAtPositionEmptyRead(); void readPointerAtPositionWithHead(); @@ -82,10 +60,20 @@ void tst_QRingBuffer::constructing() void tst_QRingBuffer::usingInVector() { QRingBuffer ringBuffer; - QVector<QRingBuffer> buffers; + std::vector<QRingBuffer> buffers; + + ringBuffer.reserve(5); + buffers.push_back(std::move(ringBuffer)); + QCOMPARE(buffers[0].size(), Q_INT64_C(5)); +} + +void tst_QRingBuffer::usingInVarLengthArray() +{ + QRingBuffer ringBuffer; + QVarLengthArray<QRingBuffer, 42> buffers; ringBuffer.reserve(5); - buffers.append(ringBuffer); + buffers.push_back(std::move(ringBuffer)); QCOMPARE(buffers[0].size(), Q_INT64_C(5)); } diff --git a/tests/auto/corelib/tools/qscopedpointer/CMakeLists.txt b/tests/auto/corelib/tools/qscopedpointer/CMakeLists.txt new file mode 100644 index 0000000000..7bfcfdebbf --- /dev/null +++ b/tests/auto/corelib/tools/qscopedpointer/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qscopedpointer Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qscopedpointer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qscopedpointer + SOURCES + tst_qscopedpointer.cpp +) diff --git a/tests/auto/corelib/tools/qscopedpointer/qscopedpointer.pro b/tests/auto/corelib/tools/qscopedpointer/qscopedpointer.pro deleted file mode 100644 index 5248ad5528..0000000000 --- a/tests/auto/corelib/tools/qscopedpointer/qscopedpointer.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qscopedpointer -QT = core testlib -SOURCES = tst_qscopedpointer.cpp diff --git a/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp b/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp index ea94cc2999..3468c97f42 100644 --- a/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp +++ b/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <QtCore/QScopedPointer> /*! @@ -61,6 +36,11 @@ private Q_SLOTS: void comparison(); void array(); // TODO instanciate on const object + + // Tests for deprecated APIs +#if QT_DEPRECATED_SINCE(6, 1) + void deprecatedTake(); +#endif // QT_DEPRECATED_SINCE(6, 1) }; void tst_QScopedPointer::defaultConstructor() @@ -160,7 +140,7 @@ public: class SubClass : public AbstractClass { public: - virtual int member() const + virtual int member() const override { return 5; } @@ -252,7 +232,7 @@ void tst_QScopedPointer::negationOperatorSignature() !p; /* The return value should be bool. */ - static_cast<bool>(!p); + Q_UNUSED(static_cast<bool>(!p)); } void tst_QScopedPointer::operatorBool() @@ -302,7 +282,7 @@ void tst_QScopedPointer::isNullSignature() const QScopedPointer<int> p(new int(69)); /* The signature should be const and return bool. */ - static_cast<bool>(p.isNull()); + Q_UNUSED(static_cast<bool>(p.isNull())); } void tst_QScopedPointer::objectSize() @@ -367,53 +347,63 @@ void scopedPointerComparisonTest(const A1 &a1, const A2 &a2, const B &b) QVERIFY(a2 != b); } +// tst_QScopedPointer::comparison creates two QScopedPointers referring to the +// same memory. This will lead to double-deletion error during cleanup if we +// use a default QScopedPointer{Array}Deleter. This DummyDeleter does nothing, +// so we can safely reference the same memory from multiple QScopedPointer +// instances, and manage the memory manually. +// That is fine for the comparison() test, because its goal is to check the +// object's (in)equality, not the memory management +struct DummyDeleter +{ + static inline void cleanup(RefCounted *) noexcept {} + void operator()(RefCounted *pointer) const noexcept + { + cleanup(pointer); + } +}; + void tst_QScopedPointer::comparison() { QCOMPARE( RefCounted::instanceCount.loadRelaxed(), 0 ); { - RefCounted *a = new RefCounted; - RefCounted *b = new RefCounted; + auto a = std::make_unique<RefCounted>(); + auto b = std::make_unique<RefCounted>(); QCOMPARE( RefCounted::instanceCount.loadRelaxed(), 2 ); - QScopedPointer<RefCounted> pa1(a); - QScopedPointer<RefCounted> pa2(a); - QScopedPointer<RefCounted> pb(b); + QScopedPointer<RefCounted, DummyDeleter> pa1(a.get()); + QScopedPointer<RefCounted, DummyDeleter> pa2(a.get()); + QScopedPointer<RefCounted, DummyDeleter> pb(b.get()); scopedPointerComparisonTest(pa1, pa1, pb); scopedPointerComparisonTest(pa2, pa2, pb); scopedPointerComparisonTest(pa1, pa2, pb); - pa2.take(); - QCOMPARE( RefCounted::instanceCount.loadRelaxed(), 2 ); } QCOMPARE( RefCounted::instanceCount.loadRelaxed(), 0 ); { - RefCounted *a = new RefCounted[42]; - RefCounted *b = new RefCounted[43]; + auto a = std::make_unique<RefCounted[]>(42); + auto b = std::make_unique<RefCounted[]>(43); QCOMPARE( RefCounted::instanceCount.loadRelaxed(), 85 ); - QScopedArrayPointer<RefCounted> pa1(a); - QScopedArrayPointer<RefCounted> pa2(a); - QScopedArrayPointer<RefCounted> pb(b); + QScopedArrayPointer<RefCounted, DummyDeleter> pa1(a.get()); + QScopedArrayPointer<RefCounted, DummyDeleter> pa2(a.get()); + QScopedArrayPointer<RefCounted, DummyDeleter> pb(b.get()); scopedPointerComparisonTest(pa1, pa2, pb); - pa2.take(); - QCOMPARE( RefCounted::instanceCount.loadRelaxed(), 85 ); } QCOMPARE( RefCounted::instanceCount.loadRelaxed(), 0 ); { - // QScopedSharedPointer is an internal helper class -- it is unsupported! - RefCounted *a = new RefCounted; RefCounted *b = new RefCounted; @@ -455,6 +445,23 @@ void tst_QScopedPointer::array() QCOMPARE(instCount, RefCounted::instanceCount.loadRelaxed()); } +#if QT_DEPRECATED_SINCE(6, 1) +void tst_QScopedPointer::deprecatedTake() +{ + RefCounted *a = new RefCounted; + + QScopedPointer<RefCounted> pa1(a); + QScopedPointer<RefCounted> pa2(a); + + QCOMPARE(RefCounted::instanceCount.loadRelaxed(), 1); + + QT_IGNORE_DEPRECATIONS(pa2.take();) + + // check that pa2 holds nullptr, but the memory was not released + QVERIFY(pa2.isNull()); + QCOMPARE(RefCounted::instanceCount.loadRelaxed(), 1); +} +#endif // QT_DEPRECATED_SINCE(6, 1) QTEST_MAIN(tst_QScopedPointer) #include "tst_qscopedpointer.moc" diff --git a/tests/auto/corelib/tools/qscopedvaluerollback/CMakeLists.txt b/tests/auto/corelib/tools/qscopedvaluerollback/CMakeLists.txt new file mode 100644 index 0000000000..359a910a0a --- /dev/null +++ b/tests/auto/corelib/tools/qscopedvaluerollback/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qscopedvaluerollback Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qscopedvaluerollback LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qscopedvaluerollback + SOURCES + tst_qscopedvaluerollback.cpp +) diff --git a/tests/auto/corelib/tools/qscopedvaluerollback/qscopedvaluerollback.pro b/tests/auto/corelib/tools/qscopedvaluerollback/qscopedvaluerollback.pro deleted file mode 100644 index c9c0a029d3..0000000000 --- a/tests/auto/corelib/tools/qscopedvaluerollback/qscopedvaluerollback.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qscopedvaluerollback -QT = core testlib -SOURCES = tst_qscopedvaluerollback.cpp diff --git a/tests/auto/corelib/tools/qscopedvaluerollback/tst_qscopedvaluerollback.cpp b/tests/auto/corelib/tools/qscopedvaluerollback/tst_qscopedvaluerollback.cpp index 9b607db608..3b493b4e75 100644 --- a/tests/auto/corelib/tools/qscopedvaluerollback/tst_qscopedvaluerollback.cpp +++ b/tests/auto/corelib/tools/qscopedvaluerollback/tst_qscopedvaluerollback.cpp @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include <QtTest/QtTest> +#include <QTest> #include <QtCore/QScopedValueRollback> /*! diff --git a/tests/auto/corelib/tools/qscopeguard/CMakeLists.txt b/tests/auto/corelib/tools/qscopeguard/CMakeLists.txt new file mode 100644 index 0000000000..6f6d664554 --- /dev/null +++ b/tests/auto/corelib/tools/qscopeguard/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qscopeguard Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qscopeguard LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qscopeguard + SOURCES + tst_qscopeguard.cpp +) + +## Scopes: +##################################################################### diff --git a/tests/auto/corelib/tools/qscopeguard/qscopeguard.pro b/tests/auto/corelib/tools/qscopeguard/qscopeguard.pro deleted file mode 100644 index 070d4b077c..0000000000 --- a/tests/auto/corelib/tools/qscopeguard/qscopeguard.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qscopeguard -QT = core testlib -SOURCES = tst_qscopeguard.cpp diff --git a/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp index 01181ce20e..b7c2b952e2 100644 --- a/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp +++ b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp @@ -1,53 +1,144 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins <sergio.martins@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins <sergio.martins@kdab.com> +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <QtCore/QScopeGuard> +#include <optional> + /*! - \class tst_QScopedGuard + \class tst_QScopeGuard \internal \since 5.11 - \brief Tests class QScopedCleanup and function qScopeGuard + \brief Tests class QScopeGuard and function qScopeGuard */ -class tst_QScopedGuard : public QObject +class tst_QScopeGuard : public QObject { Q_OBJECT private Q_SLOTS: + void construction(); + void constructionFromLvalue(); + void constructionFromRvalue(); + void optionalGuard(); void leavingScope(); void exceptions(); }; +void func() +{ +} + +int intFunc() +{ + return 0; +} + +[[nodiscard]] int noDiscardFunc() +{ + return 0; +} + +struct Callable +{ + Callable() { } + Callable(const Callable &other) + { + Q_UNUSED(other); + ++copied; + } + Callable(Callable &&other) + { + Q_UNUSED(other); + ++moved; + } + void operator()() { } + + static int copied; + static int moved; + static void resetCounts() + { + copied = 0; + moved = 0; + } +}; + +int Callable::copied = 0; +int Callable::moved = 0; + static int s_globalState = 0; -void tst_QScopedGuard::leavingScope() +void tst_QScopeGuard::construction() +{ + QScopeGuard fromLambda([] { }); + QScopeGuard fromFunction(func); + QScopeGuard fromFunctionPointer(&func); + QScopeGuard fromNonVoidFunction(intFunc); + QScopeGuard fromNoDiscardFunction(noDiscardFunc); +#ifndef __apple_build_version__ + QScopeGuard fromStdFunction{std::function<void()>(func)}; + std::function<void()> stdFunction(func); + QScopeGuard fromNamedStdFunction(stdFunction); +#endif +} + +void tst_QScopeGuard::constructionFromLvalue() +{ + Callable::resetCounts(); + { + Callable callable; + QScopeGuard guard(callable); + } + QCOMPARE(Callable::copied, 1); + QCOMPARE(Callable::moved, 0); + Callable::resetCounts(); + { + Callable callable; + auto guard = qScopeGuard(callable); + } + QCOMPARE(Callable::copied, 1); + QCOMPARE(Callable::moved, 0); +} + +void tst_QScopeGuard::constructionFromRvalue() +{ + Callable::resetCounts(); + { + Callable callable; + QScopeGuard guard(std::move(callable)); + } + QCOMPARE(Callable::copied, 0); + QCOMPARE(Callable::moved, 1); + Callable::resetCounts(); + { + Callable callable; + auto guard = qScopeGuard(std::move(callable)); + } + QCOMPARE(Callable::copied, 0); + QCOMPARE(Callable::moved, 1); +} + +void tst_QScopeGuard::optionalGuard() +{ + int i = 0; + auto lambda = [&] { ++i; }; + std::optional sg = false ? std::optional{qScopeGuard(lambda)} : std::nullopt; + QVERIFY(!sg); + QCOMPARE(i, 0); + sg.emplace(qScopeGuard(lambda)); + QVERIFY(sg); + sg->dismiss(); + sg.reset(); + QCOMPARE(i, 0); + sg.emplace(qScopeGuard(lambda)); + QCOMPARE(i, 0); + sg.reset(); + QCOMPARE(i, 1); +} + +void tst_QScopeGuard::leavingScope() { auto cleanup = qScopeGuard([] { s_globalState++; QCOMPARE(s_globalState, 3); }); QCOMPARE(s_globalState, 0); @@ -61,7 +152,7 @@ void tst_QScopedGuard::leavingScope() s_globalState++; } -void tst_QScopedGuard::exceptions() +void tst_QScopeGuard::exceptions() { s_globalState = 0; bool caught = false; @@ -81,5 +172,5 @@ void tst_QScopedGuard::exceptions() } -QTEST_MAIN(tst_QScopedGuard) +QTEST_MAIN(tst_QScopeGuard) #include "tst_qscopeguard.moc" diff --git a/tests/auto/corelib/tools/qset/CMakeLists.txt b/tests/auto/corelib/tools/qset/CMakeLists.txt new file mode 100644 index 0000000000..9e3e33ee7c --- /dev/null +++ b/tests/auto/corelib/tools/qset/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qset Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qset LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qset + SOURCES + tst_qset.cpp +) + +qt_internal_undefine_global_definition(tst_qset QT_NO_JAVA_STYLE_ITERATORS) diff --git a/tests/auto/corelib/tools/qset/qset.pro b/tests/auto/corelib/tools/qset/qset.pro deleted file mode 100644 index 3ae4bc4805..0000000000 --- a/tests/auto/corelib/tools/qset/qset.pro +++ /dev/null @@ -1,8 +0,0 @@ -CONFIG += testcase -TARGET = tst_qset -QT = core testlib -qtConfig(c++14): CONFIG += c++14 -qtConfig(c++1z): CONFIG += c++1z -SOURCES = tst_qset.cpp - -DEFINES -= QT_NO_JAVA_STYLE_ITERATORS diff --git a/tests/auto/corelib/tools/qset/tst_qset.cpp b/tests/auto/corelib/tools/qset/tst_qset.cpp index 6638ad8b60..116d38112b 100644 --- a/tests/auto/corelib/tools/qset/tst_qset.cpp +++ b/tests/auto/corelib/tools/qset/tst_qset.cpp @@ -1,39 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <qset.h> #include <qdebug.h> int toNumber(const QString &str) { int res = 0; - for (int i = 0; i < str.length(); ++i) + for (int i = 0; i < str.size(); ++i) res = (res * 10) + str[i].digitValue(); return res; } @@ -59,8 +34,9 @@ private slots: void begin(); void end(); void insert(); - void reverseIterators(); + void insertConstructionCounted(); void setOperations(); + void setOperationsOnEmptySet(); void stlIterator(); void stlMutableIterator(); void javaIterator(); @@ -69,13 +45,15 @@ private slots: void initializerList(); void qhash(); void intersects(); + void find(); + void values(); }; struct IdentityTracker { int value, id; }; -inline uint qHash(IdentityTracker key) { return qHash(key.value); } +inline size_t qHash(IdentityTracker key) { return qHash(key.value); } inline bool operator==(IdentityTracker lhs, IdentityTracker rhs) { return lhs.value == rhs.value; } void tst_QSet::operator_eq() @@ -161,43 +139,44 @@ void tst_QSet::size() QSet<int> set; QVERIFY(set.size() == 0); QVERIFY(set.isEmpty()); - QVERIFY(set.count() == set.size()); + QVERIFY(set.size() == set.size()); QVERIFY(set.isEmpty() == set.empty()); + QVERIFY(!set.isDetached()); set.insert(1); QVERIFY(set.size() == 1); QVERIFY(!set.isEmpty()); - QVERIFY(set.count() == set.size()); + QVERIFY(set.size() == set.size()); QVERIFY(set.isEmpty() == set.empty()); set.insert(1); QVERIFY(set.size() == 1); QVERIFY(!set.isEmpty()); - QVERIFY(set.count() == set.size()); + QVERIFY(set.size() == set.size()); QVERIFY(set.isEmpty() == set.empty()); set.insert(2); QVERIFY(set.size() == 2); QVERIFY(!set.isEmpty()); - QVERIFY(set.count() == set.size()); + QVERIFY(set.size() == set.size()); QVERIFY(set.isEmpty() == set.empty()); set.remove(1); QVERIFY(set.size() == 1); QVERIFY(!set.isEmpty()); - QVERIFY(set.count() == set.size()); + QVERIFY(set.size() == set.size()); QVERIFY(set.isEmpty() == set.empty()); set.remove(1); QVERIFY(set.size() == 1); QVERIFY(!set.isEmpty()); - QVERIFY(set.count() == set.size()); + QVERIFY(set.size() == set.size()); QVERIFY(set.isEmpty() == set.empty()); set.remove(2); QVERIFY(set.size() == 0); QVERIFY(set.isEmpty()); - QVERIFY(set.count() == set.size()); + QVERIFY(set.size() == set.size()); QVERIFY(set.isEmpty() == set.empty()); } @@ -206,6 +185,7 @@ void tst_QSet::capacity() QSet<int> set; int n = set.capacity(); QVERIFY(n == 0); + QVERIFY(!set.isDetached()); for (int i = 0; i < 1000; ++i) { set.insert(i); @@ -239,8 +219,12 @@ void tst_QSet::reserve() void tst_QSet::squeeze() { QSet<int> set; - int n = set.capacity(); - QVERIFY(n == 0); + QCOMPARE(set.capacity(), 0); + + set.squeeze(); + QCOMPARE(set.capacity(), 0); + + QVERIFY(!set.isDetached()); set.reserve(1000); QVERIFY(set.capacity() >= 1000); @@ -250,22 +234,37 @@ void tst_QSet::squeeze() for (int i = 0; i < 500; ++i) set.insert(i); - QVERIFY(set.capacity() >= 500 && set.capacity() < 10000); + QCOMPARE(set.size(), 500); + + // squeezed capacity for 500 elements + qsizetype capacity = set.capacity(); // current implementation: 512 + QCOMPARE_GE(capacity, set.size()); set.reserve(50000); - QVERIFY(set.capacity() >= 50000); + QVERIFY(set.capacity() >= 50000); // current implementation: 65536 set.squeeze(); - QVERIFY(set.capacity() < 500); + QCOMPARE(set.capacity(), capacity); + // removing elements does not shed capacity set.remove(499); - QVERIFY(set.capacity() < 500); + QCOMPARE(set.capacity(), capacity); set.insert(499); - QVERIFY(set.capacity() >= 500); + QCOMPARE(set.capacity(), capacity); + + // grow it beyond the current capacity + for (int i = set.size(); i <= capacity; ++i) + set.insert(i); + QCOMPARE(set.size(), capacity + 1); + QCOMPARE_GT(set.capacity(), capacity + 1);// current implementation: 2 * capacity (1024) for (int i = 0; i < 500; ++i) set.remove(i); + + // removing elements does not shed capacity + QCOMPARE_GT(set.capacity(), capacity + 1); + set.squeeze(); QVERIFY(set.capacity() < 100); } @@ -309,6 +308,7 @@ void tst_QSet::clear() set1.clear(); QVERIFY(set1.size() == 0); + QVERIFY(!set1.isDetached()); set1.insert("foo"); QVERIFY(set1.size() != 0); @@ -326,10 +326,9 @@ void tst_QSet::clear() void tst_QSet::cpp17ctad() { -#ifdef __cpp_deduction_guides #define QVERIFY_IS_SET_OF(obj, Type) \ QVERIFY2((std::is_same<decltype(obj), QSet<Type>>::value), \ - QMetaType::typeName(qMetaTypeId<decltype(obj)::value_type>())) + QMetaType::fromType<decltype(obj)::value_type>().name()) #define CHECK(Type, One, Two, Three) \ do { \ const Type v[] = {One, Two, Three}; \ @@ -346,29 +345,36 @@ void tst_QSet::cpp17ctad() CHECK(QString, QStringLiteral("one"), QStringLiteral("two"), QStringLiteral("three")); #undef QVERIFY_IS_SET_OF #undef CHECK -#else - QSKIP("This test requires C++17 Constructor Template Argument Deduction support enabled in the compiler."); -#endif } void tst_QSet::remove() { - QSet<QString> set1; + QSet<QString> set; + QCOMPARE(set.remove("test"), false); + QVERIFY(!set.isDetached()); + + const auto cnt = set.removeIf([](auto it) { + Q_UNUSED(it); + return true; + }); + QCOMPARE(cnt, 0); for (int i = 0; i < 500; ++i) - set1.insert(QString::number(i)); + set.insert(QString::number(i)); - QCOMPARE(set1.size(), 500); + QCOMPARE(set.size(), 500); for (int j = 0; j < 500; ++j) { - set1.remove(QString::number((j * 17) % 500)); - QCOMPARE(set1.size(), 500 - j - 1); + set.remove(QString::number((j * 17) % 500)); + QCOMPARE(set.size(), 500 - j - 1); } } void tst_QSet::contains() { QSet<QString> set1; + QVERIFY(!set1.contains("test")); + QVERIFY(!set1.isDetached()); for (int i = 0; i < 500; ++i) { QVERIFY(!set1.contains(QString::number(i))); @@ -393,6 +399,7 @@ void tst_QSet::containsSet() // empty set contains the empty set QVERIFY(set1.contains(set2)); + QVERIFY(!set1.isDetached()); for (int i = 0; i < 500; ++i) { set1.insert(QString::number(i)); @@ -414,6 +421,7 @@ void tst_QSet::containsSet() // the empty set doesn't contain a filled set QVERIFY(!set3.contains(set1)); + QVERIFY(!set3.isDetached()); // verify const signature const QSet<QString> set4; @@ -435,6 +443,8 @@ void tst_QSet::begin() QVERIFY(k == ell); QVERIFY(i == k); QVERIFY(j == ell); + QVERIFY(!set1.isDetached()); + QVERIFY(!set2.isDetached()); } set1.insert(44); @@ -464,6 +474,31 @@ void tst_QSet::begin() QVERIFY(i == k); QVERIFY(j == ell); } + + const QSet<int> set3; + QSet<int> set4 = set3; + + { + QSet<int>::const_iterator i = set3.begin(); + QSet<int>::const_iterator j = set3.cbegin(); + QSet<int>::const_iterator k = set4.begin(); + QVERIFY(i == j); + QVERIFY(k == j); + QVERIFY(!set3.isDetached()); + QVERIFY(set4.isDetached()); + } + + set4.insert(1); + + { + QSet<int>::const_iterator i = set3.begin(); + QSet<int>::const_iterator j = set3.cbegin(); + QSet<int>::const_iterator k = set4.begin(); + QVERIFY(i == j); + QVERIFY(k != j); + QVERIFY(!set3.isDetached()); + QVERIFY(set4.isDetached()); + } } void tst_QSet::end() @@ -484,6 +519,9 @@ void tst_QSet::end() QVERIFY(set1.constBegin() == set1.constEnd()); QVERIFY(set2.constBegin() == set2.constEnd()); + + QVERIFY(!set1.isDetached()); + QVERIFY(!set2.isDetached()); } set1.insert(44); @@ -496,13 +534,13 @@ void tst_QSet::end() QVERIFY(i == j); QVERIFY(k == ell); - QVERIFY(i != k); - QVERIFY(j != ell); QVERIFY(set1.constBegin() != set1.constEnd()); QVERIFY(set2.constBegin() == set2.constEnd()); + QVERIFY(set1.constBegin() != set2.constBegin()); } + set2 = set1; { @@ -524,6 +562,37 @@ void tst_QSet::end() set2.clear(); QVERIFY(set1.constBegin() == set1.constEnd()); QVERIFY(set2.constBegin() == set2.constEnd()); + + const QSet<int> set3; + QSet<int> set4 = set3; + + { + QSet<int>::const_iterator i = set3.end(); + QSet<int>::const_iterator j = set3.cend(); + QSet<int>::const_iterator k = set4.end(); + QVERIFY(i == j); + QVERIFY(k == j); + QVERIFY(!set3.isDetached()); + QVERIFY(!set4.isDetached()); + + QVERIFY(set3.constBegin() == set3.constEnd()); + QVERIFY(set4.constBegin() == set4.constEnd()); + } + + set4.insert(1); + + { + QSet<int>::const_iterator i = set3.end(); + QSet<int>::const_iterator j = set3.cend(); + QSet<int>::const_iterator k = set4.end(); + QVERIFY(i == j); + QVERIFY(k == j); + QVERIFY(!set3.isDetached()); + QVERIFY(set4.isDetached()); + + QVERIFY(set3.constBegin() == set3.constEnd()); + QVERIFY(set4.constBegin() != set4.constEnd()); + } } void tst_QSet::insert() @@ -577,19 +646,82 @@ void tst_QSet::insert() } } -void tst_QSet::reverseIterators() +struct ConstructionCounted { - QSet<int> s; - s << 1 << 17 << 61 << 127 << 911; - std::vector<int> v(s.begin(), s.end()); - std::reverse(v.begin(), v.end()); - const QSet<int> &cs = s; - QVERIFY(std::equal(v.begin(), v.end(), s.rbegin())); - QVERIFY(std::equal(v.begin(), v.end(), s.crbegin())); - QVERIFY(std::equal(v.begin(), v.end(), cs.rbegin())); - QVERIFY(std::equal(s.rbegin(), s.rend(), v.begin())); - QVERIFY(std::equal(s.crbegin(), s.crend(), v.begin())); - QVERIFY(std::equal(cs.rbegin(), cs.rend(), v.begin())); + ConstructionCounted(int i) : i(i) { } + ConstructionCounted(ConstructionCounted &&other) noexcept + : i(other.i), copies(other.copies), moves(other.moves + 1) + { + // set to some easily noticeable values + other.i = -64; + other.copies = -64; + other.moves = -64; + } + ConstructionCounted &operator=(ConstructionCounted &&other) noexcept + { + ConstructionCounted moved = std::move(other); + std::swap(*this, moved); + return *this; + } + ConstructionCounted(const ConstructionCounted &other) noexcept + : i(other.i), copies(other.copies + 1), moves(other.moves) + { + } + ConstructionCounted &operator=(const ConstructionCounted &other) noexcept + { + ConstructionCounted copy = other; + std::swap(*this, copy); + return *this; + } + ~ConstructionCounted() = default; + + friend bool operator==(const ConstructionCounted &lhs, const ConstructionCounted &rhs) + { + return lhs.i == rhs.i; + } + + QString toString() { return QString::number(i); } + + int i; + int copies = 0; + int moves = 0; +}; + +size_t qHash(const ConstructionCounted &c, std::size_t seed = 0) +{ + return qHash(c.i, seed); +} + +void tst_QSet::insertConstructionCounted() +{ + QSet<ConstructionCounted> set; + + // copy-insert + ConstructionCounted toCopy(7); + auto inserted = set.insert(toCopy); + QCOMPARE(set.size(), 1); + auto element = set.begin(); + QCOMPARE(inserted, element); + QCOMPARE(inserted->copies, 1); + QCOMPARE(inserted->moves, 1); + QCOMPARE(inserted->i, 7); + + // move-insert + ConstructionCounted toMove(8); + inserted = set.insert(std::move(toMove)); + element = set.find(8); + QCOMPARE(set.size(), 2); + QVERIFY(element != set.end()); + QCOMPARE(inserted, element); + QCOMPARE(inserted->copies, 0); + QCOMPARE(inserted->moves, 1); + QCOMPARE(inserted->i, 8); + + inserted = set.insert(std::move(toCopy)); // move-insert an existing value + QCOMPARE(set.size(), 2); + // The previously existing key is used as they compare equal: + QCOMPARE(inserted->copies, 1); + QCOMPARE(inserted->moves, 1); } void tst_QSet::setOperations() @@ -690,6 +822,44 @@ void tst_QSet::setOperations() QVERIFY(set18 == set8); } +void tst_QSet::setOperationsOnEmptySet() +{ + { + // Both sets are empty + QSet<int> set1; + QSet<int> set2; + + set1.unite(set2); + QVERIFY(set1.isEmpty()); + QVERIFY(!set1.isDetached()); + + set1.intersect(set2); + QVERIFY(set1.isEmpty()); + QVERIFY(!set1.isDetached()); + + set1.subtract(set2); + QVERIFY(set1.isEmpty()); + QVERIFY(!set1.isDetached()); + } + { + // Second set is not empty + QSet<int> empty; + QSet<int> nonEmpty { 1, 2, 3 }; + + empty.intersect(nonEmpty); + QVERIFY(empty.isEmpty()); + QVERIFY(!empty.isDetached()); + + empty.subtract(nonEmpty); + QVERIFY(empty.isEmpty()); + QVERIFY(!empty.isDetached()); + + empty.unite(nonEmpty); + QCOMPARE(empty, nonEmpty); + QVERIFY(!empty.isDetached()); + } +} + void tst_QSet::stlIterator() { QSet<QString> set1; @@ -705,16 +875,6 @@ void tst_QSet::stlIterator() } QVERIFY(sum == 24999 * 25000 / 2); } - - { - int sum = 0; - QSet<QString>::const_iterator i = set1.end(); - while (i != set1.begin()) { - --i; - sum += toNumber(*i); - } - QVERIFY(sum == 24999 * 25000 / 2); - } } void tst_QSet::stlMutableIterator() @@ -734,21 +894,10 @@ void tst_QSet::stlMutableIterator() } { - int sum = 0; - QSet<QString>::iterator i = set1.end(); - while (i != set1.begin()) { - --i; - sum += toNumber(*i); - } - QVERIFY(sum == 24999 * 25000 / 2); - } - - { QSet<QString> set2 = set1; QSet<QString> set3 = set2; QSet<QString>::iterator i = set2.begin(); - QSet<QString>::iterator j = set3.begin(); while (i != set2.end()) { i = set2.erase(i); @@ -756,24 +905,7 @@ void tst_QSet::stlMutableIterator() QVERIFY(set2.isEmpty()); QVERIFY(!set3.isEmpty()); - j = set3.end(); - while (j != set3.begin()) { - j--; - if (j + 1 != set3.end()) - set3.erase(j + 1); - } - if (set3.begin() != set3.end()) - set3.erase(set3.begin()); - - QVERIFY(set2.isEmpty()); - QVERIFY(set3.isEmpty()); - -// #if QT_VERSION >= 0x050000 -// i = set2.insert("foo"); -// #else - QSet<QString>::const_iterator k = set2.insert("foo"); - i = reinterpret_cast<QSet<QString>::iterator &>(k); -// #endif + i = set2.insert("foo"); QCOMPARE(*i, QLatin1String("foo")); } } @@ -802,59 +934,16 @@ void tst_QSet::javaIterator() QVERIFY(sum == 24999 * 25000 / 2); } - { - int sum = 0; - QSetIterator<QString> i(set1); - while (i.hasNext()) { - i.next(); - sum += toNumber(i.peekPrevious()); - } - QVERIFY(sum == 24999 * 25000 / 2); - } - - { - int sum = 0; - QSetIterator<QString> i(set1); - i.toBack(); - while (i.hasPrevious()) - sum += toNumber(i.previous()); - QVERIFY(sum == 24999 * 25000 / 2); - } - - { - int sum = 0; - QSetIterator<QString> i(set1); - i.toBack(); - while (i.hasPrevious()) { - sum += toNumber(i.peekPrevious()); - i.previous(); - } - QVERIFY(sum == 24999 * 25000 / 2); - } - - { - int sum = 0; - QSetIterator<QString> i(set1); - i.toBack(); - while (i.hasPrevious()) { - i.previous(); - sum += toNumber(i.peekNext()); - } - QVERIFY(sum == 24999 * 25000 / 2); - } - int sum1 = 0; int sum2 = 0; QSetIterator<QString> i(set1); QSetIterator<QString> j(set1); - int n = 0; while (i.hasNext()) { QVERIFY(j.hasNext()); set1.remove(i.peekNext()); sum1 += toNumber(i.next()); sum2 += toNumber(j.next()); - ++n; } QVERIFY(!j.hasNext()); QVERIFY(sum1 == 24999 * 25000 / 2); @@ -897,52 +986,10 @@ void tst_QSet::javaMutableIterator() } { - int sum = 0; - QMutableSetIterator<QString> i(set1); - while (i.hasNext()) { - i.next(); - sum += toNumber(i.peekPrevious()); - } - QVERIFY(sum == 24999 * 25000 / 2); - } - - { - int sum = 0; - QMutableSetIterator<QString> i(set1); - i.toBack(); - while (i.hasPrevious()) - sum += toNumber(i.previous()); - QVERIFY(sum == 24999 * 25000 / 2); - } - - { - int sum = 0; - QMutableSetIterator<QString> i(set1); - i.toBack(); - while (i.hasPrevious()) { - sum += toNumber(i.peekPrevious()); - i.previous(); - } - QVERIFY(sum == 24999 * 25000 / 2); - } - - { - int sum = 0; - QMutableSetIterator<QString> i(set1); - i.toBack(); - while (i.hasPrevious()) { - i.previous(); - sum += toNumber(i.peekNext()); - } - QVERIFY(sum == 24999 * 25000 / 2); - } - - { QSet<QString> set2 = set1; QSet<QString> set3 = set2; QMutableSetIterator<QString> i(set2); - QMutableSetIterator<QString> j(set3); while (i.hasNext()) { i.next(); @@ -950,14 +997,6 @@ void tst_QSet::javaMutableIterator() } QVERIFY(set2.isEmpty()); QVERIFY(!set3.isEmpty()); - - j.toBack(); - while (j.hasPrevious()) { - j.previous(); - j.remove(); - } - QVERIFY(set2.isEmpty()); - QVERIFY(set3.isEmpty()); } } @@ -982,7 +1021,7 @@ void tst_QSet::makeSureTheComfortFunctionsCompile() void tst_QSet::initializerList() { QSet<int> set = {1, 1, 2, 3, 4, 5}; - QCOMPARE(set.count(), 5); + QCOMPARE(set.size(), 5); QVERIFY(set.contains(1)); QVERIFY(set.contains(2)); QVERIFY(set.contains(3)); @@ -991,7 +1030,7 @@ void tst_QSet::initializerList() // check _which_ of the equal elements gets inserted (in the QHash/QMap case, it's the last): const QSet<IdentityTracker> set2 = {{1, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - QCOMPARE(set2.count(), 5); + QCOMPARE(set2.size(), 5); const int dummy = -1; const IdentityTracker searchKey = {1, dummy}; QCOMPARE(set2.find(searchKey)->id, 0); @@ -1010,23 +1049,33 @@ void tst_QSet::qhash() // { // create some deterministic initial state: - qSetGlobalQHashSeed(0); + QHashSeed::setDeterministicGlobalSeed(); QSet<int> s1; s1.reserve(4); s1 << 400 << 300 << 200 << 100; - // also change the seed: - qSetGlobalQHashSeed(0x10101010); + int retries = 128; + while (--retries >= 0) { + // reset the global seed to something different + QHashSeed::resetRandomGlobalSeed(); + + QSet<int> s2; + s2.reserve(100); // provoke different bucket counts + s2 << 100 << 200 << 300 << 400; // and insert elements in different order, too + QVERIFY(s1.capacity() != s2.capacity()); - QSet<int> s2; - s2.reserve(100); // provoke different bucket counts - s2 << 100 << 200 << 300 << 400; // and insert elements in different order, too + // see if we got a _different_ order + if (std::equal(s1.cbegin(), s1.cend(), s2.cbegin())) + continue; - QVERIFY(s1.capacity() != s2.capacity()); - QCOMPARE(s1, s2); - QVERIFY(!std::equal(s1.cbegin(), s1.cend(), s2.cbegin())); // verify that the order _is_ different - QCOMPARE(qHash(s1), qHash(s2)); + // check if the two QHashes still compare equal and produce the + // same hash, despite containing elements in different orders + QCOMPARE(s1, s2); + QCOMPARE(qHash(s1), qHash(s2)); + } + QVERIFY2(retries != 0, "Could not find a QSet with a different order of elements even " + "after a lot of retries. This is unlikely, but possible."); } // @@ -1045,6 +1094,8 @@ void tst_QSet::intersects() QVERIFY(!s1.intersects(s1)); QVERIFY(!s1.intersects(s2)); + QVERIFY(!s1.isDetached()); + QVERIFY(!s2.isDetached()); s1 << 100; QVERIFY(s1.intersects(s1)); @@ -1056,7 +1107,7 @@ void tst_QSet::intersects() s1 << 200; QVERIFY(s1.intersects(s2)); - qSetGlobalQHashSeed(0x10101010); + QHashSeed::resetRandomGlobalSeed(); QSet<int> s3; s3 << 500; QVERIFY(!s1.intersects(s3)); @@ -1064,6 +1115,50 @@ void tst_QSet::intersects() QVERIFY(s1.intersects(s3)); } +void tst_QSet::find() +{ + QSet<int> set; + QCOMPARE(set.find(1), set.end()); + QCOMPARE(set.constFind(1), set.constEnd()); + QVERIFY(!set.isDetached()); + + set.insert(1); + set.insert(2); + + QVERIFY(set.find(1) != set.end()); + QVERIFY(set.constFind(2) != set.constEnd()); + QVERIFY(set.find(3) == set.end()); + QVERIFY(set.constFind(4) == set.constEnd()); +} + +template<typename T> +QList<T> sorted(const QList<T> &list) +{ + QList<T> res = list; + std::sort(res.begin(), res.end()); + return res; +} + +void tst_QSet::values() +{ + QSet<int> set; + QVERIFY(set.values().isEmpty()); + QVERIFY(!set.isDetached()); + + set.insert(1); + QCOMPARE(set.values(), QList<int> { 1 }); + + set.insert(10); + set.insert(5); + set.insert(2); + + QCOMPARE(sorted(set.values()), QList<int>({ 1, 2, 5, 10 })); + + set.remove(5); + + QCOMPARE(sorted(set.values()), QList<int>({ 1, 2, 10 })); +} + QTEST_APPLESS_MAIN(tst_QSet) #include "tst_qset.moc" diff --git a/tests/auto/corelib/tools/qsharedpointer/CMakeLists.txt b/tests/auto/corelib/tools/qsharedpointer/CMakeLists.txt new file mode 100644 index 0000000000..0db0cba4c0 --- /dev/null +++ b/tests/auto/corelib/tools/qsharedpointer/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qsharedpointer Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsharedpointer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qsharedpointer + SOURCES + forwarddeclared.cpp + nontracked.cpp + wrapper.cpp + tst_qsharedpointer.cpp + LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp b/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp index d1bb89f549..c676924668 100644 --- a/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "externaltests.h" @@ -80,32 +55,34 @@ namespace QTest { { if (process.state() == QProcess::Running) { process.terminate(); - QThread::msleep(20); + QThread::sleep(std::chrono::milliseconds{20}); if (process.state() == QProcess::Running) process.kill(); } } +# ifdef Q_OS_UNIX class QExternalProcess: public QProcess { - protected: -#ifdef Q_OS_UNIX - void setupChildProcess() + public: + QExternalProcess() { - // run in user code - QProcess::setupChildProcess(); - - if (processChannelMode() == ForwardedChannels) { - // reopen /dev/tty into stdin - int fd = ::open("/dev/tty", O_RDONLY); - if (fd == -1) - return; - ::dup2(fd, 0); - ::close(fd); - } + setChildProcessModifier([this]() { + // run in user code + if (processChannelMode() == ForwardedChannels) { + // reopen /dev/tty into stdin + int fd = ::open("/dev/tty", O_RDONLY); + if (fd == -1) + return; + ::dup2(fd, 0); + ::close(fd); + } + }); } -#endif }; +# else + using QExternalProcess = QProcess; +# endif #endif // QT_CONFIG(process) class QExternalTestPrivate @@ -341,7 +318,7 @@ namespace QTest { if (qtModules & QExternalTest::QtScript) sourceCode += "#include <QtScript/QtScript>\n"; if (qtModules & QExternalTest::QtTest) - sourceCode += "#include <QtTest/QtTest>\n"; + sourceCode += "#include <QTest>\n"; if (qtModules & QExternalTest::QtDBus) sourceCode += "#include <QtDBus/QtDBus>\n"; if (qtModules & QExternalTest::QtWebKit) @@ -360,7 +337,7 @@ namespace QTest { "}\n" "\n" "#ifdef Q_OS_WIN\n" - "#include <windows.h>\n" + "#include <qt_windows.h>\n" "#if defined(Q_CC_MSVC)\n" "#include <crtdbg.h>\n" "#endif\n" @@ -588,7 +565,7 @@ namespace QTest { << QLatin1String("project.pro"); qmake.setWorkingDirectory(temporaryDirPath); - QString cmd = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmake"; + QString cmd = QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/qmake"; #ifdef Q_OS_WIN cmd.append(".exe"); #endif diff --git a/tests/auto/corelib/tools/qsharedpointer/externaltests.h b/tests/auto/corelib/tools/qsharedpointer/externaltests.h index bae6adaefe..790ca61992 100644 --- a/tests/auto/corelib/tools/qsharedpointer/externaltests.h +++ b/tests/auto/corelib/tools/qsharedpointer/externaltests.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef QTEST_EXTERNAL_TESTS_H diff --git a/tests/auto/corelib/tools/qsharedpointer/externaltests.pri b/tests/auto/corelib/tools/qsharedpointer/externaltests.pri deleted file mode 100644 index 604bd7f59f..0000000000 --- a/tests/auto/corelib/tools/qsharedpointer/externaltests.pri +++ /dev/null @@ -1,6 +0,0 @@ -SOURCES += $$PWD/externaltests.cpp -HEADERS += $$PWD/externaltests.h -cleanedQMAKESPEC = $$replace(QMAKESPEC, \\\\, /) -DEFINES += DEFAULT_MAKESPEC=\\\"$$cleanedQMAKESPEC\\\" - -cross_compile:DEFINES += QTEST_NO_RTTI QTEST_CROSS_COMPILED diff --git a/tests/auto/corelib/tools/qsharedpointer/forwarddeclared.cpp b/tests/auto/corelib/tools/qsharedpointer/forwarddeclared.cpp index df343b5ebc..5a0af60c11 100644 --- a/tests/auto/corelib/tools/qsharedpointer/forwarddeclared.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/forwarddeclared.cpp @@ -1,31 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "forwarddeclared.h" #include "qsharedpointer.h" diff --git a/tests/auto/corelib/tools/qsharedpointer/forwarddeclared.h b/tests/auto/corelib/tools/qsharedpointer/forwarddeclared.h index c72324841c..ba436d99cf 100644 --- a/tests/auto/corelib/tools/qsharedpointer/forwarddeclared.h +++ b/tests/auto/corelib/tools/qsharedpointer/forwarddeclared.h @@ -1,31 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef FORWARDDECLARED_H #define FORWARDDECLARED_H diff --git a/tests/auto/corelib/tools/qsharedpointer/nontracked.cpp b/tests/auto/corelib/tools/qsharedpointer/nontracked.cpp index e0780f8a9a..b572fa1b9f 100644 --- a/tests/auto/corelib/tools/qsharedpointer/nontracked.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/nontracked.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only /* * This file exists because tst_qsharedpointer.cpp is compiled with @@ -40,7 +15,7 @@ */ #include <qsharedpointer.h> -#include <QtTest> +#include <QTest> #include "nontracked.h" diff --git a/tests/auto/corelib/tools/qsharedpointer/nontracked.h b/tests/auto/corelib/tools/qsharedpointer/nontracked.h index 76af80d2d7..e10ea08a4d 100644 --- a/tests/auto/corelib/tools/qsharedpointer/nontracked.h +++ b/tests/auto/corelib/tools/qsharedpointer/nontracked.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef NONTRACKED_H #define NONTRACKED_H diff --git a/tests/auto/corelib/tools/qsharedpointer/qsharedpointer.pro b/tests/auto/corelib/tools/qsharedpointer/qsharedpointer.pro deleted file mode 100644 index 240137d563..0000000000 --- a/tests/auto/corelib/tools/qsharedpointer/qsharedpointer.pro +++ /dev/null @@ -1,17 +0,0 @@ -CONFIG += testcase -TARGET = tst_qsharedpointer -QT = core testlib - -SOURCES = tst_qsharedpointer.cpp \ - forwarddeclared.cpp \ - nontracked.cpp \ - wrapper.cpp - -HEADERS = forwarddeclared.h \ - nontracked.h \ - wrapper.h - -TESTDATA += forwarddeclared.cpp forwarddeclared.h - -include(externaltests.pri) -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp index a9fd282ac9..f42637a3fe 100644 --- a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp @@ -1,41 +1,19 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2022 Intel Corporation. +// Copyright (C) 2021 Klarälvdalens Datakonsult AB. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #define QT_SHAREDPOINTER_TRACK_POINTERS #include "qsharedpointer.h" -#include <QtTest/QtTest> +#include <QTest> +#include <QPointer> +#include <QRandomGenerator> #include <QtCore/QHash> +#include <QtCore/QList> #include <QtCore/QMap> #include <QtCore/QThread> -#include <QtCore/QVector> +#include <QtCore/private/qvolatile_p.h> -#include "externaltests.h" #include "forwarddeclared.h" #include "nontracked.h" #include "wrapper.h" @@ -45,7 +23,7 @@ #include <stdlib.h> #include <time.h> -#ifdef Q_OS_UNIX +#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY) #include <sys/resource.h> #endif @@ -76,10 +54,6 @@ private slots: void functionCallDownCast(); void upCast(); void qobjectWeakManagement(); -#if QT_DEPRECATED_SINCE(5, 0) - void noSharedPointerFromWeakQObject(); - void sharedPointerFromQObjectWithWeak(); -#endif void weakQObjectFromSharedPointer(); void objectCast(); void objectCastStdSharedPtr(); @@ -95,6 +69,7 @@ private slots: void constCorrectness(); void customDeleter(); void lambdaCustomDeleter(); + void customDeleterOnNullptr(); void creating(); void creatingCvQualified(); void creatingVariadic(); @@ -112,9 +87,11 @@ private slots: void threadStressTest_data(); void threadStressTest(); void validConstructs(); +#if 0 void invalidConstructs_data(); void invalidConstructs(); - +#endif + void ownerComparisons(); // let invalidConstructs be the last test, because it's the slowest; // add new tests above this block @@ -132,7 +109,7 @@ public: void tst_QSharedPointer::initTestCase() { -#if defined(Q_OS_UNIX) +#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY) // The tests create a lot of threads, which require file descriptors. On systems like // OS X low defaults such as 256 as the limit for the number of simultaneously // open files is not sufficient. @@ -153,7 +130,7 @@ QtSharedPointer::ExternalRefCountData *refCountData(const QSharedPointer<T> &b) QtSharedPointer::ExternalRefCountData* data; }; // sanity checks: - Q_STATIC_ASSERT(sizeof(QSharedPointer<T>) == sizeof(Dummy)); + static_assert(sizeof(QSharedPointer<T>) == sizeof(Dummy)); Q_ASSERT(static_cast<const Dummy*>(static_cast<const void*>(&b))->value == b.data()); return static_cast<const Dummy*>(static_cast<const void*>(&b))->data; } @@ -341,6 +318,11 @@ void tst_QSharedPointer::basics() QCOMPARE(!weak, isNull); QCOMPARE(bool(weak), !isNull); + QCOMPARE(weak.isNull(), (weak == nullptr)); + QCOMPARE(weak.isNull(), (nullptr == weak)); + QCOMPARE(!weak.isNull(), (weak != nullptr)); + QCOMPARE(!weak.isNull(), (nullptr != weak)); + QVERIFY(ptr == weak); QVERIFY(weak == ptr); QVERIFY(! (ptr != weak)); @@ -426,6 +408,12 @@ void tst_QSharedPointer::nullptrOps() QVERIFY(!p2.get()); QVERIFY(p1 == p2); + QWeakPointer<char> wp1 = p1; + QVERIFY(wp1 == nullptr); + QVERIFY(nullptr == wp1); + QCOMPARE(wp1, nullptr); + QCOMPARE(nullptr, wp1); + QSharedPointer<char> p3 = p1; QVERIFY(p3 == p1); QVERIFY(p3 == null); @@ -452,6 +440,10 @@ void tst_QSharedPointer::nullptrOps() QVERIFY(p4 != p2); QVERIFY(p4 != null); QVERIFY(p4 != p3); + + QWeakPointer<char> wp2 = p4; + QVERIFY(wp2 != nullptr); + QVERIFY(nullptr != wp2); } void tst_QSharedPointer::swap() @@ -594,6 +586,9 @@ void tst_QSharedPointer::useOfForwardDeclared() void tst_QSharedPointer::memoryManagement() { +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded") + int generation = Data::generationCounter + 1; int destructorCounter = Data::destructorCounter; @@ -655,6 +650,7 @@ void tst_QSharedPointer::memoryManagement() QVERIFY(ptr.isNull()); QVERIFY(ptr == 0); QCOMPARE(ptr.data(), (Data*)0); +QT_WARNING_POP } void tst_QSharedPointer::dropLastReferenceOfForwardDeclared() @@ -740,12 +736,12 @@ public: DerivedData() : moreData(0) { } ~DerivedData() { ++derivedDestructorCounter; } - virtual void virtualDelete() + void virtualDelete() override { delete this; } - virtual int classLevel() { return 2; } + int classLevel() override { return 2; } }; int DerivedData::derivedDestructorCounter = 0; @@ -760,7 +756,7 @@ public: class DiffPtrDerivedData: public Stuffing, public Data { public: - virtual int classLevel() { return 3; } + int classLevel() override { return 3; } }; class VirtualDerived: virtual public Data @@ -769,15 +765,20 @@ public: int moreData; VirtualDerived() : moreData(0xc0ffee) { } - virtual int classLevel() { return 4; } + int classLevel() override { return 4; } }; void tst_QSharedPointer::downCast() { { + // copy construction QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData); + QSharedPointer<DerivedData> copy = ptr; QSharedPointer<Data> baseptr = qSharedPointerCast<Data>(ptr); QSharedPointer<Data> other; + QWeakPointer<DerivedData> weak = ptr; + QWeakPointer<Data> baseweak = qSharedPointerCast<Data>(ptr); + QWeakPointer<Data> baseweak2 = qSharedPointerCast<Data>(weak); QVERIFY(ptr == baseptr); QVERIFY(baseptr == ptr); @@ -788,11 +789,55 @@ void tst_QSharedPointer::downCast() QVERIFY(other != ptr); QVERIFY(! (ptr == other)); QVERIFY(! (other == ptr)); + + // copy assignments + baseptr = qSharedPointerCast<Data>(ptr); + baseweak = qSharedPointerCast<Data>(ptr); + baseweak2 = baseweak; + + // move assignments (these don't actually move) + baseptr = qSharedPointerCast<Data>(std::move(ptr)); + ptr = copy; + baseweak = qSharedPointerCast<Data>(std::move(ptr)); + ptr = copy; + baseweak2 = qSharedPointerCast<Data>(std::move(baseweak)); + + // move construction (these don't actually move) + ptr = copy; + QSharedPointer<Data> ptr3(qSharedPointerCast<Data>(std::move(ptr))); + ptr = copy; + QWeakPointer<Data> baseweak3(qSharedPointerCast<Data>(std::move(ptr))); + ptr = copy; + QWeakPointer<Data> baseweak4(qSharedPointerCast<Data>(std::move(weak))); } { + // copy construction QSharedPointer<DerivedData> ptr = QSharedPointer<DerivedData>(new DerivedData); + QSharedPointer<DerivedData> copy = ptr; QSharedPointer<Data> baseptr = ptr; + QWeakPointer<DerivedData> weak = ptr; + QWeakPointer<Data> baseweak = ptr; + QWeakPointer<Data> baseweak2 = weak; + + // copy assignments + baseptr = ptr; + baseweak = ptr; + baseweak2 = weak; + + // move assignments (only the QSharedPointer-QSharedPointer actually moves) + baseweak = std::move(ptr); + baseweak2 = std::move(weak); + ptr = copy; + baseptr = std::move(ptr); + + // move construction (only the QSharedPointer-QSharedPointer actually moves) + ptr = copy; + QWeakPointer<Data> baseweak3(std::move(ptr)); + ptr = copy; + QWeakPointer<Data> baseweak4(std::move(weak)); + ptr = copy; + QSharedPointer<Data> baseptr2(std::move(ptr)); } int destructorCount; @@ -954,156 +999,10 @@ void tst_QSharedPointer::qobjectWeakManagement() QVERIFY(weak.isNull()); } safetyCheck(); - -#if QT_DEPRECATED_SINCE(5, 0) - { - QWeakPointer<QObject> weak; - weak = QWeakPointer<QObject>(); - QVERIFY(weak.isNull()); - QVERIFY(!weak.data()); - } - - { - QObject *obj = new QObject; - QWeakPointer<QObject> weak(obj); - QVERIFY(!weak.isNull()); - QVERIFY(weak.data() == obj); - - // now delete - delete obj; - QVERIFY(weak.isNull()); - } - safetyCheck(); - - { - // same, bit with operator= - QObject *obj = new QObject; - QWeakPointer<QObject> weak; - weak = obj; - QVERIFY(!weak.isNull()); - QVERIFY(weak.data() == obj); - - // now delete - delete obj; - QVERIFY(weak.isNull()); - } - safetyCheck(); - - { - // delete triggered by parent - QObject *obj, *parent; - parent = new QObject; - obj = new QObject(parent); - QWeakPointer<QObject> weak(obj); - - // now delete the parent - delete parent; - QVERIFY(weak.isNull()); - } - safetyCheck(); - - { - // same as above, but set the parent after QWeakPointer is created - QObject *obj, *parent; - obj = new QObject; - QWeakPointer<QObject> weak(obj); - - parent = new QObject; - obj->setParent(parent); - - // now delete the parent - delete parent; - QVERIFY(weak.isNull()); - } - safetyCheck(); - - { - // with two QWeakPointers - QObject *obj = new QObject; - QWeakPointer<QObject> weak(obj); - - { - QWeakPointer<QObject> weak2(obj); - QVERIFY(!weak2.isNull()); - QVERIFY(weak == weak2); - } - QVERIFY(!weak.isNull()); - - delete obj; - QVERIFY(weak.isNull()); - } - safetyCheck(); - - { - // same, but delete the pointer while two QWeakPointers exist - QObject *obj = new QObject; - QWeakPointer<QObject> weak(obj); - - { - QWeakPointer<QObject> weak2(obj); - QVERIFY(!weak2.isNull()); - - delete obj; - QVERIFY(weak.isNull()); - QVERIFY(weak2.isNull()); - } - QVERIFY(weak.isNull()); - } - safetyCheck(); -#endif } -#if QT_DEPRECATED_SINCE(5, 0) -void tst_QSharedPointer::noSharedPointerFromWeakQObject() -{ - // you're not allowed to create a QSharedPointer from an unmanaged QObject - QObject obj; - QWeakPointer<QObject> weak(&obj); - - { - QTest::ignoreMessage(QtWarningMsg , "QSharedPointer: cannot create a QSharedPointer from a QObject-tracking QWeakPointer"); - QSharedPointer<QObject> strong = weak.toStrongRef(); - QVERIFY(strong.isNull()); - } - { - QTest::ignoreMessage(QtWarningMsg , "QSharedPointer: cannot create a QSharedPointer from a QObject-tracking QWeakPointer"); - QSharedPointer<QObject> strong = weak; - QVERIFY(strong.isNull()); - } - - QCOMPARE(weak.data(), &obj); - // if something went wrong, we'll probably crash here -} - -void tst_QSharedPointer::sharedPointerFromQObjectWithWeak() -{ - QObject *ptr = new QObject; - QWeakPointer<QObject> weak = ptr; - { - QSharedPointer<QObject> shared(ptr); - QVERIFY(!weak.isNull()); - QCOMPARE(shared.data(), ptr); - QCOMPARE(weak.data(), ptr); - } - QVERIFY(weak.isNull()); -} -#endif - void tst_QSharedPointer::weakQObjectFromSharedPointer() { -#if QT_DEPRECATED_SINCE(5, 0) - { - // this is the inverse of the above: you're allowed to create a QWeakPointer - // from a managed QObject - QSharedPointer<QObject> shared(new QObject); - QWeakPointer<QObject> weak = shared.data(); - QVERIFY(!weak.isNull()); - - // delete: - shared.clear(); - QVERIFY(weak.isNull()); - } -#endif { QSharedPointer<QObject> shared(new QObject); QWeakPointer<QObject> weak = shared; @@ -1377,6 +1276,22 @@ void tst_QSharedPointer::virtualBaseDifferentPointers() QVERIFY(baseptr == aBase); } safetyCheck(); + { + VirtualDerived *aData = new VirtualDerived; + + QSharedPointer<VirtualDerived> ptr = QSharedPointer<VirtualDerived>(aData); + QWeakPointer<VirtualDerived> wptr = ptr; + + ptr.reset(); + QVERIFY(wptr.toStrongRef().isNull()); + + QWeakPointer<Data> wptr2 = wptr; + QVERIFY(wptr2.toStrongRef().isNull()); + + QWeakPointer<Data> wptr3 = std::move(wptr); + QVERIFY(wptr3.toStrongRef().isNull()); + } + safetyCheck(); } #ifndef QTEST_NO_RTTI @@ -1846,6 +1761,33 @@ void tst_QSharedPointer::lambdaCustomDeleter() safetyCheck(); } +void tst_QSharedPointer::customDeleterOnNullptr() +{ + Data *null = nullptr; + int callCount = 0; + auto deleter = [&callCount](Data *) { ++callCount; }; + { + QSharedPointer<Data> ptr(null, deleter); + } + safetyCheck(); + QCOMPARE(callCount, 1); + + callCount = 0; + { + QSharedPointer<Data> ptr(nullptr, deleter); + } + safetyCheck(); + QCOMPARE(callCount, 1); + + callCount = 0; + { + QSharedPointer<Data> ptr1(null, deleter); + QSharedPointer<Data> ptr2(nullptr, deleter); + } + safetyCheck(); + QCOMPARE(callCount, 2); +} + void customQObjectDeleterFn(QObject *obj) { ++customDeleterFnCallCount; @@ -2044,7 +1986,7 @@ class ThreadData QAtomicInt * volatile ptr; public: ThreadData(QAtomicInt *p) : ptr(p) { } - ~ThreadData() { ++ptr; } + ~ThreadData() { QtPrivate::volatilePreIncrement(ptr); } void ref() { // if we're called after the destructor, we'll crash @@ -2055,9 +1997,9 @@ public: class StrongThread: public QThread { protected: - void run() + void run() override { - usleep(QRandomGenerator::global()->bounded(2000)); + sleep(std::chrono::microseconds{QRandomGenerator::global()->bounded(2000)}); ptr->ref(); ptr.clear(); } @@ -2068,9 +2010,9 @@ public: class WeakThread: public QThread { protected: - void run() + void run() override { - usleep(QRandomGenerator::global()->bounded(2000)); + sleep(std::chrono::microseconds{QRandomGenerator::global()->bounded(2000)}); QSharedPointer<ThreadData> ptr = weak; if (ptr) ptr->ref(); @@ -2114,7 +2056,7 @@ void tst_QSharedPointer::threadStressTest() memset(guard2, 0, sizeof guard2); for (int r = 0; r < 5; ++r) { - QVector<QThread*> allThreads(6 * qMax(strongThreadCount, weakThreadCount) + 3, 0); + QList<QThread*> allThreads(6 * qMax(strongThreadCount, weakThreadCount) + 3, 0); QSharedPointer<ThreadData> base = QSharedPointer<ThreadData>(new ThreadData(&counter)); counter.storeRelaxed(0); @@ -2133,11 +2075,11 @@ void tst_QSharedPointer::threadStressTest() base.clear(); // start threads - for (int i = 0; i < allThreads.count(); ++i) + for (int i = 0; i < allThreads.size(); ++i) if (allThreads[i]) allThreads[i]->start(); // wait for them to finish - for (int i = 0; i < allThreads.count(); ++i) + for (int i = 0; i < allThreads.size(); ++i) if (allThreads[i]) allThreads[i]->wait(); qDeleteAll(allThreads); @@ -2155,7 +2097,7 @@ void tst_QSharedPointer::threadStressTest() } } -template<typename Container, bool Ordered> +template<typename Container, bool Ordered, bool Multi> void hashAndMapTest() { typedef typename Container::key_type Key; @@ -2200,26 +2142,30 @@ void hashAndMapTest() QVERIFY(it == c.end()); } - c.insertMulti(k1, Value(47)); - it = c.find(k1); - QVERIFY(it != c.end()); - QCOMPARE(it.key(), k1); - ++it; - QVERIFY(it != c.end()); - QCOMPARE(it.key(), k1); - ++it; - if (Ordered) - QVERIFY(it == c.end()); + if (Multi) { + c.insert(k1, Value(47)); + it = c.find(k1); + QVERIFY(it != c.end()); + QCOMPARE(it.key(), k1); + ++it; + QVERIFY(it != c.end()); + QCOMPARE(it.key(), k1); + ++it; + if (Ordered) + QVERIFY(it == c.end()); + } } void tst_QSharedPointer::map() { - hashAndMapTest<QMap<QSharedPointer<int>, int>, true>(); + hashAndMapTest<QMap<QSharedPointer<int>, int>, true, false>(); + hashAndMapTest<QMultiMap<QSharedPointer<int>, int>, true, true>(); } void tst_QSharedPointer::hash() { - hashAndMapTest<QHash<QSharedPointer<int>, int>, false>(); + hashAndMapTest<QHash<QSharedPointer<int>, int>, false, false>(); + hashAndMapTest<QMultiHash<QSharedPointer<int>, int>, false, true>(); } void tst_QSharedPointer::validConstructs() @@ -2228,7 +2174,10 @@ void tst_QSharedPointer::validConstructs() Data *aData = new Data; QSharedPointer<Data> ptr1 = QSharedPointer<Data>(aData); +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded") ptr1 = ptr1; // valid +QT_WARNING_POP QSharedPointer<Data> ptr2(ptr1); @@ -2240,6 +2189,7 @@ void tst_QSharedPointer::validConstructs() } } +#if 0 typedef bool (QTest::QExternalTest:: * TestFunction)(const QByteArray &body); Q_DECLARE_METATYPE(TestFunction) void tst_QSharedPointer::invalidConstructs_data() @@ -2368,12 +2318,12 @@ void tst_QSharedPointer::invalidConstructs_data() QTest::newRow("weak-pointer-from-regular-pointer") << &QTest::QExternalTest::tryCompileFail - << "Data *ptr = 0;\n" + << "Data *ptr = nullptr;\n" "QWeakPointer<Data> weakptr(ptr);\n"; QTest::newRow("shared-pointer-implicit-from-uninitialized") << &QTest::QExternalTest::tryCompileFail - << "Data *ptr = 0;\n" + << "Data *ptr = nullptr;\n" "QSharedPointer<Data> weakptr = Qt::Uninitialized;\n"; QTest::newRow("incompatible-custom-deleter1") @@ -2449,6 +2399,7 @@ void tst_QSharedPointer::invalidConstructs() QFAIL("Fail"); } } +#endif // #if 0 void tst_QSharedPointer::qvariantCast() { @@ -2474,52 +2425,6 @@ void tst_QSharedPointer::qvariantCast() } // Intentionally does not compile. // QSharedPointer<int> sop = qSharedPointerFromVariant<int>(v); - -#if QT_DEPRECATED_SINCE(5, 0) - v = QVariant::fromValue(sp.toWeakRef()); - - { - QWeakPointer<QObject> other = qWeakPointerFromVariant<QObject>(v); - QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); - } - { - QWeakPointer<QIODevice> other = qWeakPointerFromVariant<QIODevice>(v); - QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); - } - { - QWeakPointer<QFile> other = qWeakPointerFromVariant<QFile>(v); - QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); - } - { - QWeakPointer<QThread> other = qWeakPointerFromVariant<QThread>(v); - QVERIFY(!other); - } - - // Intentionally does not compile. -// QWeakPointer<int> sop = qWeakPointerFromVariant<int>(v); - - QFile file; - QWeakPointer<QFile> tracking = &file; - tracking.data()->setObjectName("A test name"); - v = QVariant::fromValue(tracking); - - { - QWeakPointer<QObject> other = qWeakPointerFromVariant<QObject>(v); - QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); - } - { - QWeakPointer<QIODevice> other = qWeakPointerFromVariant<QIODevice>(v); - QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); - } - { - QWeakPointer<QFile> other = qWeakPointerFromVariant<QFile>(v); - QCOMPARE(other.data()->objectName(), QString::fromLatin1("A test name")); - } - { - QWeakPointer<QThread> other = qWeakPointerFromVariant<QThread>(v); - QVERIFY(!other); - } -#endif } class SomeClass : public QEnableSharedFromThis<SomeClass> @@ -2824,7 +2729,7 @@ void tst_QSharedPointer::constructorThrow() int childDestructorCounter = ThrowData::childDestructorCounter; QSharedPointer<ThrowData> ptr; - QVERIFY_EXCEPTION_THROWN(ptr = QSharedPointer<ThrowData>::create(), QString); + QVERIFY_THROWS_EXCEPTION(QString, ptr = QSharedPointer<ThrowData>::create()); QVERIFY(ptr.isNull()); QCOMPARE(ThrowData::childGenerationCounter, childGeneration + 1); // destructor should never be called, if a constructor throws @@ -2866,7 +2771,7 @@ namespace ReentrancyWhileDestructing { { QSharedPointer<IB> b; - virtual QSharedPointer<IB> getB() + virtual QSharedPointer<IB> getB() override { return b; } @@ -2910,12 +2815,12 @@ struct Overloaded { return {}; } - static const Q_CONSTEXPR uint base1Called = sizeof(std::array<int, 1>); - static const Q_CONSTEXPR uint base2Called = sizeof(std::array<int, 2>); + static const constexpr uint base1Called = sizeof(std::array<int, 1>); + static const constexpr uint base2Called = sizeof(std::array<int, 2>); void test() { -#define QVERIFY_CALLS(expr, base) Q_STATIC_ASSERT(sizeof(call(expr)) == base##Called) +#define QVERIFY_CALLS(expr, base) static_assert(sizeof(call(expr)) == base##Called) QVERIFY_CALLS(SmartPtr<Base1>{}, base1); QVERIFY_CALLS(SmartPtr<Base2>{}, base2); QVERIFY_CALLS(SmartPtr<const Base1>{}, base1); @@ -2937,5 +2842,140 @@ void tst_QSharedPointer::overloads() weakOverloaded.test(); } +void tst_QSharedPointer::ownerComparisons() +{ + using SP = QSharedPointer<int>; + using WP = QWeakPointer<int>; + +#define CHECK_EQ(a, b) \ + do { \ + QVERIFY(a.owner_equal(b)); \ + QVERIFY(b.owner_equal(a)); \ + QVERIFY(!a.owner_before(b)); \ + QVERIFY(!b.owner_before(a)); \ + QVERIFY(a.owner_hash() == b.owner_hash()); \ + } while (false) + +#define CHECK_NOT_EQ(a, b) \ + do { \ + QVERIFY(!a.owner_equal(b)); \ + QVERIFY(!b.owner_equal(a)); \ + QVERIFY(a.owner_before(b) || b.owner_before(a)); \ + } while (false) + + // null + { + SP sp1; + SP sp2; + WP wp1 = sp1; + WP wp2; + + CHECK_EQ(sp1, sp1); + CHECK_EQ(sp1, sp2); + CHECK_EQ(sp1, wp1); + CHECK_EQ(sp2, wp2); + CHECK_EQ(wp1, wp1); + CHECK_EQ(wp1, wp2); + CHECK_EQ(wp2, wp2); + } + + // same owner + { + SP sp1 = SP::create(123); + SP sp2 = sp1; + WP wp1 = sp1; + SP wp2 = sp2; + + CHECK_EQ(sp1, sp1); + CHECK_EQ(sp1, sp2); + CHECK_EQ(sp1, wp1); + CHECK_EQ(sp2, wp2); + CHECK_EQ(wp1, wp1); + CHECK_EQ(wp1, wp2); + } + + // owning vs null + { + SP sp1 = SP::create(123); + SP sp2; + WP wp1 = sp1; + WP wp2 = sp2; + + CHECK_EQ(sp1, sp1); + CHECK_NOT_EQ(sp1, sp2); + CHECK_EQ(sp1, wp1); + CHECK_EQ(sp2, wp2); + CHECK_EQ(wp1, wp1); + CHECK_NOT_EQ(wp1, wp2); + } + + // different owners + { + SP sp1 = SP::create(123); + SP sp2 = SP::create(456); + WP wp1 = sp1; + WP wp2 = sp2; + + CHECK_EQ(sp1, sp1); + CHECK_NOT_EQ(sp1, sp2); + CHECK_EQ(sp1, wp1); + CHECK_EQ(sp2, wp2); + CHECK_EQ(wp1, wp1); + CHECK_NOT_EQ(wp1, wp2); + } + + // reset vs. null + { + SP sp1 = SP::create(123); + SP sp2; + WP wp1 = sp1; + WP wp2; + + CHECK_EQ(sp1, sp1); + CHECK_NOT_EQ(sp1, sp2); + CHECK_EQ(sp1, wp1); + CHECK_NOT_EQ(sp1, wp2); + CHECK_EQ(wp1, wp1); + CHECK_NOT_EQ(wp1, wp2); + + sp1.reset(); + + CHECK_EQ(sp1, sp1); + CHECK_EQ(sp1, sp2); + CHECK_NOT_EQ(sp1, wp1); + CHECK_EQ(sp2, wp2); + CHECK_EQ(wp1, wp1); + CHECK_NOT_EQ(wp1, wp2); + } + + // expired weak pointers + { + WP wp1 = SP::create(123); + WP wp2; + + CHECK_EQ(wp1, wp1); + CHECK_NOT_EQ(wp1, wp2); + } + + { + WP wp1 = SP::create(123); + WP wp2 = wp1; + + CHECK_EQ(wp1, wp1); + CHECK_EQ(wp1, wp2); + } + + { + WP wp1 = SP::create(123); + WP wp2 = SP::create(456); + + CHECK_EQ(wp1, wp1); + CHECK_EQ(wp2, wp2); + CHECK_NOT_EQ(wp1, wp2); + } +#undef CHECK_EQ +#undef CHECK_NOT_EQ +} + QTEST_MAIN(tst_QSharedPointer) #include "tst_qsharedpointer.moc" diff --git a/tests/auto/corelib/tools/qsharedpointer/wrapper.cpp b/tests/auto/corelib/tools/qsharedpointer/wrapper.cpp index 24a0cdc9c1..b39eee7d98 100644 --- a/tests/auto/corelib/tools/qsharedpointer/wrapper.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/wrapper.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifdef QT_SHAREDPOINTER_TRACK_POINTERS # undef QT_SHAREDPOINTER_TRACK_POINTERS diff --git a/tests/auto/corelib/tools/qsharedpointer/wrapper.h b/tests/auto/corelib/tools/qsharedpointer/wrapper.h index 18cea6e199..3b0bc09fed 100644 --- a/tests/auto/corelib/tools/qsharedpointer/wrapper.h +++ b/tests/auto/corelib/tools/qsharedpointer/wrapper.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef WRAPPER_H #define WRAPPER_H diff --git a/tests/auto/corelib/tools/qsize/CMakeLists.txt b/tests/auto/corelib/tools/qsize/CMakeLists.txt new file mode 100644 index 0000000000..91de696ddd --- /dev/null +++ b/tests/auto/corelib/tools/qsize/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qsize Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsize LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qsize + SOURCES + tst_qsize.cpp +) diff --git a/tests/auto/corelib/tools/qsize/qsize.pro b/tests/auto/corelib/tools/qsize/qsize.pro deleted file mode 100644 index 9462e98e49..0000000000 --- a/tests/auto/corelib/tools/qsize/qsize.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qsize -QT = core testlib -SOURCES = tst_qsize.cpp diff --git a/tests/auto/corelib/tools/qsize/tst_qsize.cpp b/tests/auto/corelib/tools/qsize/tst_qsize.cpp index 6824bad9c8..c9699c5e76 100644 --- a/tests/auto/corelib/tools/qsize/tst_qsize.cpp +++ b/tests/auto/corelib/tools/qsize/tst_qsize.cpp @@ -1,34 +1,33 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QSize> +#ifdef QVARIANT_H +# error "This test requires qsize.h to not include qvariant.h" +#endif + +// don't assume <type_traits> +template <typename T, typename U> +constexpr inline bool my_is_same_v = false; +template <typename T> +constexpr inline bool my_is_same_v<T, T> = true; + +#define CHECK(cvref) \ + static_assert(my_is_same_v<decltype(get<0>(std::declval<QSize cvref >())), int cvref >); \ + static_assert(my_is_same_v<decltype(get<1>(std::declval<QSize cvref >())), int cvref >) + +CHECK(&); +CHECK(const &); +CHECK(&&); +CHECK(const &&); + +#undef CHECK + +#include <QTest> #include <qsize.h> +#include <array> + Q_DECLARE_METATYPE(QMargins) class tst_QSize : public QObject @@ -47,8 +46,13 @@ private slots: void grownOrShrunkBy_data(); void grownOrShrunkBy(); + void toSizeF_data(); + void toSizeF(); + void transpose_data(); void transpose(); + + void structuredBinding(); }; // Testing get/set functions @@ -230,6 +234,30 @@ void tst_QSize::grownOrShrunkBy() QCOMPARE(shrunk.grownBy(margins), input); } +void tst_QSize::toSizeF_data() +{ + QTest::addColumn<QSize>("input"); + QTest::addColumn<QSizeF>("result"); + + auto row = [](int w, int h) { + QTest::addRow("(%d, %d)", w, h) << QSize(w, h) << QSizeF(w, h); + }; + constexpr std::array samples = {-1, 0, 1}; + for (int w : samples) { + for (int h : samples) { + row(w, h); + } + } +} + +void tst_QSize::toSizeF() +{ + QFETCH(const QSize, input); + QFETCH(const QSizeF, result); + + QCOMPARE(input.toSizeF(), result); +} + void tst_QSize::transpose_data() { QTest::addColumn<QSize>("input1"); @@ -250,5 +278,26 @@ void tst_QSize::transpose() QCOMPARE(input1 , expected); } +void tst_QSize::structuredBinding() +{ + { + QSize size(10, 20); + auto [width, height] = size; + QCOMPARE(width, 10); + QCOMPARE(height, 20); + } + { + QSize size(30, 40); + auto &[width, height] = size; + QCOMPARE(width, 30); + QCOMPARE(height, 40); + + width = 100; + height = 200; + QCOMPARE(size.width(), 100); + QCOMPARE(size.height(), 200); + } +} + QTEST_APPLESS_MAIN(tst_QSize) #include "tst_qsize.moc" diff --git a/tests/auto/corelib/tools/qsizef/CMakeLists.txt b/tests/auto/corelib/tools/qsizef/CMakeLists.txt new file mode 100644 index 0000000000..9adaafe2ea --- /dev/null +++ b/tests/auto/corelib/tools/qsizef/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qsizef Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsizef LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qsizef + SOURCES + tst_qsizef.cpp +) diff --git a/tests/auto/corelib/tools/qsizef/qsizef.pro b/tests/auto/corelib/tools/qsizef/qsizef.pro deleted file mode 100644 index f5ba220b06..0000000000 --- a/tests/auto/corelib/tools/qsizef/qsizef.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qsizef -QT = core testlib -SOURCES = tst_qsizef.cpp diff --git a/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp b/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp index bbffa74a62..ee33fa13b6 100644 --- a/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp +++ b/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp @@ -1,32 +1,29 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QSizeF> +#ifdef QVARIANT_H +# error "This test requires qsize.h to not include qvariant.h" +#endif + +// don't assume <type_traits> +template <typename T, typename U> +constexpr inline bool my_is_same_v = false; +template <typename T> +constexpr inline bool my_is_same_v<T, T> = true; + +#define CHECK(cvref) \ + static_assert(my_is_same_v<decltype(get<0>(std::declval<QSizeF cvref >())), qreal cvref >); \ + static_assert(my_is_same_v<decltype(get<1>(std::declval<QSizeF cvref >())), qreal cvref >) + +CHECK(&); +CHECK(const &); +CHECK(&&); +CHECK(const &&); + +#undef CHECK + +#include <QTest> #include <qsize.h> Q_DECLARE_METATYPE(QMarginsF) @@ -51,6 +48,8 @@ private slots: void transpose_data(); void transpose(); + + void structuredBinding(); }; void tst_QSizeF::isNull_data() @@ -214,5 +213,26 @@ void tst_QSizeF::transpose() { QCOMPARE(input1 , expected); } +void tst_QSizeF::structuredBinding() +{ + { + QSizeF size(10.0, 20.0); + auto [width, height] = size; + QCOMPARE(width, 10.0); + QCOMPARE(height, 20.0); + } + { + QSizeF size(30.0, 40.0); + auto &[width, height] = size; + QCOMPARE(width, 30.0); + QCOMPARE(height, 40.0); + + width = 100.0; + height = 200.0; + QCOMPARE(size.width(), 100.0); + QCOMPARE(size.height(), 200.0); + } +} + QTEST_APPLESS_MAIN(tst_QSizeF) #include "tst_qsizef.moc" diff --git a/tests/auto/corelib/tools/qspan/CMakeLists.txt b/tests/auto/corelib/tools/qspan/CMakeLists.txt new file mode 100644 index 0000000000..595d19dc43 --- /dev/null +++ b/tests/auto/corelib/tools/qspan/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qspan LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qspan + SOURCES + tst_qspan.cpp +) diff --git a/tests/auto/corelib/tools/qspan/tst_qspan.cpp b/tests/auto/corelib/tools/qspan/tst_qspan.cpp new file mode 100644 index 0000000000..91d2ecf739 --- /dev/null +++ b/tests/auto/corelib/tools/qspan/tst_qspan.cpp @@ -0,0 +1,450 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QSpan> + +#include <QList> +#include <QTest> + +#include <algorithm> +#include <array> +#ifdef __cpp_lib_span +#include <span> +#endif +#include <vector> + +namespace { + +struct NotNothrowMovable { + NotNothrowMovable(NotNothrowMovable &&) noexcept(false) {}; + NotNothrowMovable &operator=(NotNothrowMovable &&) noexcept(false) { return *this; }; +}; +static_assert(!std::is_nothrow_move_constructible_v<NotNothrowMovable>); +static_assert(!std::is_nothrow_move_assignable_v<NotNothrowMovable>); + +} // unnamed namespace + +// +// QSpan is nothrow movable even if the payload type is not: +// +static_assert(std::is_nothrow_move_constructible_v<QSpan<NotNothrowMovable>>); +static_assert(std::is_nothrow_move_constructible_v<QSpan<NotNothrowMovable, 42>>); +static_assert(std::is_nothrow_move_constructible_v<QSpan<NotNothrowMovable, 0>>); + +static_assert(std::is_nothrow_move_assignable_v<QSpan<NotNothrowMovable>>); +static_assert(std::is_nothrow_move_assignable_v<QSpan<NotNothrowMovable, 42>>); +static_assert(std::is_nothrow_move_assignable_v<QSpan<NotNothrowMovable, 0>>); + +// +// All QSpans are trivially destructible and trivially copyable: +// +static_assert(std::is_trivially_copyable_v<QSpan<NotNothrowMovable>>); +static_assert(std::is_trivially_copyable_v<QSpan<NotNothrowMovable, 42>>); +static_assert(std::is_trivially_copyable_v<QSpan<NotNothrowMovable, 0>>); + +static_assert(std::is_trivially_destructible_v<QSpan<NotNothrowMovable>>); +static_assert(std::is_trivially_destructible_v<QSpan<NotNothrowMovable, 42>>); +static_assert(std::is_trivially_destructible_v<QSpan<NotNothrowMovable, 0>>); + +// +// Fixed-size QSpans implicitly convert to variable-sized ones: +// +static_assert(std::is_convertible_v<QSpan<int, 42>, QSpan<int>>); +static_assert(std::is_convertible_v<QSpan<int, 0>, QSpan<int>>); + +#ifdef __cpp_lib_span +static_assert(std::is_convertible_v<std::span<int, 42>, QSpan<int>>); +static_assert(std::is_convertible_v<std::span<int, 0>, QSpan<int>>); + +#ifdef __cpp_lib_concepts +// requires enable_borrowed_range +static_assert(std::is_convertible_v<QSpan<int, 42>, std::span<int>>); +static_assert(std::is_convertible_v<QSpan<int, 0>, std::span<int>>); +#endif // __cpp_lib_concepts +#endif // __cpp_lib_span + +// +// Mutable spans implicitly convert to read-only ones, but not vice versa: +// +static_assert(std::is_convertible_v<QSpan<int>, QSpan<const int>>); +static_assert(std::is_convertible_v<QSpan<int, 42>, QSpan<const int, 42>>); +static_assert(std::is_convertible_v<QSpan<int, 0>, QSpan<const int, 0>>); + +static_assert(!std::is_convertible_v<QSpan<const int>, QSpan<int>>); +static_assert(!std::is_convertible_v<QSpan<const int, 42>, QSpan<int, 42>>); +static_assert(!std::is_convertible_v<QSpan<const int, 0>, QSpan<int, 0>>); + +#ifdef __cpp_lib_span +static_assert(std::is_convertible_v<std::span<int>, QSpan<const int>>); +static_assert(std::is_convertible_v<std::span<int, 42>, QSpan<const int, 42>>); +static_assert(std::is_convertible_v<std::span<int, 0>, QSpan<const int, 0>>); + +static_assert(!std::is_convertible_v<std::span<const int>, QSpan<int>>); +static_assert(!std::is_convertible_v<std::span<const int, 42>, QSpan<int, 42>>); +static_assert(!std::is_convertible_v<std::span<const int, 0>, QSpan<int, 0>>); + +static_assert(std::is_convertible_v<QSpan<int>, std::span<const int>>); +// fixed-size std::span constructors are explicit: +static_assert(!std::is_convertible_v<QSpan<int, 42>, std::span<const int, 42>>); +static_assert(!std::is_convertible_v<QSpan<int, 0>, std::span<const int, 0>>); +// observe: is_convertible<From,To>, but is_constuctible<To,From>! +static_assert(std::is_constructible_v<std::span<const int, 42>, QSpan<int, 42>>); +static_assert(std::is_constructible_v<std::span<const int, 0>, QSpan<int, 0>>); + +static_assert(!std::is_convertible_v<QSpan<const int>, std::span<int>>); +static_assert(!std::is_convertible_v<QSpan<const int, 42>, std::span<int, 42>>); +static_assert(!std::is_convertible_v<QSpan<const int, 0>, std::span<int, 0>>); +#endif // __cpp_lib_span + +// Spans don't convert from nonsense: +static_assert(!std::is_constructible_v<QSpan<const int>, int&&>); + +// Span is constructible from initializer_list +static_assert( std::is_convertible_v<std::initializer_list<int>, QSpan<const int>>); +static_assert(!std::is_convertible_v<std::initializer_list<int>, QSpan< int>>); +static_assert(!std::is_constructible_v<QSpan<int>, std::initializer_list<int>>); + +static_assert( std::is_convertible_v<std::initializer_list<int>, QSpan<const int, 4>>); // non-standard, but QSpan considers initializer_list a range +static_assert( std::is_constructible_v<QSpan<const int, 4>, std::initializer_list<int>>); +static_assert(!std::is_constructible_v<QSpan< int, 4>, std::initializer_list<int>>); + +class tst_QSpan : public QObject +{ + Q_OBJECT +public: + using QObject::QObject; + +private Q_SLOTS: + void onlyZeroExtentSpansHaveDefaultCtors() const; + void zeroExtentSpansMaintainADataPointer() const; + void fromArray() const; + void fromStdArray() const; + void fromStdInitializerList() const; + void fromZeroSizeStdArray() const; + void fromStdVector() const; + void fromQList() const; + void fromInitList() const; + +private: + template <typename T, std::size_t N> + void check_nonempty_span(QSpan<T, N>, qsizetype expectedSize) const; + template <typename T, std::size_t N> + void check_empty_span_incl_subspans(QSpan<T, N>) const; + template <typename T, std::size_t N> + void check_empty_span(QSpan<T, N>) const; + template <typename T, std::size_t N> + void check_null_span(QSpan<T, N>) const; + + template <std::size_t ExpectedExtent, typename C> + void from_container_impl(C &&c) const; + template <typename C> + void from_variable_size_container_impl(C &&c) const; +}; + +#define RETURN_IF_FAILED() \ + do { if (QTest::currentTestFailed()) return; } while (false) + +void tst_QSpan::onlyZeroExtentSpansHaveDefaultCtors() const +{ + static_assert(std::is_nothrow_default_constructible_v<QSpan<int, 0>>); + static_assert(std::is_nothrow_default_constructible_v<QSpan<const int, 0>>); + static_assert(std::is_nothrow_default_constructible_v<QSpan<int>>); + static_assert(std::is_nothrow_default_constructible_v<QSpan<const int, 0>>); + + QSpan<int, 0> si; + check_null_span(si); + RETURN_IF_FAILED(); + + QSpan<const int, 0> sci; + check_null_span(sci); + RETURN_IF_FAILED(); + + QSpan<int> sdi; + check_null_span(sdi); + RETURN_IF_FAILED(); + + QSpan<const int> sdci; + check_null_span(sdci); + RETURN_IF_FAILED(); + + static_assert(!std::is_default_constructible_v<QSpan<int, 1>>); + static_assert(!std::is_default_constructible_v<QSpan<const int, 42>>); +} + +void tst_QSpan::zeroExtentSpansMaintainADataPointer() const +{ + int i; + QSpan<int, 0> si{&i, 0}; + QCOMPARE(si.data(), &i); + check_empty_span_incl_subspans(si); + RETURN_IF_FAILED(); + + QSpan<const int, 0> sci{&i, 0}; + QCOMPARE(sci.data(), &i); + check_empty_span_incl_subspans(sci); + RETURN_IF_FAILED(); + + QSpan<int, 0> sdi{&i, 0}; + QCOMPARE(sdi.data(), &i); + check_empty_span_incl_subspans(sdi); + RETURN_IF_FAILED(); + + QSpan<const int, 0> sdci{&i, 0}; + QCOMPARE(sdci.data(), &i); + check_empty_span_incl_subspans(sdci); + RETURN_IF_FAILED(); +} + +template <typename T, std::size_t N> +void tst_QSpan::check_nonempty_span(QSpan<T, N> s, qsizetype expectedSize) const +{ + static_assert(N > 0); + QCOMPARE_GT(expectedSize, 0); // otherwise, use check_empty_span! + + QVERIFY(!s.empty()); + QVERIFY(!s.isEmpty()); + + QCOMPARE_EQ(s.size(), expectedSize); + QCOMPARE_NE(s.data(), nullptr); + + QCOMPARE_NE(s.begin(), s.end()); + QCOMPARE_NE(s.rbegin(), s.rend()); + QCOMPARE_NE(s.cbegin(), s.cend()); + QCOMPARE_NE(s.crbegin(), s.crend()); + + QCOMPARE_EQ(s.end() - s.begin(), s.size()); + QCOMPARE_EQ(s.cend() - s.cbegin(), s.size()); + QCOMPARE_EQ(s.rend() - s.rbegin(), s.size()); + QCOMPARE_EQ(s.crend() - s.crbegin(), s.size()); + + QCOMPARE_EQ(std::addressof(s.front()), std::addressof(*s.begin())); + QCOMPARE_EQ(std::addressof(s.front()), std::addressof(*s.cbegin())); + QCOMPARE_EQ(std::addressof(s.front()), std::addressof(s[0])); + QCOMPARE_EQ(std::addressof(s.back()), std::addressof(*s.rbegin())); + QCOMPARE_EQ(std::addressof(s.back()), std::addressof(*s.crbegin())); + QCOMPARE_EQ(std::addressof(s.back()), std::addressof(s[s.size() - 1])); + + // ### more? + + if (expectedSize == 1) { + // don't run into Mandates: Offset >= Extent + if constexpr (N > 0) { // incl. N == std::dynamic_extent + check_empty_span_incl_subspans(s.template subspan<1>()); + RETURN_IF_FAILED(); + } + check_empty_span_incl_subspans(s.subspan(1)); + RETURN_IF_FAILED(); + } else { + // don't run into Mandates: Offset >= Extent + if constexpr (N > 1) { // incl. N == std::dynamic_extent + check_nonempty_span(s.template subspan<1>(), expectedSize - 1); + RETURN_IF_FAILED(); + } + check_nonempty_span(s.subspan(1), expectedSize - 1); + RETURN_IF_FAILED(); + } +} + +template <typename T, std::size_t N> +void tst_QSpan::check_empty_span(QSpan<T, N> s) const +{ + QVERIFY(s.empty()); + QVERIFY(s.isEmpty()); + + QCOMPARE_EQ(s.size(), 0); + + QCOMPARE_EQ(s.begin(), s.end()); + QCOMPARE_EQ(s.cbegin(), s.cend()); + QCOMPARE_EQ(s.rbegin(), s.rend()); + QCOMPARE_EQ(s.crbegin(), s.crend()); +} + +template <typename T, std::size_t N> +void tst_QSpan::check_empty_span_incl_subspans(QSpan<T, N> s) const +{ + check_empty_span(s); + RETURN_IF_FAILED(); + + { + const auto fi = s.template first<0>(); + check_empty_span(fi); + RETURN_IF_FAILED(); + QCOMPARE_EQ(fi.data(), s.data()); + } + { + const auto la = s.template last<0>(); + check_empty_span(la); + RETURN_IF_FAILED(); + QCOMPARE_EQ(la.data(), s.data()); + } + { + const auto ss = s.template subspan<0>(); + check_empty_span(ss); + RETURN_IF_FAILED(); + QCOMPARE_EQ(ss.data(), s.data()); + } + { + const auto ss = s.template subspan<0, 0>(); + check_empty_span(ss); + RETURN_IF_FAILED(); + QCOMPARE_EQ(ss.data(), s.data()); + } + + { + const auto fi = s.first(0); + check_empty_span(fi); + RETURN_IF_FAILED(); + QCOMPARE_EQ(fi.data(), s.data()); + } + { + const auto la = s.last(0); + check_empty_span(la); + RETURN_IF_FAILED(); + QCOMPARE_EQ(la.data(), s.data()); + } + { + const auto ss = s.subspan(0); + check_empty_span(ss); + RETURN_IF_FAILED(); + QCOMPARE_EQ(ss.data(), s.data()); + } + { + const auto ss = s.subspan(0, 0); + check_empty_span(ss); + RETURN_IF_FAILED(); + QCOMPARE_EQ(ss.data(), s.data()); + } +} + + +template<typename T, std::size_t N> +void tst_QSpan::check_null_span(QSpan<T, N> s) const +{ + QCOMPARE_EQ(s.data(), nullptr); + QCOMPARE_EQ(s.begin(), nullptr); + QCOMPARE_EQ(s.cbegin(), nullptr); + QCOMPARE_EQ(s.end(), nullptr); + check_empty_span_incl_subspans(s); +} + +template <std::size_t ExpectedExtent, typename C> +void tst_QSpan::from_container_impl(C &&c) const +{ + const auto c_size = qsizetype(QSpanPrivate::adl_size(c)); + const auto c_data = QSpanPrivate::adl_data(c); + + using V = std::remove_reference_t<QSpanPrivate::range_reference_t<C>>; + { + QSpan si = c; // CTAD + static_assert(std::is_same_v<decltype(si), QSpan<V, ExpectedExtent>>); + + QCOMPARE_EQ(si.size(), c_size); + QCOMPARE_EQ(si.data(), c_data); + + check_nonempty_span(si, c_size); + RETURN_IF_FAILED(); + + QSpan<const int> sci = c; + + QCOMPARE_EQ(sci.size(), c_size); + QCOMPARE_EQ(sci.data(), c_data); + + check_nonempty_span(sci, c_size); + RETURN_IF_FAILED(); + } + { + QSpan sci = std::as_const(c); // CTAD + static_assert(std::is_same_v<decltype(sci), QSpan<const int, ExpectedExtent>>); + + QCOMPARE_EQ(sci.size(), c_size); + QCOMPARE_EQ(sci.data(), c_data); + + check_nonempty_span(sci, c_size); + RETURN_IF_FAILED(); + } +} + +template <typename C> +void tst_QSpan::from_variable_size_container_impl(C &&c) const +{ + constexpr auto E = q20::dynamic_extent; + from_container_impl<E>(std::forward<C>(c)); +} + +void tst_QSpan::fromArray() const +{ + int ai[] = {42, 84, 168, 336}; + from_container_impl<4>(ai); +} + +void tst_QSpan::fromStdArray() const +{ + std::array<int, 4> ai = {42, 84, 168, 336}; + from_container_impl<4>(ai); +} + +void tst_QSpan::fromStdInitializerList() const +{ + std::initializer_list<int> il = {42, 84, 168, 336}; + + QSpan sci = il; // CTAD + // special case: always deduced as <const int>: + static_assert(std::is_same_v<decltype(sci), QSpan<const int>>); + + QCOMPARE_EQ(sci.size(), qsizetype(il.size())); + QCOMPARE_EQ(sci.data(), il.begin()); + + check_nonempty_span(sci, 4); + RETURN_IF_FAILED(); +} + +void tst_QSpan::fromZeroSizeStdArray() const +{ + std::array<int, 0> ai = {}; + QSpan si = ai; // CTAD + static_assert(std::is_same_v<decltype(si), QSpan<int, 0>>); + QCOMPARE_EQ(si.data(), ai.data()); + + const std::array<int, 0> cai = {}; + QSpan csi = cai; // CTAD + static_assert(std::is_same_v<decltype(csi), QSpan<const int, 0>>); + QCOMPARE_EQ(csi.data(), cai.data()); + + std::array<const int, 0> aci = {}; + QSpan sci = aci; // CTAD + static_assert(std::is_same_v<decltype(sci), QSpan<const int, 0>>); + QCOMPARE_EQ(sci.data(), aci.data()); + + std::array<const int, 0> caci = {}; + QSpan csci = caci; // CTAD + static_assert(std::is_same_v<decltype(csci), QSpan<const int, 0>>); + QCOMPARE_EQ(csci.data(), caci.data()); +} + +void tst_QSpan::fromStdVector() const +{ + std::vector<int> vi = {42, 84, 168, 336}; + from_variable_size_container_impl(vi); +} + +void tst_QSpan::fromQList() const +{ + QList<int> li = {42, 84, 168, 336}; + from_variable_size_container_impl(li); +} + +void tst_QSpan::fromInitList() const +{ + from_variable_size_container_impl(std::initializer_list<int>{42, 84, 168, 336}); + + auto l1 = [](QSpan<const int>){}; + l1({1, 2, 3}); + + auto l2 = [](QSpan<const int, 3>){}; + l2({4, 5, 6}); +} + +#undef RETURN_IF_FAILED + +QTEST_APPLESS_MAIN(tst_QSpan); +#include "tst_qspan.moc" diff --git a/tests/auto/corelib/tools/qstl/CMakeLists.txt b/tests/auto/corelib/tools/qstl/CMakeLists.txt new file mode 100644 index 0000000000..b2f053e6ce --- /dev/null +++ b/tests/auto/corelib/tools/qstl/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qstl Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstl LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qstl + SOURCES + tst_qstl.cpp +) diff --git a/tests/auto/corelib/tools/qstl/qstl.pro b/tests/auto/corelib/tools/qstl/qstl.pro deleted file mode 100644 index e57e62b828..0000000000 --- a/tests/auto/corelib/tools/qstl/qstl.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qstl -QT = core testlib -SOURCES = tst_qstl.cpp diff --git a/tests/auto/corelib/tools/qstl/tst_qstl.cpp b/tests/auto/corelib/tools/qstl/tst_qstl.cpp index 6114da55b4..43d40bc128 100644 --- a/tests/auto/corelib/tools/qstl/tst_qstl.cpp +++ b/tests/auto/corelib/tools/qstl/tst_qstl.cpp @@ -1,33 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + + +#include <QTest> #include <qstring.h> diff --git a/tests/auto/corelib/tools/qtaggedpointer/.gitignore b/tests/auto/corelib/tools/qtaggedpointer/.gitignore new file mode 100644 index 0000000000..d027eb73b2 --- /dev/null +++ b/tests/auto/corelib/tools/qtaggedpointer/.gitignore @@ -0,0 +1 @@ +tst_qtaggedpointer diff --git a/tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt b/tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt new file mode 100644 index 0000000000..fb2e5dc922 --- /dev/null +++ b/tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qtaggedpointer Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtaggedpointer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtaggedpointer + SOURCES + tst_qtaggedpointer.cpp + LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp b/tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp new file mode 100644 index 0000000000..a1e61fc3a1 --- /dev/null +++ b/tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp @@ -0,0 +1,478 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QtCore/qtaggedpointer.h> + +class tst_QTaggedPointer : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void constExpr(); + void construction(); + void assignment(); + void dereferenceOperator(); + void pointerOperator(); + void negationOperator(); + void operatorBool(); + void comparison(); + void tag(); + void objectMember(); + void customTagType(); + void taggedLinkedList(); +}; + +void tst_QTaggedPointer::constExpr() +{ + { + constexpr QTaggedPointer<int> p; + Q_UNUSED(p); + } + { + enum Foo : uint {}; + constexpr QTaggedPointer<int, Foo> p; + Q_UNUSED(p); + } + { + enum Foo : int {}; + constexpr QTaggedPointer<int, Foo> p; + Q_UNUSED(p); + } + { + constexpr QTaggedPointer<int> p = nullptr; + Q_UNUSED(p); + } + { + enum Foo : uint {}; + constexpr QTaggedPointer<int, Foo> p = nullptr; + Q_UNUSED(p); + } + { + enum Foo : int {}; + constexpr QTaggedPointer<int, Foo> p = nullptr; + Q_UNUSED(p); + } +} + +void tst_QTaggedPointer::construction() +{ + { + QTaggedPointer<int> p; + QCOMPARE(p.data(), nullptr); + QVERIFY(!p.tag()); + } + { + QTaggedPointer<int> p(nullptr, 0x1); + QCOMPARE(p.data(), nullptr); + QCOMPARE(p.tag(), quintptr(0x1)); + } + { + QScopedPointer<int> rawPointer(new int(5)); + QTaggedPointer<int> p(rawPointer.data()); + QCOMPARE(p.data(), rawPointer.data()); + QVERIFY(!p.tag()); + } + { + QScopedPointer<int> rawPointer(new int(5)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + QCOMPARE(p.data(), rawPointer.data()); + QCOMPARE(p.tag(), quintptr(0x1)); + } +} + +void tst_QTaggedPointer::assignment() +{ + QScopedPointer<int> rawPointer(new int(5)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + QTaggedPointer<int> p2(rawPointer.data(), 0x2); + + QCOMPARE(p.data(), rawPointer.data()); + QCOMPARE(p.tag(), quintptr(0x1)); + + QCOMPARE(p2.data(), rawPointer.data()); + QCOMPARE(p2.tag(), quintptr(0x2)); + + p = nullptr; + QCOMPARE(p.data(), nullptr); + QCOMPARE(p.tag(), quintptr(0x1)); + + p = rawPointer.data(); + QCOMPARE(p.data(), rawPointer.data()); + QCOMPARE(p.tag(), quintptr(0x1)); + + p = {}; + QCOMPARE(p.data(), nullptr); + QCOMPARE(p.tag(), quintptr(0x0)); + + p = p2; + QCOMPARE(p.data(), rawPointer.data()); + QCOMPARE(p.tag(), quintptr(0x2)); + + p = nullptr; + QCOMPARE(p.data(), nullptr); + QCOMPARE(p.tag(), quintptr(0x2)); + + p = {}; + QCOMPARE(p.data(), nullptr); + QCOMPARE(p.tag(), quintptr(0x0)); + + p = rawPointer.data(); + QCOMPARE(p.data(), rawPointer.data()); + QCOMPARE(p.tag(), quintptr(0x0)); +} + +class AbstractClass +{ +public: + virtual ~AbstractClass() {} + virtual int member() const = 0; +}; + +class SubClass : public AbstractClass +{ +public: + int member() const override { return 5; } +}; + +void tst_QTaggedPointer::dereferenceOperator() +{ + /* Dereference a basic value. */ + { + QScopedPointer<int> rawPointer(new int(5)); + QTaggedPointer<int> p(rawPointer.data()); + const int value = *p; + QCOMPARE(value, 5); + } + + /* Dereference a basic value with tag. */ + { + QScopedPointer<int> rawPointer(new int(5)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + const int value = *p; + QCOMPARE(value, 5); + } + + /* Dereference a pointer to an abstract class. This verifies + * that the operator returns a reference, when compiling + * with MSVC 2005. */ + { + QScopedPointer<SubClass> ptr(new SubClass()); + QTaggedPointer<AbstractClass> p(ptr.data()); + QCOMPARE((*p).member(), 5); + } + + /* The operator should be const. */ + { + QScopedPointer<int> rawPointer(new int(5)); + const QTaggedPointer<int> p(rawPointer.data()); + *p; + } + + /* A reference should be returned, not a value. */ + { + QScopedPointer<int> rawPointer(new int(5)); + const QTaggedPointer<int> p(rawPointer.data()); + Q_UNUSED(static_cast<int &>(*p)); + } + + /* Instantiated on a const object, the returned object is a const reference. */ + { + QScopedPointer<int> rawPointer(new int(5)); + const QTaggedPointer<const int> p(rawPointer.data()); + Q_UNUSED(static_cast<const int &>(*p)); + } +} + +class Value +{ +public: + int value; +}; + +void tst_QTaggedPointer::pointerOperator() +{ + { + QScopedPointer<Value> valuePtr(new Value{5}); + QTaggedPointer<Value> p(valuePtr.data()); + QCOMPARE(p->value, 5); + } + + { + QScopedPointer<Value> valuePtr(new Value{5}); + QTaggedPointer<Value> p(valuePtr.data(), 0x1); + QCOMPARE(p->value, 5); + } + + /* The operator should be const. */ + { + QScopedPointer<Value> valuePtr(new Value{5}); + const QTaggedPointer<Value> p(valuePtr.data()); + QVERIFY(p->value); + } +} + +void tst_QTaggedPointer::negationOperator() +{ + /* Invoke on default constructed value. */ + { + QTaggedPointer<int> p; + QVERIFY(!p); + } + + /* Invoke on nullptr value with tag. */ + { + QTaggedPointer<int> p(nullptr, 0x1); + QVERIFY(!p); + } + + /* Invoke on a value. */ + { + QScopedPointer<int> rawPointer(new int(2)); + QTaggedPointer<int> p(rawPointer.data()); + QCOMPARE(!p, false); + } + + /* Invoke on a value with tag. */ + { + QScopedPointer<int> rawPointer(new int(2)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + QCOMPARE(!p, false); + } + + /* The signature should be const. */ + { + const QTaggedPointer<int> p; + !p; + } + + /* The return value should be bool. */ + { + const QTaggedPointer<int> p; + Q_UNUSED(static_cast<bool>(!p)); + } +} + +void tst_QTaggedPointer::operatorBool() +{ + /* Invoke on default constructed value. */ + { + QTaggedPointer<int> p; + QCOMPARE(bool(p), false); + } + + /* Invoke on nullptr value with tag. */ + { + QTaggedPointer<int> p(nullptr, 0x1); + QCOMPARE(bool(p), false); + } + + /* Invoke on active value. */ + { + QScopedPointer<int> rawPointer(new int(3)); + QTaggedPointer<int> p(rawPointer.data()); + QVERIFY(p); + } + + /* Invoke on active value with tag. */ + { + QScopedPointer<int> rawPointer(new int(3)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + QVERIFY(p); + } + + /* The signature should be const and return bool. */ + { + const QTaggedPointer<int> p; + (void)static_cast<bool>(p); + } +} + +template <class A1, class A2, class B> +void comparisonTest(const A1 &a1, const A2 &a2, const B &b) +{ + // test equality on equal pointers + QVERIFY(a1 == a2); + QVERIFY(a2 == a1); + + // test inequality on equal pointers + QVERIFY(!(a1 != a2)); + QVERIFY(!(a2 != a1)); + + // test equality on unequal pointers + QVERIFY(!(a1 == b)); + QVERIFY(!(a2 == b)); + QVERIFY(!(b == a1)); + QVERIFY(!(b == a2)); + + // test inequality on unequal pointers + QVERIFY(b != a1); + QVERIFY(b != a2); + QVERIFY(a1 != b); + QVERIFY(a2 != b); +} + +void tst_QTaggedPointer::comparison() +{ + QScopedPointer<int> a(new int(5)); + + { + QTaggedPointer<int> a1(a.data()); + QTaggedPointer<int> a2(a.data()); + QScopedPointer<int> rawPointer(new int(6)); + QTaggedPointer<int> b(rawPointer.data()); + + comparisonTest(a1, a1, b); + comparisonTest(a2, a2, b); + comparisonTest(a1, a2, b); + } + { + QTaggedPointer<int> a1(a.data(), 0x1); + QTaggedPointer<int> a2(a.data(), 0x1); + QScopedPointer<int> rawPointer(new int(6)); + QTaggedPointer<int> b(rawPointer.data(), 0x1); + + comparisonTest(a1, a1, b); + comparisonTest(a2, a2, b); + comparisonTest(a1, a2, b); + } + { + QTaggedPointer<int> a1(a.data(), 0x1); + QTaggedPointer<int> a2(a.data(), 0x2); + QScopedPointer<int> rawPointer(new int(6)); + QTaggedPointer<int> b(rawPointer.data(), 0x2); + + comparisonTest(a1, a1, b); + comparisonTest(a2, a2, b); + comparisonTest(a1, a2, b); + } + { + QTaggedPointer<int> p; + QVERIFY(p.isNull()); + QVERIFY(p == nullptr); + QVERIFY(nullptr == p); + } + { + QTaggedPointer<int> p(nullptr, 0x1); + QVERIFY(p.isNull()); + QVERIFY(p == nullptr); + QVERIFY(nullptr == p); + } + { + QScopedPointer<int> rawPointer(new int(42)); + QTaggedPointer<int> p(rawPointer.data()); + QVERIFY(!p.isNull()); + QVERIFY(p != nullptr); + QVERIFY(nullptr != p); + } + { + QScopedPointer<int> rawPointer(new int(42)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + QVERIFY(!p.isNull()); + QVERIFY(p != nullptr); + QVERIFY(nullptr != p); + } +} + +void tst_QTaggedPointer::tag() +{ + QScopedPointer<int> rawPointer(new int(3)); + QTaggedPointer<int> p(rawPointer.data()); + QCOMPARE(*p.data(), 3); + QVERIFY(!p.tag()); + + p.setTag(0x1); + QCOMPARE(p.tag(), 0x1); + + p.setTag(0x2); + QCOMPARE(p.tag(), 0x2); +} + +struct Foo +{ + Foo() : p(nullptr) {} + Foo(const Foo &other) : p(other.p) {} + Foo &operator=(const Foo &other) { + p = other.p; + return *this; + } + QTaggedPointer<int> p; +}; + +void tst_QTaggedPointer::objectMember() +{ + QScopedPointer<int> rawPointer(new int(42)); + Foo f; + f.p = QTaggedPointer<int>(rawPointer.data(), 0x1); + + Foo f2(f); + QCOMPARE(f2.p.data(), f.p.data()); + QCOMPARE(f2.p.tag(), f.p.tag()); + + Foo f3 = f; + QCOMPARE(f3.p.data(), f.p.data()); + QCOMPARE(f3.p.tag(), f.p.tag()); +} + +class Bar +{ + Q_GADGET +public: + enum Tag { + NoTag = 0, + FirstTag = 1, + SecondTag = 2 + }; + Q_DECLARE_FLAGS(Tags, Tag) + Q_FLAG(Tags) + + int value; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Bar::Tags) + +void tst_QTaggedPointer::customTagType() +{ + QScopedPointer<Bar> barPtr(new Bar{5}); + typedef QTaggedPointer<Bar, Bar::Tags> TaggedBar; + TaggedBar p(barPtr.data()); + QCOMPARE(p->value, 5); + QVERIFY(TaggedBar::maximumTag()); + + QVERIFY(!p.tag()); + QCOMPARE(p.tag(), Bar::NoTag); + + p.setTag(Bar::FirstTag | Bar::SecondTag); + QCOMPARE(p->value, 5); + QCOMPARE(p.tag(), Bar::FirstTag | Bar::SecondTag); +} + +// Compile-only test to ensure it's possible to use tagged pointers +// with incomplete types. +struct LinkedListItem +{ + enum Tag { + NoTag = 0, + FirstTag = 1 + }; + Q_DECLARE_FLAGS(Tags, Tag) + + QTaggedPointer<LinkedListItem, Tag> next; + + ~LinkedListItem() + { + delete next.data(); + } +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(LinkedListItem::Tags) + +void tst_QTaggedPointer::taggedLinkedList() +{ + QScopedPointer<LinkedListItem> lli(new LinkedListItem); + lli->next = QTaggedPointer<LinkedListItem, LinkedListItem::Tag>(new LinkedListItem); + lli->next.setTag(LinkedListItem::FirstTag); +} + +QTEST_MAIN(tst_QTaggedPointer) +#include "tst_qtaggedpointer.moc" diff --git a/tests/auto/corelib/tools/qtimeline/BLACKLIST b/tests/auto/corelib/tools/qtimeline/BLACKLIST index 9794b0059f..8040a529ba 100644 --- a/tests/auto/corelib/tools/qtimeline/BLACKLIST +++ b/tests/auto/corelib/tools/qtimeline/BLACKLIST @@ -1,7 +1,6 @@ [interpolation] -windows-10 msvc-2015 +windows osx [frameRate] -osx-10.12 -osx-10.13 +macos diff --git a/tests/auto/corelib/tools/qtimeline/CMakeLists.txt b/tests/auto/corelib/tools/qtimeline/CMakeLists.txt new file mode 100644 index 0000000000..a43e93990a --- /dev/null +++ b/tests/auto/corelib/tools/qtimeline/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qtimeline Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtimeline LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtimeline + SOURCES + tst_qtimeline.cpp + LIBRARIES + Qt::TestPrivate +) diff --git a/tests/auto/corelib/tools/qtimeline/qtimeline.pro b/tests/auto/corelib/tools/qtimeline/qtimeline.pro deleted file mode 100644 index 9424cf8fd2..0000000000 --- a/tests/auto/corelib/tools/qtimeline/qtimeline.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtimeline -QT = core testlib -SOURCES = tst_qtimeline.cpp diff --git a/tests/auto/corelib/tools/qtimeline/tst_qtimeline.cpp b/tests/auto/corelib/tools/qtimeline/tst_qtimeline.cpp index b68c582732..3593a65c4e 100644 --- a/tests/auto/corelib/tools/qtimeline/tst_qtimeline.cpp +++ b/tests/auto/corelib/tools/qtimeline/tst_qtimeline.cpp @@ -1,32 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QtTest/private/qpropertytesthelper_p.h> +#include <QSignalSpy> #include <qtimeline.h> @@ -36,15 +13,20 @@ class tst_QTimeLine : public QObject private slots: void range(); void currentTime(); + void bindableCurrentTime(); void duration(); + void bindableDuration(); void frameRate(); + void bindableUpdateInterval(); void value(); void currentFrame(); void loopCount(); + void bindableLoopCount(); void interpolation(); void reverse_data(); void reverse(); void toggleDirection(); + void bindableDirection(); void frameChanged(); void stopped(); void finished(); @@ -57,6 +39,7 @@ private slots: void resume(); void restart(); void setPaused(); + void automatedBindableTests(); protected slots: void finishedSlot(); @@ -97,7 +80,7 @@ void tst_QTimeLine::range() timeLine.setStartFrame(5000); QVERIFY(timeLine.currentFrame() > oldValue); timeLine.setFrameRange(0, 500); - QTRY_VERIFY(spy.count() > 1); + QTRY_VERIFY(spy.size() > 1); QVERIFY(timeLine.currentFrame() < oldValue); } @@ -119,7 +102,7 @@ void tst_QTimeLine::currentTime() spy.clear(); timeLine.setCurrentTime(timeLine.duration()/2); timeLine.setCurrentTime(timeLine.duration()/2); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QCOMPARE(timeLine.currentTime(), timeLine.duration()/2); timeLine.resume(); @@ -143,6 +126,55 @@ void tst_QTimeLine::currentTime() timeLine.stop(); } +void tst_QTimeLine::bindableCurrentTime() +{ + QTimeLine timeLine(2000); + QProperty<int> currentTimeObserver([&]() { return timeLine.currentTime(); }); + + timeLine.setUpdateInterval((timeLine.duration() / 2) / 33); + timeLine.setFrameRange(10, 20); + QCOMPARE(timeLine.currentTime(), 0); + QCOMPARE(currentTimeObserver.value(), 0); + QCOMPARE(currentTimeObserver.value(), timeLine.currentTime()); + + timeLine.start(); + QTRY_COMPARE(timeLine.state(), QTimeLine::Running); + QCOMPARE(currentTimeObserver.value(), timeLine.currentTime()); + QTRY_VERIFY(timeLine.currentTime() > timeLine.duration() / 2 - timeLine.duration() / 4); + QVERIFY(timeLine.currentTime() < timeLine.duration() / 2 + timeLine.duration() / 4); + QCOMPARE(currentTimeObserver.value(), timeLine.currentTime()); + + QTRY_COMPARE(timeLine.state(), QTimeLine::NotRunning); + QCOMPARE(timeLine.currentTime(), timeLine.duration()); + QCOMPARE(currentTimeObserver.value(), timeLine.currentTime()); + + QSignalSpy spy(&timeLine, &QTimeLine::valueChanged); + QVERIFY(spy.isValid()); + spy.clear(); + QProperty<int> referenceCurrentTime(timeLine.duration() / 2); + timeLine.bindableCurrentTime().setBinding([&]() { return referenceCurrentTime.value(); }); + QCOMPARE(spy.size(), 1); + // setting it a second time to check that valueChanged() is emitted only once + referenceCurrentTime = timeLine.duration() / 2; + QCOMPARE(spy.size(), 1); + + spy.clear(); + QCOMPARE(timeLine.currentTime(), timeLine.duration() / 2); + QCOMPARE(currentTimeObserver.value(), timeLine.duration() / 2); + timeLine.resume(); + // Let it update on its own + QCOMPARE(timeLine.state(), QTimeLine::Running); + QTRY_VERIFY(currentTimeObserver.value() > timeLine.duration() / 2); + QVERIFY(currentTimeObserver.value() < timeLine.duration()); + QTRY_COMPARE(timeLine.state(), QTimeLine::NotRunning); + QCOMPARE(currentTimeObserver.value(), timeLine.duration()); + // the resume above should have broken the connection to referenceCurrentTime, check that: + spy.clear(); + referenceCurrentTime = 0; + QCOMPARE(currentTimeObserver.value(), timeLine.duration()); + QCOMPARE(spy.size(), 0); +} + void tst_QTimeLine::duration() { QTimeLine timeLine(200); @@ -160,6 +192,35 @@ void tst_QTimeLine::duration() QCOMPARE(timeLine.duration(), 1000); } +void tst_QTimeLine::bindableDuration() +{ + QTimeLine timeLine(200); + QProperty<int> durationObserver; + durationObserver.setBinding([&]() { return timeLine.duration(); }); + QCOMPARE(durationObserver.value(), timeLine.duration()); + + timeLine.setFrameRange(10, 20); + QCOMPARE(timeLine.duration(), 200); + + QProperty<int> referenceDuration(500); + timeLine.bindableDuration().setBinding([&]() { return referenceDuration.value(); }); + QCOMPARE(durationObserver.value(), referenceDuration.value()); + + QCOMPARE(timeLine.duration(), 500); + + timeLine.start(); + QTRY_COMPARE(timeLine.state(), QTimeLine::Running); + QTRY_VERIFY(timeLine.currentTime() > 0); + QTRY_COMPARE(timeLine.state(), QTimeLine::NotRunning); + QCOMPARE(timeLine.currentTime(), 500); + // The duration shouldn't change + QCOMPARE(timeLine.duration(), 500); + + referenceDuration = 30; + QCOMPARE(timeLine.duration(), 30); + QCOMPARE(durationObserver.value(), 30); +} + void tst_QTimeLine::frameRate() { QTimeLine timeLine; @@ -175,7 +236,7 @@ void tst_QTimeLine::frameRate() timeLine.start(); QTest::qWait(timeLine.duration()*2); QCOMPARE(timeLine.state(), QTimeLine::NotRunning); - int slowCount = spy.count(); + int slowCount = spy.size(); // Faster!! timeLine.setUpdateInterval(1000 / 100); @@ -184,7 +245,41 @@ void tst_QTimeLine::frameRate() timeLine.start(); QTest::qWait(timeLine.duration()*2); QCOMPARE(timeLine.state(), QTimeLine::NotRunning); - QVERIFY2(slowCount < spy.count(), QByteArray::number(spy.count())); + QVERIFY2(slowCount < spy.size(), QByteArray::number(spy.size())); +} + +void tst_QTimeLine::bindableUpdateInterval() +{ + QTimeLine timeLine; + timeLine.setFrameRange(100, 2000); + + QProperty<int> updateIntervalObserver; + updateIntervalObserver.setBinding([&]() { return timeLine.updateInterval(); }); + + QCOMPARE(updateIntervalObserver.value(), 1000 / 25); + QProperty<int> updateIntervalReference(1000 / 60); + timeLine.bindableUpdateInterval().setBinding([&]() { return updateIntervalReference.value(); }); + + updateIntervalReference = 1000 / 60; + QCOMPARE(updateIntervalObserver.value(), 1000 / 60); + + // Default speed + updateIntervalReference = 1000 / 33; + QSignalSpy spy(&timeLine, &QTimeLine::frameChanged); + QVERIFY(spy.isValid()); + timeLine.start(); + QTest::qWait(timeLine.duration() * 2); + QCOMPARE(timeLine.state(), QTimeLine::NotRunning); + int slowCount = spy.size(); + + // Faster!! + updateIntervalReference = 1000 / 100; + spy.clear(); + timeLine.setCurrentTime(0); + timeLine.start(); + QTest::qWait(timeLine.duration() * 2); + QCOMPARE(timeLine.state(), QTimeLine::NotRunning); + QVERIFY2(slowCount < spy.size(), QByteArray::number(spy.size())); } void tst_QTimeLine::value() @@ -199,7 +294,7 @@ void tst_QTimeLine::value() QTRY_VERIFY(timeLine.currentValue() > 0); QTRY_COMPARE(timeLine.state(), QTimeLine::NotRunning); QCOMPARE(timeLine.currentValue(), 1.0); - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); // Reverse should decrease the value timeLine.setCurrentTime(100); @@ -248,7 +343,7 @@ void tst_QTimeLine::loopCount() timeLine.setLoopCount(0); QCOMPARE(timeLine.loopCount(), 0); - // Default speed infiniti looping + // Default speed endless looping QSignalSpy spy(&timeLine, &QTimeLine::frameChanged); QVERIFY(spy.isValid()); timeLine.start(); @@ -263,7 +358,7 @@ void tst_QTimeLine::loopCount() QCOMPARE(timeLine.state(), QTimeLine::Running); timeLine.stop(); - timeLine.setDuration(2500); // ### some platforms have a very low resolution timer + timeLine.setDuration(2500); // some platforms have a very low resolution timer timeLine.setFrameRange(0, 2); timeLine.setLoopCount(4); @@ -278,41 +373,125 @@ void tst_QTimeLine::loopCount() for(int i=0;i<2;i++) { timeLine.start(); - //we clear te list after the start so we don't catch - //a frameChanged signal for the frame 0 at the beginning + // we clear the list after the start so we don't catch + // a frameChanged signal for the frame 0 at the beginning finishedSpy.clear(); frameChangedSpy.clear(); loop.exec(); - QCOMPARE(finishedSpy.count(), 1); - QCOMPARE(frameChangedSpy.count(), 11); - for (int i = 0; i < 11; ++i) { + QCOMPARE(finishedSpy.size(), 1); + QCOMPARE(frameChangedSpy.size(), 11); + for (int i = 0; i < 11; ++i) QCOMPARE(frameChangedSpy.at(i).at(0).toInt(), (i+1) % 3); - } } timeLine.setDirection(QTimeLine::Backward); timeLine.start(); loop.exec(); - QCOMPARE(finishedSpy.count(), 2); - QCOMPARE(frameChangedSpy.count(), 22); + QCOMPARE(finishedSpy.size(), 2); + QCOMPARE(frameChangedSpy.size(), 22); for (int i = 11; i < 22; ++i) { QCOMPARE(frameChangedSpy.at(i).at(0).toInt(), 2 - (i+2) % 3); } } +void tst_QTimeLine::bindableLoopCount() +{ + QTimeLine timeLine(200); + QProperty<int> referenceLoopCount(1); + timeLine.bindableLoopCount().setBinding([&]() { return referenceLoopCount.value(); }); + QProperty<int> loopCountObserver([&]() { return timeLine.loopCount(); }); + + QCOMPARE(referenceLoopCount.value(), 1); + QCOMPARE(timeLine.loopCount(), 1); + QCOMPARE(loopCountObserver.value(), 1); + + timeLine.setFrameRange(10, 20); + + QCOMPARE(referenceLoopCount.value(), 1); + QCOMPARE(timeLine.loopCount(), 1); + QCOMPARE(loopCountObserver.value(), 1); + + referenceLoopCount = 0; + + QCOMPARE(referenceLoopCount.value(), 0); + QCOMPARE(timeLine.loopCount(), 0); + QCOMPARE(loopCountObserver.value(), 0); + + // Default speed endless looping + QSignalSpy spy(&timeLine, &QTimeLine::frameChanged); + QVERIFY(spy.isValid()); + timeLine.start(); + QTest::qWait(timeLine.duration()); + QCOMPARE(timeLine.state(), QTimeLine::Running); + // QCOMPARE(timeLine.currentFrame(), 20); + QTest::qWait(timeLine.duration() * 6); + QCOMPARE(timeLine.state(), QTimeLine::Running); + QVERIFY(timeLine.currentTime() >= 0); + QVERIFY(timeLine.currentFrame() >= 10); + QVERIFY(timeLine.currentFrame() <= 20); + QCOMPARE(timeLine.state(), QTimeLine::Running); + timeLine.stop(); + + timeLine.setDuration(2500); // some platforms have a very low resolution timer + timeLine.setFrameRange(0, 2); + referenceLoopCount = 4; + + QSignalSpy finishedSpy(&timeLine, &QTimeLine::finished); + QSignalSpy frameChangedSpy(&timeLine, &QTimeLine::frameChanged); + QVERIFY(finishedSpy.isValid()); + QVERIFY(frameChangedSpy.isValid()); + QEventLoop loop; + connect(&timeLine, SIGNAL(finished()), &loop, SLOT(quit())); + + for (int i = 0; i < 2; i++) { + + timeLine.start(); + // we clear the list after the start so we don't catch + // a frameChanged signal for the frame 0 at the beginning + finishedSpy.clear(); + frameChangedSpy.clear(); + + loop.exec(); + + QCOMPARE(finishedSpy.size(), 1); + QCOMPARE(frameChangedSpy.size(), 11); + for (int i = 0; i < 11; ++i) + QCOMPARE(frameChangedSpy.at(i).at(0).toInt(), (i + 1) % 3); + } + + timeLine.setDirection(QTimeLine::Backward); + timeLine.start(); + loop.exec(); + + QCOMPARE(finishedSpy.size(), 2); + QCOMPARE(frameChangedSpy.size(), 22); + for (int i = 11; i < 22; ++i) + QCOMPARE(frameChangedSpy.at(i).at(0).toInt(), 2 - (i + 2) % 3); +} + void tst_QTimeLine::interpolation() { + // also tests bindableEasingCurve QTimeLine timeLine(400); - QCOMPARE(timeLine.curveShape(), QTimeLine::EaseInOutCurve); + QProperty<QEasingCurve> easingCurveObserver([&]() { return timeLine.easingCurve(); }); + + QCOMPARE(timeLine.easingCurve(), QEasingCurve::InOutSine); + QCOMPARE(easingCurveObserver.value(), QEasingCurve::InOutSine); + timeLine.setFrameRange(100, 200); - timeLine.setCurveShape(QTimeLine::LinearCurve); - QCOMPARE(timeLine.curveShape(), QTimeLine::LinearCurve); + QProperty<QEasingCurve> referenceEasingCurve(QEasingCurve::Linear); + timeLine.bindableEasingCurve().setBinding([&]() { return referenceEasingCurve.value(); }); + QCOMPARE(timeLine.easingCurve(), QEasingCurve::Linear); + QCOMPARE(easingCurveObserver.value(), QEasingCurve::Linear); // smooth - timeLine.setCurveShape(QTimeLine::EaseInOutCurve); + referenceEasingCurve = QEasingCurve::InOutSine; + QCOMPARE(timeLine.easingCurve(), QEasingCurve::InOutSine); + QCOMPARE(easingCurveObserver.value(), QEasingCurve::InOutSine); + timeLine.start(); QTest::qWait(100); QCOMPARE(timeLine.state(), QTimeLine::Running); @@ -323,7 +502,11 @@ void tst_QTimeLine::interpolation() timeLine.setCurrentTime(0); // linear - timeLine.setCurveShape(QTimeLine::LinearCurve); + referenceEasingCurve = QEasingCurve::Linear; + + QCOMPARE(timeLine.easingCurve(), QEasingCurve::Linear); + QCOMPARE(easingCurveObserver.value(), QEasingCurve::Linear); + timeLine.start(); QTest::qWait(100); QCOMPARE(timeLine.state(), QTimeLine::Running); @@ -373,7 +556,7 @@ void tst_QTimeLine::reverse() QFETCH(int, wait2); QTimeLine timeLine(duration); - timeLine.setCurveShape(QTimeLine::LinearCurve); + timeLine.setEasingCurve(QEasingCurve::Linear); timeLine.setFrameRange(start, end); timeLine.setDirection((QTimeLine::Direction)direction); @@ -406,20 +589,44 @@ void tst_QTimeLine::reverse() void tst_QTimeLine::toggleDirection() { - // Note: enum values are cast to int so that QCOMPARE will show - // the values if they don't match. QTimeLine timeLine; - QCOMPARE(int(timeLine.direction()), int(QTimeLine::Forward)); + QCOMPARE(timeLine.direction(), QTimeLine::Forward); timeLine.toggleDirection(); - QCOMPARE(int(timeLine.direction()), int(QTimeLine::Backward)); + QCOMPARE(timeLine.direction(), QTimeLine::Backward); timeLine.toggleDirection(); - QCOMPARE(int(timeLine.direction()), int(QTimeLine::Forward)); + QCOMPARE(timeLine.direction(), QTimeLine::Forward); +} + +void tst_QTimeLine::bindableDirection() +{ + // Note: enum values are cast to int so that QCOMPARE will show + // the values if they don't match. + QTimeLine timeLine; + QProperty<QTimeLine::Direction> directionObserver([&]() { return timeLine.direction(); }); + QProperty<QTimeLine::Direction> referenceDirection(QTimeLine::Forward); + timeLine.bindableDirection().setBinding([&]() { return referenceDirection.value(); }); + + QCOMPARE(referenceDirection.value(), QTimeLine::Forward); + QCOMPARE(timeLine.direction(), QTimeLine::Forward); + QCOMPARE(directionObserver.value(), QTimeLine::Forward); + + referenceDirection = QTimeLine::Backward; + + QCOMPARE(referenceDirection.value(), QTimeLine::Backward); + QCOMPARE(timeLine.direction(), QTimeLine::Backward); + QCOMPARE(directionObserver.value(), QTimeLine::Backward); + + referenceDirection = QTimeLine::Forward; + + QCOMPARE(referenceDirection.value(), QTimeLine::Forward); + QCOMPARE(timeLine.direction(), QTimeLine::Forward); + QCOMPARE(directionObserver.value(), QTimeLine::Forward); } void tst_QTimeLine::frameChanged() { QTimeLine timeLine; - timeLine.setCurveShape(QTimeLine::LinearCurve); + timeLine.setEasingCurve(QEasingCurve::Linear); timeLine.setFrameRange(0,9); timeLine.setUpdateInterval(800); QSignalSpy spy(&timeLine, &QTimeLine::frameChanged); @@ -429,14 +636,14 @@ void tst_QTimeLine::frameChanged() timeLine.start(); QTest::qWait(timeLine.duration()/2); QCOMPARE(timeLine.state(), QTimeLine::Running); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QTest::qWait(timeLine.duration()); if (timeLine.state() != QTimeLine::NotRunning) QEXPECT_FAIL("", "QTBUG-24796: QTimeLine runs slower than it should", Abort); QCOMPARE(timeLine.state(), QTimeLine::NotRunning); - if (spy.count() != 1) + if (spy.size() != 1) QEXPECT_FAIL("", "QTBUG-24796: QTimeLine runs slower than it should", Abort); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); // Test what happens when the frames are all emitted well before duration expires. timeLine.setUpdateInterval(5); @@ -445,7 +652,7 @@ void tst_QTimeLine::frameChanged() timeLine.start(); QTest::qWait(timeLine.duration()*2); QCOMPARE(timeLine.state(), QTimeLine::NotRunning); - QCOMPARE(spy.count(), 10); + QCOMPARE(spy.size(), 10); } void tst_QTimeLine::stopped() @@ -458,11 +665,11 @@ void tst_QTimeLine::stopped() timeLine.start(); QTest::qWait(timeLine.duration()*2); QCOMPARE(timeLine.state(), QTimeLine::NotRunning); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); spy.clear(); timeLine.start(); timeLine.stop(); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); timeLine.setDirection(QTimeLine::Backward); QCOMPARE(timeLine.loopCount(), 1); } @@ -474,13 +681,13 @@ void tst_QTimeLine::finished() QSignalSpy spy(&timeLine, &QTimeLine::finished); QVERIFY(spy.isValid()); timeLine.start(); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(timeLine.state(), QTimeLine::NotRunning); spy.clear(); timeLine.start(); timeLine.stop(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QTimeLine::isRunning() @@ -513,13 +720,13 @@ void tst_QTimeLine::multipleTimeLines() timeLine.start(); timeLineKiller.stop(); QTest::qWait(timeLine.duration()*2); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QTimeLine::sineCurve() { QTimeLine timeLine(1000); - timeLine.setCurveShape(QTimeLine::SineCurve); + timeLine.setEasingCurve(QEasingCurve::SineCurve); QCOMPARE(timeLine.valueForTime(0), qreal(0)); QCOMPARE(timeLine.valueForTime(250), qreal(0.5)); QCOMPARE(timeLine.valueForTime(500), qreal(1)); @@ -530,7 +737,7 @@ void tst_QTimeLine::sineCurve() void tst_QTimeLine::cosineCurve() { QTimeLine timeLine(1000); - timeLine.setCurveShape(QTimeLine::CosineCurve); + timeLine.setEasingCurve(QEasingCurve::CosineCurve); QCOMPARE(timeLine.valueForTime(0), qreal(0.5)); QCOMPARE(timeLine.valueForTime(250), qreal(1)); QCOMPARE(timeLine.valueForTime(500), qreal(0.5)); @@ -544,7 +751,7 @@ void tst_QTimeLine::outOfRange() QCOMPARE(timeLine.valueForTime(-100), qreal(0)); QCOMPARE(timeLine.valueForTime(2000), qreal(1)); - timeLine.setCurveShape(QTimeLine::SineCurve); + timeLine.setEasingCurve(QEasingCurve::SineCurve); QCOMPARE(timeLine.valueForTime(2000), qreal(0)); } @@ -659,6 +866,49 @@ void tst_QTimeLine::setPaused() } } +void tst_QTimeLine::automatedBindableTests() +{ + QTimeLine timeLine(200); + + QTestPrivate::testReadWritePropertyBasics(timeLine, 1000, 2000, "duration"); + if (QTest::currentTestFailed()) { + qDebug() << "Failed property test for duration"; + return; + } + + QTestPrivate::testReadWritePropertyBasics(timeLine, 10, 20, "updateInterval"); + if (QTest::currentTestFailed()) { + qDebug() << "Failed property test for updateInterval"; + return; + } + + QTestPrivate::testReadWritePropertyBasics(timeLine, 10, 20, "currentTime"); + if (QTest::currentTestFailed()) { + qDebug() << "Failed property test for currentTime"; + return; + } + + QTestPrivate::testReadWritePropertyBasics(timeLine, QTimeLine::Forward, QTimeLine::Backward, + "direction"); + if (QTest::currentTestFailed()) { + qDebug() << "Failed property test for direction"; + return; + } + + QTestPrivate::testReadWritePropertyBasics(timeLine, 4, 5, "loopCount"); + if (QTest::currentTestFailed()) { + qDebug() << "Failed property test for loopCount"; + return; + } + + QTestPrivate::testReadWritePropertyBasics<QTimeLine, QEasingCurve>( + timeLine, QEasingCurve::InQuad, QEasingCurve::OutQuad, "easingCurve"); + if (QTest::currentTestFailed()) { + qDebug() << "Failed property test for easingCurve"; + return; + } +} + QTEST_MAIN(tst_QTimeLine) #include "tst_qtimeline.moc" diff --git a/tests/auto/corelib/tools/qtyperevision/CMakeLists.txt b/tests/auto/corelib/tools/qtyperevision/CMakeLists.txt new file mode 100644 index 0000000000..527156e3c2 --- /dev/null +++ b/tests/auto/corelib/tools/qtyperevision/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtyperevision LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtyperevision + SOURCES + tst_qtyperevision.cpp + LIBRARIES + Qt::TestPrivate +) diff --git a/tests/auto/corelib/tools/qtyperevision/tst_qtyperevision.cpp b/tests/auto/corelib/tools/qtyperevision/tst_qtyperevision.cpp new file mode 100644 index 0000000000..66c746382a --- /dev/null +++ b/tests/auto/corelib/tools/qtyperevision/tst_qtyperevision.cpp @@ -0,0 +1,202 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2014 Keith Gardner <kreios4004@gmail.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QtCore/qtyperevision.h> +#include <QtTest/private/qcomparisontesthelper_p.h> + +using namespace Qt::StringLiterals; + +class tst_QTypeRevision : public QObject +{ + Q_OBJECT + +private slots: + void qTypeRevision_data(); + void qTypeRevision(); + void qTypeRevisionTypes(); + void qTypeRevisionComparisonCompiles(); + void qTypeRevisionComparison_data(); + void qTypeRevisionComparison(); +}; + +template<typename Integer> +void compileTestRevisionMajorMinor() +{ + const Integer major = 8; + const Integer minor = 4; + + const QTypeRevision r2 = QTypeRevision::fromVersion(major, minor); + QCOMPARE(r2.majorVersion(), 8); + QCOMPARE(r2.minorVersion(), 4); + + const QTypeRevision r3 = QTypeRevision::fromMajorVersion(major); + QCOMPARE(r3.majorVersion(), 8); + QVERIFY(!r3.hasMinorVersion()); + + const QTypeRevision r4 = QTypeRevision::fromMinorVersion(minor); + QVERIFY(!r4.hasMajorVersion()); + QCOMPARE(r4.minorVersion(), 4); +} + + +template<typename Integer> +void compileTestRevision() +{ + if (std::is_signed<Integer>::value) + compileTestRevision<typename QIntegerForSize<sizeof(Integer) / 2>::Signed>(); + else + compileTestRevision<typename QIntegerForSize<sizeof(Integer) / 2>::Unsigned>(); + + const Integer value = 0x0510; + const QTypeRevision r = QTypeRevision::fromEncodedVersion(value); + + QCOMPARE(r.majorVersion(), 5); + QCOMPARE(r.minorVersion(), 16); + QCOMPARE(r.toEncodedVersion<Integer>(), value); + + compileTestRevisionMajorMinor<Integer>(); +} + +template<> +void compileTestRevision<qint16>() +{ + compileTestRevisionMajorMinor<quint8>(); +} + +template<> +void compileTestRevision<quint8>() +{ + compileTestRevisionMajorMinor<quint8>(); +} + +template<> +void compileTestRevision<qint8>() +{ + compileTestRevisionMajorMinor<qint8>(); +} + +void tst_QTypeRevision::qTypeRevision_data() +{ + QTest::addColumn<QTypeRevision>("revision"); + QTest::addColumn<bool>("valid"); + QTest::addColumn<int>("major"); + QTest::addColumn<int>("minor"); + + QTest::addRow("Qt revision") << QTypeRevision::fromVersion(QT_VERSION_MAJOR, QT_VERSION_MINOR) + << true << QT_VERSION_MAJOR << QT_VERSION_MINOR; + QTest::addRow("invalid") << QTypeRevision() << false << 0xff << 0xff; + QTest::addRow("major") << QTypeRevision::fromMajorVersion(6) << true << 6 << 0xff; + QTest::addRow("minor") << QTypeRevision::fromMinorVersion(15) << true << 0xff << 15; + QTest::addRow("zero") << QTypeRevision::fromVersion(0, 0) << true << 0 << 0; + + // We're intentionally not testing negative numbers. + // There are asserts against negative numbers in QTypeRevision. + // You must not pass them as major or minor versions, or values. +} + +void tst_QTypeRevision::qTypeRevision() +{ + const QTypeRevision other = QTypeRevision::fromVersion(127, 128); + + QFETCH(QTypeRevision, revision); + + QFETCH(bool, valid); + QFETCH(int, major); + QFETCH(int, minor); + + QCOMPARE(revision.isValid(), valid); + QCOMPARE(revision.majorVersion(), major); + QCOMPARE(revision.minorVersion(), minor); + + QCOMPARE(revision.hasMajorVersion(), QTypeRevision::isValidSegment(major)); + QCOMPARE(revision.hasMinorVersion(), QTypeRevision::isValidSegment(minor)); + + const QTypeRevision copy = QTypeRevision::fromEncodedVersion(revision.toEncodedVersion<int>()); + QCOMPARE(copy, revision); + + QVERIFY(revision != other); + QVERIFY(copy != other); +} + +void tst_QTypeRevision::qTypeRevisionTypes() +{ + compileTestRevision<quint64>(); + compileTestRevision<qint64>(); + + QVERIFY(!QTypeRevision::isValidSegment(0xff)); + QVERIFY(!QTypeRevision::isValidSegment(-1)); + + const QTypeRevision maxRevision = QTypeRevision::fromVersion(254, 254); + QVERIFY(maxRevision.hasMajorVersion()); + QVERIFY(maxRevision.hasMinorVersion()); +} + +void tst_QTypeRevision::qTypeRevisionComparisonCompiles() +{ + QTestPrivate::testAllComparisonOperatorsCompile<QTypeRevision>(); +} + +void tst_QTypeRevision::qTypeRevisionComparison_data() +{ + QTest::addColumn<QTypeRevision>("lhs"); + QTest::addColumn<QTypeRevision>("rhs"); + QTest::addColumn<Qt::strong_ordering>("expectedResult"); + + static auto versionStr = [](QTypeRevision r) { + QByteArray res = r.hasMajorVersion() ? QByteArray::number(r.majorVersion()) + : "x"_ba; + res.append('.'); + res.append(r.hasMinorVersion() ? QByteArray::number(r.minorVersion()) + : "x"_ba); + return res; + }; + + const QTypeRevision revisions[] = { + QTypeRevision::zero(), + QTypeRevision::fromMajorVersion(0), + QTypeRevision::fromVersion(0, 1), + QTypeRevision::fromVersion(0, 20), + QTypeRevision::fromMinorVersion(0), + QTypeRevision(), + QTypeRevision::fromMinorVersion(1), + QTypeRevision::fromMinorVersion(20), + QTypeRevision::fromVersion(1, 0), + QTypeRevision::fromMajorVersion(1), + QTypeRevision::fromVersion(1, 1), + QTypeRevision::fromVersion(1, 20), + QTypeRevision::fromVersion(20, 0), + QTypeRevision::fromMajorVersion(20), + QTypeRevision::fromVersion(20, 1), + QTypeRevision::fromVersion(20, 20), + }; + + const int length = sizeof(revisions) / sizeof(QTypeRevision); + for (int i = 0; i < length; ++i) { + for (int j = i; j < length; ++j) { + const Qt::strong_ordering expectedRes = (i == j) + ? Qt::strong_ordering::equal + : (i < j) ? Qt::strong_ordering::less + : Qt::strong_ordering::greater; + + const auto lhs = revisions[i]; + const auto rhs = revisions[j]; + QTest::addRow("%s_vs_%s", versionStr(lhs).constData(), versionStr(rhs).constData()) + << lhs << rhs << expectedRes; + } + } +} + +void tst_QTypeRevision::qTypeRevisionComparison() +{ + QFETCH(const QTypeRevision, lhs); + QFETCH(const QTypeRevision, rhs); + QFETCH(const Qt::strong_ordering, expectedResult); + + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs, expectedResult); +} + +QTEST_APPLESS_MAIN(tst_QTypeRevision) + +#include "tst_qtyperevision.moc" diff --git a/tests/auto/corelib/tools/quniquehandle/CMakeLists.txt b/tests/auto/corelib/tools/quniquehandle/CMakeLists.txt new file mode 100644 index 0000000000..fe46826f37 --- /dev/null +++ b/tests/auto/corelib/tools/quniquehandle/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_quniquehandle LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_quniquehandle + SOURCES + tst_quniquehandle.cpp + LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/tools/quniquehandle/tst_quniquehandle.cpp b/tests/auto/corelib/tools/quniquehandle/tst_quniquehandle.cpp new file mode 100644 index 0000000000..ed46999e73 --- /dev/null +++ b/tests/auto/corelib/tools/quniquehandle/tst_quniquehandle.cpp @@ -0,0 +1,308 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <private/quniquehandle_p.h> + +#include <QTest> + +QT_USE_NAMESPACE; + +// clang-format off +namespace GlobalResource { + +std::array<bool, 3> s_resources = { false, false, false }; + +using handle = size_t; +constexpr handle s_invalidHandle = static_cast<handle>(-1); + +handle open() +{ + const auto it = std::find_if(s_resources.begin(), s_resources.end(), + [](bool resource) { + return !resource; + }); + + if (it == s_resources.end()) + return s_invalidHandle; + + *it = true; + + return std::distance(s_resources.begin(), it); +} + +bool open(handle* dest) +{ + const handle resource = open(); + + if (resource == s_invalidHandle) + return false; + + *dest = resource; + return true; +} + +bool close(handle h) +{ + if (h >= s_resources.size()) + return false; // Invalid handle + + if (!s_resources[h]) + return false; // Un-allocated resource + + s_resources[h] = false; + return true; +} + +bool isOpen(handle h) +{ + return s_resources[h]; +} + +void reset() +{ + std::fill(s_resources.begin(), s_resources.end(), false); +} + +bool isReset() +{ + return std::all_of(s_resources.begin(), s_resources.end(), [](bool res) { + return !res; + }); +} + +} // namespace GlobalResource + +struct TestTraits +{ + using Type = GlobalResource::handle; + + static bool close(Type handle) + { + return GlobalResource::close(handle); + } + + static Type invalidValue() noexcept + { + return GlobalResource::s_invalidHandle; + } +}; + +using Handle = QUniqueHandle<TestTraits>; + +class tst_QUniqueHandle : public QObject +{ + Q_OBJECT + +private slots: + + void init() const + { + GlobalResource::reset(); + } + + void cleanup() const + { + QVERIFY(GlobalResource::isReset()); + } + + void defaultConstructor_initializesToInvalidHandle() const + { + const Handle h; + QCOMPARE_EQ(h.get(), TestTraits::invalidValue()); + } + + void constructor_initializesToValid_whenCalledWithValidHandle() const + { + const auto res = GlobalResource::open(); + + const Handle h{ res }; + + QCOMPARE_EQ(h.get(), res); + } + + void copyConstructor_and_assignmentOperator_areDeleted() const + { + static_assert(!std::is_copy_constructible_v<Handle> && !std::is_copy_assignable_v<Handle>); + } + + void moveConstructor_movesOwnershipAndResetsSource() const + { + Handle source{ GlobalResource::open() }; + const Handle dest{ std::move(source) }; + + QVERIFY(!source.isValid()); + QVERIFY(dest.isValid()); + QVERIFY(GlobalResource::isOpen(dest.get())); + } + + void moveAssignment_movesOwnershipAndResetsSource() const + { + Handle source{ GlobalResource::open() }; + Handle dest; + dest = { std::move(source) }; + + QVERIFY(!source.isValid()); + QVERIFY(dest.isValid()); + QVERIFY(GlobalResource::isOpen(dest.get())); + } + + void isValid_returnsFalse_onlyWhenHandleIsInvalid() const + { + const Handle invalid; + QVERIFY(!invalid.isValid()); + + const Handle valid{ GlobalResource::open() }; + QVERIFY(valid.isValid()); + } + + void destructor_callsClose_whenHandleIsValid() + { + { + const Handle h0{ GlobalResource::open() }; + const Handle h1{ GlobalResource::open() }; + const Handle h2{ GlobalResource::open() }; + QVERIFY(!GlobalResource::isReset()); + } + + QVERIFY(GlobalResource::isReset()); + } + + void operatorBool_returnsFalse_onlyWhenHandleIsInvalid() const + { + const Handle invalid; + QVERIFY(!invalid); + + const Handle valid{ GlobalResource::open() }; + QVERIFY(valid); + } + + void get_returnsValue() const + { + const Handle invalid; + QCOMPARE_EQ(invalid.get(), GlobalResource::s_invalidHandle); + + const auto resource = GlobalResource::open(); + const Handle valid{ resource }; + QCOMPARE_EQ(valid.get(), resource); + } + + void reset_resetsPreviousValueAndTakesOwnership() const + { + const auto resource0 = GlobalResource::open(); + const auto resource1 = GlobalResource::open(); + + Handle h1{ resource0 }; + h1.reset(resource1); + + QVERIFY(!GlobalResource::isOpen(resource0)); + QVERIFY(GlobalResource::isOpen(resource1)); + } + + void release_returnsInvalidResource_whenCalledOnInvalidHandle() const + { + Handle h; + QCOMPARE_EQ(h.release(), GlobalResource::s_invalidHandle); + } + + void release_releasesOwnershipAndReturnsResource_whenHandleOwnsObject() const + { + GlobalResource::handle resource{ GlobalResource::open() }; + GlobalResource::handle released{}; + { + Handle h{ resource }; + released = h.release(); + } + QVERIFY(GlobalResource::isOpen(resource)); + QCOMPARE_EQ(resource, released); + + GlobalResource::close(resource); + } + + void swap_swapsOwnership() const + { + const auto resource0 = GlobalResource::open(); + const auto resource1 = GlobalResource::open(); + + Handle h0{ resource0 }; + Handle h1{ resource1 }; + + std::swap(h0, h1); + + QCOMPARE_EQ(h0.get(), resource1); + QCOMPARE_EQ(h1.get(), resource0); + } + + void comparison_behavesAsInt_whenHandleTypeIsInt_data() const + { + QTest::addColumn<int>("lhs"); + QTest::addColumn<int>("rhs"); + + QTest::addRow("lhs == rhs") << 1 << 1; + QTest::addRow("lhs < rhs") << 0 << 1; + QTest::addRow("lhs > rhs") << 1 << 0; + } + + void comparison_behavesAsInt_whenHandleTypeIsInt() const + { + struct IntTraits + { + using Type = int; + + static bool close(Type) + { + return true; + } + + static Type invalidValue() noexcept + { + return INT_MAX; + } + }; + + using Handle = QUniqueHandle<IntTraits>; + + QFETCH(int, lhs); + QFETCH(int, rhs); + + QCOMPARE_EQ(Handle{ lhs } == Handle{ rhs }, lhs == rhs); + QCOMPARE_EQ(Handle{ lhs } != Handle{ rhs }, lhs != rhs); + QCOMPARE_EQ(Handle{ lhs } < Handle{ rhs }, lhs < rhs); + QCOMPARE_EQ(Handle{ lhs } <= Handle{ rhs }, lhs <= rhs); + QCOMPARE_EQ(Handle{ lhs } > Handle{ rhs }, lhs > rhs); + QCOMPARE_EQ(Handle{ lhs } >= Handle{ rhs }, lhs >= rhs); + + QCOMPARE_EQ(Handle{ }, Handle{ }); + } + + void sort_sortsHandles() const + { + const auto resource0 = GlobalResource::open(); + const auto resource1 = GlobalResource::open(); + + QVERIFY(resource1 > resource0); // Precondition of underlying allocator + + Handle h0{ resource0 }; + Handle h1{ resource1 }; + + std::vector<Handle> handles; + handles.push_back(std::move(h1)); + handles.push_back(std::move(h0)); + + std::sort(handles.begin(), handles.end()); + + QCOMPARE_LT(handles.front(), handles.back()); + QCOMPARE_LT(handles.front().get(), handles.back().get()); + } + + void addressOf_returnsAddressOfHandle() const + { + Handle h; + QVERIFY(GlobalResource::open(&h)); + QVERIFY(h.isValid()); + } + +}; + +// clang-format on +QTEST_MAIN(tst_QUniqueHandle) +#include "tst_quniquehandle.moc" diff --git a/tests/auto/corelib/tools/qvarlengtharray/CMakeLists.txt b/tests/auto/corelib/tools/qvarlengtharray/CMakeLists.txt new file mode 100644 index 0000000000..eccb2634cc --- /dev/null +++ b/tests/auto/corelib/tools/qvarlengtharray/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qvarlengtharray Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qvarlengtharray LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qvarlengtharray + SOURCES + tst_qvarlengtharray.cpp +) + +## Scopes: +##################################################################### diff --git a/tests/auto/corelib/tools/qvarlengtharray/qvarlengtharray.pro b/tests/auto/corelib/tools/qvarlengtharray/qvarlengtharray.pro deleted file mode 100644 index 14b2bc213b..0000000000 --- a/tests/auto/corelib/tools/qvarlengtharray/qvarlengtharray.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qvarlengtharray -QT = core testlib -qtConfig(c++14): CONFIG += c++14 -qtConfig(c++1z): CONFIG += c++1z -SOURCES = tst_qvarlengtharray.cpp diff --git a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp index 6220cc766a..6a92663bc4 100644 --- a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp +++ b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp @@ -1,47 +1,92 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> -#include <qvarlengtharray.h> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtTest/QTest> +#include <QVarLengthArray> #include <qvariant.h> +#include <qscopeguard.h> +#include <qscopedvaluerollback.h> +#include <algorithm> +#include <q20iterator.h> #include <memory> +struct Tracker +{ + static int count; + Tracker() { ++count; } + Tracker(const Tracker &) { ++count; } + Tracker(Tracker &&) { ++count; } + + Tracker &operator=(const Tracker &) = default; + Tracker &operator=(Tracker &&) = default; + + ~Tracker() { --count; } + +}; + +int Tracker::count = 0; + +template <typename T> +class ValueTracker +{ + Tracker m_tracker; +public: + ValueTracker() = default; + ValueTracker(T value) : value{std::move(value)} {} + T value; + + friend bool operator==(const ValueTracker &lhs, const ValueTracker &rhs) noexcept + { return lhs.value == rhs.value; } + friend bool operator!=(const ValueTracker &lhs, const ValueTracker &rhs) noexcept + { return !operator==(lhs, rhs); } +}; + +class NonCopyable +{ + Q_DISABLE_COPY(NonCopyable) + int n; +public: + NonCopyable() : n(0) {} + explicit NonCopyable(int n) : n(n) {} + + friend bool operator==(const NonCopyable &lhs, const NonCopyable &rhs) noexcept + { return lhs.n == rhs.n; } + friend bool operator!=(const NonCopyable &lhs, const NonCopyable &rhs) noexcept + { return !operator==(lhs, rhs); } +}; + class tst_QVarLengthArray : public QObject { Q_OBJECT private slots: + void defaultConstructor_int() { defaultConstructor<int>(); } + void defaultConstructor_QString() { defaultConstructor<QString>(); } + void sizeConstructor_int() { sizeConstructor<int>(); } + void sizeConstructor_QString() { sizeConstructor<QString>(); } + void sizeConstructor_NonCopyable() { sizeConstructor<NonCopyable>(); } void append(); + void preallocatedSize(); +#if QT_DEPRECATED_SINCE(6, 3) + void prepend(); +#endif + void emplace(); + void move_int_1() { move_int<1>(); } + void move_int_2() { move_int<2>(); } + void move_int_3() { move_int<3>(); } + void move_QString_1() { move_QString<1>(); } + void move_QString_2() { move_QString<2>(); } + void move_QString_3() { move_QString<3>(); } + void move_Tracker_1() { move_Tracker<1>(); } + void move_Tracker_2() { move_Tracker<2>(); } + void move_Tracker_3() { move_Tracker<3>(); } void removeLast(); void oldTests(); void appendCausingRealloc(); + void appendIsStronglyExceptionSafe(); void resize(); void realloc(); + void iterators(); void reverseIterators(); void count(); void cpp17ctad(); @@ -59,26 +104,74 @@ private slots: void insertMove(); void nonCopyable(); void implicitDefaultCtor(); - + void reserve(); + void value(); + void insert(); + void insert_data(); + void replace(); + void remove(); + void erase(); + + // special cases: + void copesWithCopyabilityOfMoveOnlyVector(); // QTBUG-109745 private: + template <typename T> + void defaultConstructor(); + template <typename T> + void sizeConstructor(); + template <qsizetype N, typename T> + void move(T t1, T t2); + template <qsizetype N> + void move_int() { move<N, int>(42, 24); } + template <qsizetype N> + void move_QString() { move<N, QString>("Hello", "World"); } + template <qsizetype N> + void move_Tracker(); template<typename T> void initializeList(); }; -struct Tracker +template <typename T> +void tst_QVarLengthArray::defaultConstructor() { - static int count; - Tracker() { ++count; } - Tracker(const Tracker &) { ++count; } - Tracker(Tracker &&) { ++count; } - - Tracker &operator=(const Tracker &) = default; - Tracker &operator=(Tracker &&) = default; - - ~Tracker() { --count; } -}; + { + QVarLengthArray<T, 123> vla; + QCOMPARE(vla.size(), 0); + QVERIFY(vla.empty()); + QVERIFY(vla.isEmpty()); + QCOMPARE(vla.begin(), vla.end()); + QCOMPARE(vla.capacity(), 123); + } + { + QVarLengthArray<T> vla; + QCOMPARE(vla.capacity(), 256); // notice, should we change the default + } +} -int Tracker::count = 0; +template <typename T> +void tst_QVarLengthArray::sizeConstructor() +{ + { + QVarLengthArray<T, 123> vla(0); + QCOMPARE(vla.size(), 0); + QVERIFY(vla.empty()); + QVERIFY(vla.isEmpty()); + QCOMPARE(vla.begin(), vla.end()); + QCOMPARE(vla.capacity(), 123); + } + { + QVarLengthArray<T, 124> vla(124); + QCOMPARE(vla.size(), 124); + QVERIFY(!vla.empty()); + QCOMPARE(vla.capacity(), 124); + } + { + QVarLengthArray<T, 124> vla(125); + QCOMPARE(vla.size(), 125); + QVERIFY(!vla.empty()); + QCOMPARE_GE(vla.capacity(), 125); + } +} void tst_QVarLengthArray::append() { @@ -102,6 +195,112 @@ void tst_QVarLengthArray::append() v2.append(5); } +void tst_QVarLengthArray::preallocatedSize() +{ + // The default is 256: + static_assert(QVarLengthArray<int>::PreallocatedSize == 256); + // Otherwise, whatever was given as template argument: + static_assert(QVarLengthArray<int, 42>::PreallocatedSize == 42); + static_assert(QVarLengthArray<int, 1'000'000>::PreallocatedSize == 1'000'000); +} + +#if QT_DEPRECATED_SINCE(6, 3) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED +void tst_QVarLengthArray::prepend() +{ + QVarLengthArray<QString, 2> v; + v.prepend(QString("1")); + v.prepend(v.front()); + QCOMPARE(v.capacity(), 2); + // transition from stack to heap + v.prepend(v.back()); + QVERIFY(v.capacity() > 2); + QCOMPARE(v.front(), v.back()); + while (v.size() < v.capacity()) + v.prepend(v.back()); + QCOMPARE(v.front(), v.back()); + QCOMPARE(v.size(), v.capacity()); + // transition from heap to larger heap: + v.prepend(v.back()); + QCOMPARE(v.front(), v.back()); +} +QT_WARNING_POP +#endif // QT_DEPRECATED_SINCE(6, 3) + +void tst_QVarLengthArray::emplace() +{ + { + QVarLengthArray<QString, 2> strings; + strings.emplace_back(); + QCOMPARE(strings.size(), 1); + QCOMPARE(strings.front().isNull(), true); + strings.emplace(strings.begin(), 42, u'x'); + QCOMPARE(strings.size(), 2); + QCOMPARE(strings.back().isNull(), true); + QCOMPARE(strings.front(), QString(42, u'x')); + auto &r = strings.emplace_back(42, u'y'); + QCOMPARE(&r, &strings.back()); + QCOMPARE(strings.size(), 3); + QCOMPARE(strings.back(), QString(42, u'y')); + + // test growing from empty arrays + QVarLengthArray<QString> emptyArrDefaultPrealloc; + QCOMPARE(emptyArrDefaultPrealloc.size(), 0); + emptyArrDefaultPrealloc.emplace_back(); + QCOMPARE(emptyArrDefaultPrealloc.size(), 1); + emptyArrDefaultPrealloc.resize(1024); + QCOMPARE(emptyArrDefaultPrealloc.size(), 1024); + emptyArrDefaultPrealloc.resize(0); + QCOMPARE(emptyArrDefaultPrealloc.size(), 0); + emptyArrDefaultPrealloc.squeeze(); + QCOMPARE(emptyArrDefaultPrealloc.size(), 0); + emptyArrDefaultPrealloc.emplace_back(); + QCOMPARE(emptyArrDefaultPrealloc.size(), 1); + + QVarLengthArray<QString, 1> emptyArrSmallPrealloc; + QCOMPARE(emptyArrSmallPrealloc.size(), 0); + emptyArrSmallPrealloc.emplace_back(); + QCOMPARE(emptyArrSmallPrealloc.size(), 1); + emptyArrSmallPrealloc.resize(1024); + QCOMPARE(emptyArrSmallPrealloc.size(), 1024); + emptyArrSmallPrealloc.resize(0); + QCOMPARE(emptyArrSmallPrealloc.size(), 0); + emptyArrSmallPrealloc.squeeze(); + QCOMPARE(emptyArrSmallPrealloc.size(), 0); + emptyArrSmallPrealloc.emplace_back(); + QCOMPARE(emptyArrSmallPrealloc.size(), 1); + } +} + +template <qsizetype N> +void tst_QVarLengthArray::move_Tracker() +{ + const auto reset = qScopeGuard([] { Tracker::count = 0; }); + move<N, ValueTracker<int>>({24}, {24}); + QCOMPARE(Tracker::count, 0); +} + +template <qsizetype N, typename T> +void tst_QVarLengthArray::move(T t1, T t2) +{ + { + QVarLengthArray<T, N> v; + v.append(t1); + v.append(t2); + + auto moved = std::move(v); + QCOMPARE(moved.size(), 2); + QCOMPARE(moved[0], t1); + QCOMPARE(moved[1], t2); + + v = std::move(moved); + QCOMPARE(v.size(), 2); + QCOMPARE(v[0], t1); + QCOMPARE(v[1], t2); + } +} + void tst_QVarLengthArray::removeLast() { { @@ -243,10 +442,109 @@ void tst_QVarLengthArray::appendCausingRealloc() QVarLengthArray<float, 1> d(1); for (int i=0; i<30; i++) d.append(i); + + // Regression test for QTBUG-110412: + constexpr qsizetype InitialCapacity = 10; + QVarLengthArray<float, InitialCapacity> d2(InitialCapacity); + std::iota(d2.begin(), d2.end(), 0.0f); + QCOMPARE_EQ(d2.size(), d2.capacity()); // by construction + float floats[1000]; + std::iota(std::begin(floats), std::end(floats), InitialCapacity + 0.0f); + d2.append(floats, q20::ssize(floats)); + QCOMPARE_EQ(d2.size(), q20::ssize(floats) + InitialCapacity); + QCOMPARE_GE(d2.capacity(), d2.size()); +} + +void tst_QVarLengthArray::appendIsStronglyExceptionSafe() +{ +#ifdef QT_NO_EXCEPTIONS + QSKIP("This test requires exception support enabled in the compiler."); +#else + static bool throwOnCopyNow = false; + static bool throwOnMoveNow = false; + struct Thrower { + Thrower() = default; + Thrower(const Thrower &) + { + if (throwOnCopyNow) + throw 1; + } + Thrower &operator=(const Thrower &) = default; + Thrower(Thrower &&) + { + if (throwOnMoveNow) + throw 1; + } + Thrower &operator=(Thrower &&) = default; + ~Thrower() = default; + }; + + { + QVarLengthArray<Thrower, 2> vla(1); + { + Thrower t; + const QScopedValueRollback rb(throwOnCopyNow, true); + QVERIFY_THROWS_EXCEPTION(int, vla.push_back(t)); + QCOMPARE(vla.size(), 1); + } + { + const QScopedValueRollback rb(throwOnMoveNow, true); + QVERIFY_THROWS_EXCEPTION(int, vla.push_back({})); + QCOMPARE(vla.size(), 1); + } + vla.push_back({}); + QCOMPARE(vla.size(), 2); + { + Thrower t; + { + // tests the copy inside append() + const QScopedValueRollback rb(throwOnCopyNow, true); + QVERIFY_THROWS_EXCEPTION(int, vla.push_back(t)); + QCOMPARE(vla.size(), 2); + } + { + // tests the move inside reallocate() + const QScopedValueRollback rb(throwOnMoveNow, true); + QVERIFY_THROWS_EXCEPTION(int, vla.push_back(t)); + QCOMPARE(vla.size(), 2); + } + } + { + const QScopedValueRollback rb(throwOnMoveNow, true); + QVERIFY_THROWS_EXCEPTION(int, vla.push_back({})); + QCOMPARE(vla.size(), 2); + } + } +#endif } void tst_QVarLengthArray::resize() { + // Empty Movable + { + QVarLengthArray<QVariant, 1> values; + QCOMPARE(values.size(), 0); + values.resize(2); + QCOMPARE(values.size(), 2); + QCOMPARE(values[0], QVariant()); + QCOMPARE(values[1], QVariant()); + } + + // Empty POD + { + QVarLengthArray<int, 1> values; + QCOMPARE(values.size(), 0); + values.resize(2); + QCOMPARE(values.size(), 2); + // POD values are uninitialized, but we can check that we can assign + // new values + values[0] = 0; + values[1] = 1; + + QCOMPARE(values[0], 0); + QCOMPARE(values[1], 1); + } + //MOVABLE { QVarLengthArray<QVariant,1> values(1); @@ -346,6 +644,12 @@ struct MyBase bool hasMoved() const { return !wasConstructedAt(this); } protected: + void swap(MyBase &other) { + using std::swap; + swap(data, other.data); + swap(isCopy, other.isCopy); + } + MyBase(const MyBase *data, bool isCopy) : data(data), isCopy(isCopy) {} @@ -420,6 +724,14 @@ struct MyMovable return *this; } + void swap(MyMovable &other) noexcept + { + MyBase::swap(other); + std::swap(i, other.i); + } + + friend void swap(MyMovable &lhs, MyMovable &rhs) noexcept { lhs.swap(rhs); } + bool operator==(const MyMovable &other) const { return i == other.i; @@ -435,24 +747,33 @@ struct MyComplex { return i == other.i; } + + void swap(MyComplex &other) noexcept + { + MyBase::swap(other); + std::swap(i, other.i); + } + + friend void swap(MyComplex &lhs, MyComplex &rhs) noexcept { lhs.swap(rhs); } + char i; }; QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(MyPrimitive, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(MyMovable, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(MyMovable, Q_RELOCATABLE_TYPE); Q_DECLARE_TYPEINFO(MyComplex, Q_COMPLEX_TYPE); QT_END_NAMESPACE bool reallocTestProceed = true; -template <class T, int PreAlloc> -int countMoved(QVarLengthArray<T, PreAlloc> const &c) +template <class T, qsizetype PreAlloc> +qsizetype countMoved(QVarLengthArray<T, PreAlloc> const &c) { - int result = 0; - for (int i = 0; i < c.size(); ++i) + qsizetype result = 0; + for (qsizetype i = 0; i < c.size(); ++i) if (c[i].hasMoved()) ++result; @@ -466,11 +787,11 @@ void reallocTest() typedef QVarLengthArray<T, 16> Container; enum { - isStatic = QTypeInfo<T>::isStatic, + isRelocatable = QTypeInfo<T>::isRelocatable, isComplex = QTypeInfo<T>::isComplex, - isPrimitive = !isComplex && !isStatic, - isMovable = !isStatic + isPrimitive = !isComplex && isRelocatable, + isMovable = isRelocatable }; // Constructors @@ -642,8 +963,53 @@ void tst_QVarLengthArray::realloc() QVERIFY(reallocTestProceed); } +void tst_QVarLengthArray::iterators() +{ + QVarLengthArray<int> emptyArr; + QCOMPARE(emptyArr.constBegin(), emptyArr.constEnd()); + QCOMPARE(emptyArr.cbegin(), emptyArr.cend()); + QCOMPARE(emptyArr.begin(), emptyArr.end()); + + QVarLengthArray<int> arr { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto it = arr.begin(); + auto constIt = arr.cbegin(); + qsizetype idx = 0; + + QCOMPARE(*it, arr[idx]); + QCOMPARE(*constIt, arr[idx]); + + it++; + constIt++; + idx++; + QCOMPARE(*it, arr[idx]); + QCOMPARE(*constIt, arr[idx]); + + it += 5; + constIt += 5; + idx += 5; + QCOMPARE(*it, arr[idx]); + QCOMPARE(*constIt, arr[idx]); + + it -= 3; + constIt -= 3; + idx -= 3; + QCOMPARE(*it, arr[idx]); + QCOMPARE(*constIt, arr[idx]); + + it--; + constIt--; + idx--; + QCOMPARE(*it, arr[idx]); + QCOMPARE(*constIt, arr[idx]); +} + void tst_QVarLengthArray::reverseIterators() { + QVarLengthArray<int> emptyArr; + QCOMPARE(emptyArr.crbegin(), emptyArr.crend()); + QCOMPARE(emptyArr.rbegin(), emptyArr.rend()); + QVarLengthArray<int> v; v << 1 << 2 << 3 << 4; QVarLengthArray<int> vr = v; @@ -662,26 +1028,29 @@ void tst_QVarLengthArray::count() // tests size(), count() and length(), since they're the same thing { const QVarLengthArray<int> list; - QCOMPARE(list.length(), 0); - QCOMPARE(list.count(), 0); QCOMPARE(list.size(), 0); + QCOMPARE(list.size(), 0); + QCOMPARE(list.size(), 0); + QVERIFY(list.isEmpty()); } { QVarLengthArray<int> list; list.append(0); - QCOMPARE(list.length(), 1); - QCOMPARE(list.count(), 1); QCOMPARE(list.size(), 1); + QCOMPARE(list.size(), 1); + QCOMPARE(list.size(), 1); + QVERIFY(!list.isEmpty()); } { QVarLengthArray<int> list; list.append(0); list.append(1); - QCOMPARE(list.length(), 2); - QCOMPARE(list.count(), 2); QCOMPARE(list.size(), 2); + QCOMPARE(list.size(), 2); + QCOMPARE(list.size(), 2); + QVERIFY(!list.isEmpty()); } { @@ -689,9 +1058,10 @@ void tst_QVarLengthArray::count() list.append(0); list.append(0); list.append(0); - QCOMPARE(list.length(), 3); - QCOMPARE(list.count(), 3); QCOMPARE(list.size(), 3); + QCOMPARE(list.size(), 3); + QCOMPARE(list.size(), 3); + QVERIFY(!list.isEmpty()); } // test removals too @@ -700,30 +1070,33 @@ void tst_QVarLengthArray::count() list.append(0); list.append(0); list.append(0); - QCOMPARE(list.length(), 3); - QCOMPARE(list.count(), 3); QCOMPARE(list.size(), 3); + QCOMPARE(list.size(), 3); + QCOMPARE(list.size(), 3); + QVERIFY(!list.isEmpty()); list.removeLast(); - QCOMPARE(list.length(), 2); - QCOMPARE(list.count(), 2); QCOMPARE(list.size(), 2); + QCOMPARE(list.size(), 2); + QCOMPARE(list.size(), 2); + QVERIFY(!list.isEmpty()); list.removeLast(); - QCOMPARE(list.length(), 1); - QCOMPARE(list.count(), 1); QCOMPARE(list.size(), 1); + QCOMPARE(list.size(), 1); + QCOMPARE(list.size(), 1); + QVERIFY(!list.isEmpty()); list.removeLast(); - QCOMPARE(list.length(), 0); - QCOMPARE(list.count(), 0); QCOMPARE(list.size(), 0); + QCOMPARE(list.size(), 0); + QCOMPARE(list.size(), 0); + QVERIFY(list.isEmpty()); } } void tst_QVarLengthArray::cpp17ctad() { -#ifdef __cpp_deduction_guides #define QVERIFY_IS_VLA_OF(obj, Type) \ QVERIFY2((std::is_same<decltype(obj), QVarLengthArray<Type>>::value), \ - QMetaType::typeName(qMetaTypeId<decltype(obj)::value_type>())) + QMetaType::fromType<decltype(obj)::value_type>().name()) #define CHECK(Type, One, Two, Three) \ do { \ const Type v[] = {One, Two, Three}; \ @@ -740,10 +1113,6 @@ void tst_QVarLengthArray::cpp17ctad() CHECK(QString, QStringLiteral("one"), QStringLiteral("two"), QStringLiteral("three")); #undef QVERIFY_IS_VLA_OF #undef CHECK -#else - QSKIP("This test requires C++17 Constructor Template Argument Deduction support enabled in the compiler."); -#endif - } void tst_QVarLengthArray::first() @@ -756,16 +1125,16 @@ void tst_QVarLengthArray::first() QCOMPARE(list.first(), 27); list.append(1987); QCOMPARE(list.first(), 27); - QCOMPARE(list.length(), 3); + QCOMPARE(list.size(), 3); // remove some, make sure it stays sane list.removeLast(); QCOMPARE(list.first(), 27); - QCOMPARE(list.length(), 2); + QCOMPARE(list.size(), 2); list.removeLast(); QCOMPARE(list.first(), 27); - QCOMPARE(list.length(), 1); + QCOMPARE(list.size(), 1); } void tst_QVarLengthArray::last() @@ -778,23 +1147,27 @@ void tst_QVarLengthArray::last() QCOMPARE(list.last(), 4); list.append(1987); QCOMPARE(list.last(), 1987); - QCOMPARE(list.length(), 3); + QCOMPARE(list.size(), 3); // remove some, make sure it stays sane list.removeLast(); QCOMPARE(list.last(), 4); - QCOMPARE(list.length(), 2); + QCOMPARE(list.size(), 2); list.removeLast(); QCOMPARE(list.last(), 27); - QCOMPARE(list.length(), 1); + QCOMPARE(list.size(), 1); } void tst_QVarLengthArray::squeeze() { - QVarLengthArray<int> list; - int sizeOnStack = list.capacity(); - int sizeOnHeap = sizeOnStack * 2; + QVarLengthArray<int, 100> list; + qsizetype sizeOnStack = list.capacity(); + QCOMPARE(sizeOnStack, 100); + list.squeeze(); + QCOMPARE(list.capacity(), sizeOnStack); + + qsizetype sizeOnHeap = sizeOnStack * 2; list.resize(0); QCOMPARE(list.capacity(), sizeOnStack); list.resize(sizeOnHeap); @@ -815,11 +1188,11 @@ void tst_QVarLengthArray::squeeze() void tst_QVarLengthArray::operators() { - QVarLengthArray<QString> myvla; + QVarLengthArray<QString, 6> myvla; myvla << "A" << "B" << "C"; - QVarLengthArray<QString> myvlatwo; + QVarLengthArray<QString, 3> myvlatwo; myvlatwo << "D" << "E" << "F"; - QVarLengthArray<QString> combined; + QVarLengthArray<QString, 7> combined; combined << "A" << "B" << "C" << "D" << "E" << "F"; // != @@ -827,7 +1200,7 @@ void tst_QVarLengthArray::operators() // +=: not provided, emulate //myvla += myvlatwo; - for (const QString &s : qAsConst(myvlatwo)) + for (const QString &s : std::as_const(myvlatwo)) myvla.push_back(s); QCOMPARE(myvla, combined); @@ -859,6 +1232,10 @@ void tst_QVarLengthArray::operators() void tst_QVarLengthArray::indexOf() { QVarLengthArray<QString> myvec; + + QCOMPARE(myvec.indexOf("A"), -1); + QCOMPARE(myvec.indexOf("A", 5), -1); + myvec << "A" << "B" << "C" << "B" << "A"; QVERIFY(myvec.indexOf("B") == 1); @@ -883,6 +1260,10 @@ void tst_QVarLengthArray::indexOf() void tst_QVarLengthArray::lastIndexOf() { QVarLengthArray<QString> myvec; + + QCOMPARE(myvec.lastIndexOf("A"), -1); + QCOMPARE(myvec.lastIndexOf("A", 5), -1); + myvec << "A" << "B" << "C" << "B" << "A"; QVERIFY(myvec.lastIndexOf("B") == 3); @@ -906,6 +1287,10 @@ void tst_QVarLengthArray::lastIndexOf() void tst_QVarLengthArray::contains() { QVarLengthArray<QString> myvec; + + QVERIFY(!myvec.contains(QLatin1String("aaa"))); + QVERIFY(!myvec.contains(QString())); + myvec << "aaa" << "bbb" << "ccc"; QVERIFY(myvec.contains(QLatin1String("aaa"))); @@ -921,6 +1306,9 @@ void tst_QVarLengthArray::contains() void tst_QVarLengthArray::clear() { QVarLengthArray<QString, 5> myvec; + QCOMPARE(myvec.size(), 0); + myvec.clear(); + QCOMPARE(myvec.size(), 0); for (int i = 0; i < 10; ++i) myvec << "aaa"; @@ -1000,6 +1388,17 @@ void tst_QVarLengthArray::insertMove() QCOMPARE(MyBase::copyCount, 0); { + MyMovable m1, m2; + QCOMPARE(MyBase::liveCount, 2); + QCOMPARE(MyBase::copyCount, 0); + using std::swap; + swap(m1, m2); + QCOMPARE(MyBase::liveCount, 2); + QCOMPARE(MyBase::movedCount, 0); + QCOMPARE(MyBase::copyCount, 0); + } + + { QVarLengthArray<MyMovable, 6> vec; MyMovable m1; MyMovable m2; @@ -1025,7 +1424,7 @@ void tst_QVarLengthArray::insertMove() QCOMPARE(MyBase::liveCount, 6); QCOMPARE(MyBase::movedCount, 2); - vec.prepend(std::move(m1)); + vec.insert(vec.cbegin(), std::move(m1)); QVERIFY(m1.wasConstructedAt(nullptr)); QVERIFY(vec.at(0).wasConstructedAt(&m1)); QVERIFY(vec.at(1).wasConstructedAt(&m3)); @@ -1097,7 +1496,7 @@ void tst_QVarLengthArray::nonCopyable() QVERIFY(!val4); QVERIFY(ptr3 == vec.at(0).get()); QVERIFY(ptr4 == vec.at(1).get()); - vec.prepend(std::move(val1)); + vec.insert(vec.cbegin(), std::move(val1)); QVERIFY(!val1); QVERIFY(ptr1 == vec.at(0).get()); QVERIFY(ptr3 == vec.at(1).get()); @@ -1131,5 +1530,239 @@ void tst_QVarLengthArray::implicitDefaultCtor() QCOMPARE(def.size(), 0); } +void tst_QVarLengthArray::reserve() +{ + QVarLengthArray<int, 100> arr; + QCOMPARE(arr.capacity(), 100); + QCOMPARE(arr.size(), 0); + + const auto *stackPtr = arr.constData(); + arr.reserve(50); + // Nothing changed, as we reserve less than pre-allocated + QCOMPARE(arr.capacity(), 100); + QCOMPARE(arr.size(), 0); + QCOMPARE(arr.constData(), stackPtr); + + arr.reserve(150); + // Allocate memory on heap, as we reserve more than pre-allocated + QCOMPARE(arr.capacity(), 150); + QCOMPARE(arr.size(), 0); + const auto *heapPtr = arr.constData(); + QVERIFY(heapPtr != stackPtr); + + arr.reserve(50); + // Nothing changed + QCOMPARE(arr.capacity(), 150); + QCOMPARE(arr.constData(), heapPtr); + + arr.squeeze(); + // After squeeze() we go back to using stack + QCOMPARE(arr.capacity(), 100); + QCOMPARE(arr.constData(), stackPtr); +} + +void tst_QVarLengthArray::value() +{ + const QString def("default value"); + + QVarLengthArray<QString> arr; + QCOMPARE(arr.value(0), QString()); + QCOMPARE(arr.value(1, def), def); + QCOMPARE(arr.value(-1, def), def); + + const qsizetype size = 5; + const QString dataStr("data%1"); + arr.resize(size); + for (qsizetype i = 0; i < size; ++i) + arr[i] = dataStr.arg(i); + + for (qsizetype i = 0; i < size; ++i) + QCOMPARE(arr.value(i, def), dataStr.arg(i)); + + QCOMPARE(arr.value(size + 1), QString()); + QCOMPARE(arr.value(-1, def), def); +} + +void tst_QVarLengthArray::insert() +{ + QFETCH(QVarLengthArray<QString>, arr); + QFETCH(int, pos); + QFETCH(int, count); + QFETCH(QString, data); + QFETCH(QVarLengthArray<QString>, expected); + + // Insert using index + { + QVarLengthArray<QString> copy = arr; + if (count == 1) { + copy.insert(pos, data); + QCOMPARE(copy, expected); + + copy = arr; + QString d = data; + copy.insert(pos, std::move(d)); + QCOMPARE(copy, expected); + } else { + copy.insert(pos, count, data); + QCOMPARE(copy, expected); + } + } + + // Insert using iterator + { + QVarLengthArray<QString> copy = arr; + if (count == 1) { + copy.insert(copy.cbegin() + pos, data); + QCOMPARE(copy, expected); + + copy = arr; + QString d = data; + copy.insert(copy.cbegin() + pos, std::move(d)); + QCOMPARE(copy, expected); + } else { + copy.insert(copy.cbegin() + pos, count, data); + QCOMPARE(copy, expected); + } + } +} + +void tst_QVarLengthArray::insert_data() +{ + QTest::addColumn<QVarLengthArray<QString>>("arr"); + QTest::addColumn<int>("pos"); + QTest::addColumn<int>("count"); + QTest::addColumn<QString>("data"); + QTest::addColumn<QVarLengthArray<QString>>("expected"); + + const QString data("Test"); + + QTest::newRow("empty") + << QVarLengthArray<QString>() << 0 << 1 << data << QVarLengthArray<QString>({ data }); + QTest::newRow("empty-none") + << QVarLengthArray<QString>() << 0 << 0 << data << QVarLengthArray<QString>(); + QTest::newRow("begin") + << QVarLengthArray<QString>({ "value1", "value2" }) << 0 << 1 << data + << QVarLengthArray<QString>({ data, "value1", "value2" }); + QTest::newRow("end") + << QVarLengthArray<QString>({ "value1", "value2" }) << 2 << 1 << data + << QVarLengthArray<QString>({ "value1", "value2", data }); + QTest::newRow("middle") + << QVarLengthArray<QString>({ "value1", "value2" }) << 1 << 1 << data + << QVarLengthArray<QString>({ "value1", data, "value2" }); + QTest::newRow("begin-none") + << QVarLengthArray<QString>({ "value1", "value2" }) << 0 << 0 << data + << QVarLengthArray<QString>({ "value1", "value2" }); + QTest::newRow("end-none") + << QVarLengthArray<QString>({ "value1", "value2" }) << 2 << 0 << data + << QVarLengthArray<QString>({ "value1", "value2" }); + QTest::newRow("middle-none") + << QVarLengthArray<QString>({ "value1", "value2" }) << 1 << 0 << data + << QVarLengthArray<QString>({ "value1", "value2" }); + QTest::newRow("multi begin") + << QVarLengthArray<QString>({ "value1", "value2" }) << 0 << 2 << data + << QVarLengthArray<QString>({ data, data, "value1", "value2" }); + QTest::newRow("multi end") + << QVarLengthArray<QString>({ "value1", "value2" }) << 2 << 2 << data + << QVarLengthArray<QString>({ "value1", "value2", data, data }); + QTest::newRow("multi middle") + << QVarLengthArray<QString>({ "value1", "value2" }) << 1 << 2 << data + << QVarLengthArray<QString>({ "value1", data, data, "value2" }); +} + +void tst_QVarLengthArray::replace() +{ + QVarLengthArray<QString> arr({ "val0", "val1", "val2" }); + + arr.replace(0, "data0"); + QCOMPARE(arr, QVarLengthArray<QString>({ "data0", "val1", "val2" })); + + arr.replace(2, "data2"); + QCOMPARE(arr, QVarLengthArray<QString>({ "data0", "val1", "data2" })); + + arr.replace(1, "data1"); + QCOMPARE(arr, QVarLengthArray<QString>({ "data0", "data1", "data2" })); +} + +void tst_QVarLengthArray::remove() +{ + auto isVal2 = [](const QString &str) { return str == "val2"; }; + + QVarLengthArray<QString> arr; + QCOMPARE(arr.removeAll("val0"), 0); + QVERIFY(!arr.removeOne("val1")); + QCOMPARE(arr.removeIf(isVal2), 0); + + arr << "val0" << "val1" << "val2"; + arr << "val0" << "val1" << "val2"; + arr << "val0" << "val1" << "val2"; + + QCOMPARE(arr.size(), 9); + + arr.remove(1, 3); + QCOMPARE(arr, QVarLengthArray<QString>({ "val0", "val1", "val2", "val0", "val1", "val2" })); + + arr.remove(2); + QCOMPARE(arr, QVarLengthArray<QString>({ "val0", "val1", "val0", "val1", "val2" })); + + QVERIFY(arr.removeOne("val1")); + QCOMPARE(arr, QVarLengthArray<QString>({ "val0", "val0", "val1", "val2" })); + + QCOMPARE(arr.removeAll("val0"), 2); + QCOMPARE(arr, QVarLengthArray<QString>({ "val1", "val2" })); + + QCOMPARE(arr.removeIf(isVal2), 1); + QCOMPARE(arr, QVarLengthArray<QString>({ "val1" })); + + arr.removeLast(); + QVERIFY(arr.isEmpty()); +} + +void tst_QVarLengthArray::erase() +{ + QVarLengthArray<QString> arr; + QCOMPARE(arr.erase(arr.cbegin(), arr.cend()), arr.cend()); + + arr << "val0" << "val1" << "val2"; + arr << "val0" << "val1" << "val2"; + arr << "val0" << "val1" << "val2"; + + auto it = arr.erase(arr.cbegin() + 1, arr.cend() - 3); + QCOMPARE(it, arr.cend() - 3); + QCOMPARE(arr, QVarLengthArray<QString>({ "val0", "val0", "val1", "val2" })); + + it = arr.erase(arr.cbegin()); + QCOMPARE(it, arr.cbegin()); + QCOMPARE(arr, QVarLengthArray<QString>({ "val0", "val1", "val2" })); + + it = arr.erase(arr.cbegin() + 1); + QCOMPARE(it, arr.cend() - 1); + QCOMPARE(arr, QVarLengthArray<QString>({ "val0", "val2" })); + + it = arr.erase(arr.cend() - 1); + QCOMPARE(it, arr.cend()); + QCOMPARE(arr, QVarLengthArray<QString>({ "val0" })); +} + +void tst_QVarLengthArray::copesWithCopyabilityOfMoveOnlyVector() +{ + // std::vector<move-only-type> is_copyable + // (https://quuxplusone.github.io/blog/2020/02/05/vector-is-copyable-except-when-its-not/) + + QVarLengthArray<std::vector<std::unique_ptr<int>>, 2> vla; + vla.emplace_back(42); + vla.emplace_back(43); + vla.emplace_back(44); // goes to the heap + QCOMPARE_EQ(vla.size(), 3); + QCOMPARE_EQ(vla.front().size(), 42U); + QCOMPARE_EQ(vla.front().front(), nullptr); + QCOMPARE_EQ(vla.back().size(), 44U); + + auto moved = std::move(vla); + QCOMPARE_EQ(moved.size(), 3); + QCOMPARE_EQ(moved.front().size(), 42U); + QCOMPARE_EQ(moved.front().front(), nullptr); + QCOMPARE_EQ(moved.back().size(), 44U); +} + QTEST_APPLESS_MAIN(tst_QVarLengthArray) #include "tst_qvarlengtharray.moc" diff --git a/tests/auto/corelib/tools/qvector/qvector.pro b/tests/auto/corelib/tools/qvector/qvector.pro deleted file mode 100644 index 689d9b87a2..0000000000 --- a/tests/auto/corelib/tools/qvector/qvector.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -qtConfig(c++11): CONFIG += c++11 -qtConfig(c++14): CONFIG += c++14 -qtConfig(c++1z): CONFIG += c++1z -TARGET = tst_qvector -QT = core testlib -SOURCES = $$PWD/tst_qvector.cpp diff --git a/tests/auto/corelib/tools/qvector/tst_qvector.cpp b/tests/auto/corelib/tools/qvector/tst_qvector.cpp deleted file mode 100644 index 7a69e844d4..0000000000 --- a/tests/auto/corelib/tools/qvector/tst_qvector.cpp +++ /dev/null @@ -1,2649 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> -#include <QAtomicInt> -#include <QThread> -#include <QSemaphore> -#include <qvector.h> - -struct Movable { - Movable(char input = 'j') - : i(input) - , that(this) - , state(Constructed) - { - counter.fetchAndAddRelaxed(1); - } - Movable(const Movable &other) - : i(other.i) - , that(this) - , state(Constructed) - { - check(other.state, Constructed); - counter.fetchAndAddRelaxed(1); - } - Movable(Movable &&other) - : i(other.i) - , that(other.that) - , state(Constructed) - { - check(other.state, Constructed); - counter.fetchAndAddRelaxed(1); - other.that = nullptr; - } - - ~Movable() - { - check(state, Constructed); - i = 0; - counter.fetchAndAddRelaxed(-1); - state = Destructed; - } - - bool operator ==(const Movable &other) const - { - check(state, Constructed); - check(other.state, Constructed); - return i == other.i; - } - - Movable &operator=(const Movable &other) - { - check(state, Constructed); - check(other.state, Constructed); - i = other.i; - that = this; - return *this; - } - Movable &operator=(Movable &&other) - { - check(state, Constructed); - check(other.state, Constructed); - i = other.i; - that = other.that; - other.that = nullptr; - return *this; - } - bool wasConstructedAt(const Movable *other) const - { - return that == other; - } - char i; - static QAtomicInt counter; -private: - Movable *that; // used to check if an instance was moved - - enum State { Constructed = 106, Destructed = 110 }; - State state; - - static void check(const State state1, const State state2) - { - QCOMPARE(int(state1), int(state2)); - } -}; - -inline uint qHash(const Movable &key, uint seed = 0) { return qHash(key.i, seed); } - -QAtomicInt Movable::counter = 0; -QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE); -QT_END_NAMESPACE -Q_DECLARE_METATYPE(Movable); - -struct Custom { - Custom(char input = 'j') - : i(input) - , that(this) - , state(Constructed) - { - counter.fetchAndAddRelaxed(1); - } - Custom(const Custom &other) - : that(this) - , state(Constructed) - { - check(&other); - counter.fetchAndAddRelaxed(1); - this->i = other.i; - } - ~Custom() - { - check(this); - i = 0; - counter.fetchAndAddRelaxed(-1); - state = Destructed; - } - - bool operator ==(const Custom &other) const - { - check(&other); - check(this); - return i == other.i; - } - - bool operator<(const Custom &other) const - { - check(&other); - check(this); - return i < other.i; - } - - Custom &operator=(const Custom &other) - { - check(&other); - check(this); - i = other.i; - return *this; - } - static QAtomicInt counter; - - char i; // used to identify orgin of an instance -private: - Custom *that; // used to check if an instance was moved - - enum State { Constructed = 106, Destructed = 110 }; - State state; - - static void check(const Custom *c) - { - // check if c object has been moved - QCOMPARE(c, c->that); - QCOMPARE(int(c->state), int(Constructed)); - } -}; -QAtomicInt Custom::counter = 0; - -inline uint qHash(const Custom &key, uint seed = 0) { return qHash(key.i, seed); } - -Q_DECLARE_METATYPE(Custom); - -// tests depends on the fact that: -Q_STATIC_ASSERT(!QTypeInfo<int>::isStatic); -Q_STATIC_ASSERT(!QTypeInfo<int>::isComplex); -Q_STATIC_ASSERT(!QTypeInfo<Movable>::isStatic); -Q_STATIC_ASSERT(QTypeInfo<Movable>::isComplex); -Q_STATIC_ASSERT(QTypeInfo<Custom>::isStatic); -Q_STATIC_ASSERT(QTypeInfo<Custom>::isComplex); - - -class tst_QVector : public QObject -{ - Q_OBJECT -private slots: - void constructors_empty() const; - void constructors_emptyReserveZero() const; - void constructors_emptyReserve() const; - void constructors_reserveAndInitialize() const; - void copyConstructorInt() const; - void copyConstructorMovable() const; - void copyConstructorCustom() const; - void assignmentInt() const; - void assignmentMovable() const; - void assignmentCustom() const; - void assignFromInitializerListInt() const; - void assignFromInitializerListMovable() const; - void assignFromInitializerListCustom() const; - void addInt() const; - void addMovable() const; - void addCustom() const; - void appendInt() const; - void appendMovable() const; - void appendCustom() const; - void appendRvalue() const; - void at() const; - void capacityInt() const; - void capacityMovable() const; - void capacityCustom() const; - void clearInt() const; - void clearMovable() const; - void clearCustom() const; - void constData() const; - void constFirst() const; - void constLast() const; - void contains() const; - void countInt() const; - void countMovable() const; - void countCustom() const; - void cpp17ctad() const; - void data() const; - void emptyInt() const; - void emptyMovable() const; - void emptyCustom() const; - void endsWith() const; - void eraseEmptyInt() const; - void eraseEmptyMovable() const; - void eraseEmptyCustom() const; - void eraseEmptyReservedInt() const; - void eraseEmptyReservedMovable() const; - void eraseEmptyReservedCustom() const; - void eraseInt() const; - void eraseIntShared() const; - void eraseMovable() const; - void eraseMovableShared() const; - void eraseCustom() const; - void eraseCustomShared() const; - void eraseReservedInt() const; - void eraseReservedMovable() const; - void eraseReservedCustom() const; - void fillInt() const; - void fillMovable() const; - void fillCustom() const; - void fillDetaches() const; - void first() const; - void fromListInt() const; - void fromListMovable() const; - void fromListCustom() const; - void indexOf() const; - void insertInt() const; - void insertMovable() const; - void insertCustom() const; - void isEmpty() const; - void last() const; - void lastIndexOf() const; - void mid() const; - void moveInt() const; - void moveMovable() const; - void moveCustom() const; - void prependInt() const; - void prependMovable() const; - void prependCustom() const; - void qhashInt() const { qhash<int>(); } - void qhashMovable() const { qhash<Movable>(); } - void qhashCustom() const { qhash<Custom>(); } - void removeAllWithAlias() const; - void removeInt() const; - void removeMovable() const; - void removeCustom() const; - void removeFirstLast() const; - void resizePOD_data() const; - void resizePOD() const; - void resizeComplexMovable_data() const; - void resizeComplexMovable() const; - void resizeComplex_data() const; - void resizeComplex() const; - void resizeCtorAndDtor() const; - void reverseIterators() const; - void sizeInt() const; - void sizeMovable() const; - void sizeCustom() const; - void startsWith() const; - void swapInt() const; - void swapMovable() const; - void swapCustom() const; - void toList() const; -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) - void fromStdVector() const; - void toStdVector() const; -#endif - void value() const; - - void testOperators() const; - - void reserve(); - void reserveZero(); - void initializeListInt(); - void initializeListMovable(); - void initializeListCustom(); - - void const_shared_null(); - - void detachInt() const; - void detachMovable() const; - void detachCustom() const; - void detachThreadSafetyInt() const; - void detachThreadSafetyMovable() const; - void detachThreadSafetyCustom() const; - - void insertMove() const; - - void swapItemsAt() const; - -private: - template<typename T> void copyConstructor() const; - template<typename T> void add() const; - template<typename T> void append() const; - template<typename T> void assignFromInitializerList() const; - template<typename T> void capacity() const; - template<typename T> void clear() const; - template<typename T> void count() const; - template<typename T> void empty() const; - template<typename T> void eraseEmpty() const; - template<typename T> void eraseEmptyReserved() const; - template<typename T> void erase(bool shared) const; - template<typename T> void eraseReserved() const; - template<typename T> void fill() const; - template<typename T> void fromList() const; - template<typename T> void insert() const; - template<typename T> void qhash() const; - template<typename T> void move() const; - template<typename T> void prepend() const; - template<typename T> void remove() const; - template<typename T> void size() const; - template<typename T> void swap() const; - template<typename T> void initializeList(); - template<typename T> void detach() const; - template<typename T> void detachThreadSafety() const; -}; - - -template<typename T> struct SimpleValue -{ - static T at(int index) - { - return Values[index % MaxIndex]; - } - - static QVector<T> vector(int size) - { - QVector<T> ret; - for (int i = 0; i < size; i++) - ret.append(at(i)); - return ret; - } - - static const uint MaxIndex = 6; - static const T Values[MaxIndex]; -}; - -template<> -const int SimpleValue<int>::Values[] = { 110, 105, 101, 114, 111, 98 }; -template<> -const Movable SimpleValue<Movable>::Values[] = { 110, 105, 101, 114, 111, 98 }; -template<> -const Custom SimpleValue<Custom>::Values[] = { 110, 105, 101, 114, 111, 98 }; - -// Make some macros for the tests to use in order to be slightly more readable... -#define T_FOO SimpleValue<T>::at(0) -#define T_BAR SimpleValue<T>::at(1) -#define T_BAZ SimpleValue<T>::at(2) -#define T_CAT SimpleValue<T>::at(3) -#define T_DOG SimpleValue<T>::at(4) -#define T_BLAH SimpleValue<T>::at(5) - -void tst_QVector::constructors_empty() const -{ - QVector<int> emptyInt; - QVector<Movable> emptyMovable; - QVector<Custom> emptyCustom; -} - -void tst_QVector::constructors_emptyReserveZero() const -{ - QVector<int> emptyInt(0); - QVector<Movable> emptyMovable(0); - QVector<Custom> emptyCustom(0); -} - -void tst_QVector::constructors_emptyReserve() const -{ - // pre-reserve capacity - QVector<int> myInt(5); - QVERIFY(myInt.capacity() == 5); - QVector<Movable> myMovable(5); - QVERIFY(myMovable.capacity() == 5); - QVector<Custom> myCustom(4); - QVERIFY(myCustom.capacity() == 4); -} - -void tst_QVector::constructors_reserveAndInitialize() const -{ - // default-initialise items - - QVector<int> myInt(5, 42); - QVERIFY(myInt.capacity() == 5); - foreach (int meaningoflife, myInt) { - QCOMPARE(meaningoflife, 42); - } - - QVector<QString> myString(5, QString::fromLatin1("c++")); - QVERIFY(myString.capacity() == 5); - // make sure all items are initialised ok - foreach (QString meaningoflife, myString) { - QCOMPARE(meaningoflife, QString::fromLatin1("c++")); - } - - QVector<Custom> myCustom(5, Custom('n')); - QVERIFY(myCustom.capacity() == 5); - // make sure all items are initialised ok - foreach (Custom meaningoflife, myCustom) { - QCOMPARE(meaningoflife.i, 'n'); - } -} - -template<typename T> -void tst_QVector::copyConstructor() const -{ - T value1(SimpleValue<T>::at(0)); - T value2(SimpleValue<T>::at(1)); - T value3(SimpleValue<T>::at(2)); - T value4(SimpleValue<T>::at(3)); - { - QVector<T> v1; - QVector<T> v2(v1); - QCOMPARE(v1, v2); - } - { - QVector<T> v1; - v1 << value1 << value2 << value3 << value4; - QVector<T> v2(v1); - QCOMPARE(v1, v2); - } -} - -void tst_QVector::copyConstructorInt() const -{ - copyConstructor<int>(); -} - -void tst_QVector::copyConstructorMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - copyConstructor<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::copyConstructorCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - copyConstructor<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -template <class T> -static inline void testAssignment() -{ - QVector<T> v1(5); - QCOMPARE(v1.size(), 5); - QVERIFY(v1.isDetached()); - - QVector<T> v2(7); - QCOMPARE(v2.size(), 7); - QVERIFY(v2.isDetached()); - - QVERIFY(!v1.isSharedWith(v2)); - - v1 = v2; - - QVERIFY(!v1.isDetached()); - QVERIFY(!v2.isDetached()); - QVERIFY(v1.isSharedWith(v2)); - - const void *const data1 = v1.constData(); - const void *const data2 = v2.constData(); - - QCOMPARE(data1, data2); - - v1.clear(); - - QVERIFY(v2.isDetached()); - QVERIFY(!v1.isSharedWith(v2)); - QCOMPARE((void *)v2.constData(), data2); -} - -void tst_QVector::assignmentInt() const -{ - testAssignment<int>(); -} - -void tst_QVector::assignmentMovable() const -{ - testAssignment<Movable>(); -} - -void tst_QVector::assignmentCustom() const -{ - testAssignment<Custom>(); -} - -template<typename T> -void tst_QVector::assignFromInitializerList() const -{ - T val1(SimpleValue<T>::at(1)); - T val2(SimpleValue<T>::at(2)); - T val3(SimpleValue<T>::at(3)); - - QVector<T> v1 = {val1, val2, val3}; - QCOMPARE(v1, QVector<T>() << val1 << val2 << val3); - QCOMPARE(v1, (QVector<T> {val1, val2, val3})); - - v1 = {}; - QCOMPARE(v1.size(), 0); -} - -void tst_QVector::assignFromInitializerListInt() const -{ - assignFromInitializerList<int>(); -} - -void tst_QVector::assignFromInitializerListMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - assignFromInitializerList<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::assignFromInitializerListCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - assignFromInitializerList<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -template<typename T> -void tst_QVector::add() const -{ - { - QVector<T> empty1; - QVector<T> empty2; - QVERIFY((empty1 + empty2).isEmpty()); - empty1 += empty2; - QVERIFY(empty1.isEmpty()); - QVERIFY(empty2.isEmpty()); - } - { - QVector<T> v(12); - QVector<T> empty; - QCOMPARE((v + empty), v); - v += empty; - QVERIFY(!v.isEmpty()); - QCOMPARE(v.size(), 12); - QVERIFY(empty.isEmpty()); - } - { - QVector<T> v1(12); - QVector<T> v2; - v2 += v1; - QVERIFY(!v1.isEmpty()); - QCOMPARE(v1.size(), 12); - QVERIFY(!v2.isEmpty()); - QCOMPARE(v2.size(), 12); - } -} - -void tst_QVector::addInt() const -{ - add<int>(); -} - -void tst_QVector::addMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - add<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::addCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - add<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -template<typename T> -void tst_QVector::append() const -{ - { - QVector<T> myvec; - myvec.append(SimpleValue<T>::at(0)); - QVERIFY(myvec.size() == 1); - myvec.append(SimpleValue<T>::at(1)); - QVERIFY(myvec.size() == 2); - myvec.append(SimpleValue<T>::at(2)); - QVERIFY(myvec.size() == 3); - - QCOMPARE(myvec, QVector<T>() << SimpleValue<T>::at(0) - << SimpleValue<T>::at(1) - << SimpleValue<T>::at(2)); - } - { - QVector<T> v(2); - v.append(SimpleValue<T>::at(0)); - QVERIFY(v.size() == 3); - QCOMPARE(v.at(v.size() - 1), SimpleValue<T>::at(0)); - } - { - QVector<T> v(2); - v.reserve(12); - v.append(SimpleValue<T>::at(0)); - QVERIFY(v.size() == 3); - QCOMPARE(v.at(v.size() - 1), SimpleValue<T>::at(0)); - } - { - QVector<int> v; - v << 1 << 2 << 3; - QVector<int> x; - x << 4 << 5 << 6; - v.append(x); - - QVector<int> combined; - combined << 1 << 2 << 3 << 4 << 5 << 6; - - QCOMPARE(v, combined); - } -} - -void tst_QVector::appendInt() const -{ - append<int>(); -} - -void tst_QVector::appendMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - append<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::appendCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - append<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -void tst_QVector::appendRvalue() const -{ - QVector<QString> v; - v.append("hello"); - QString world = "world"; - v.append(std::move(world)); - QVERIFY(world.isEmpty()); - QCOMPARE(v.front(), QString("hello")); - QCOMPARE(v.back(), QString("world")); -} - -void tst_QVector::at() const -{ - QVector<QString> myvec; - myvec << "foo" << "bar" << "baz"; - - QVERIFY(myvec.size() == 3); - QCOMPARE(myvec.at(0), QLatin1String("foo")); - QCOMPARE(myvec.at(1), QLatin1String("bar")); - QCOMPARE(myvec.at(2), QLatin1String("baz")); - - // append an item - myvec << "hello"; - QVERIFY(myvec.size() == 4); - QCOMPARE(myvec.at(0), QLatin1String("foo")); - QCOMPARE(myvec.at(1), QLatin1String("bar")); - QCOMPARE(myvec.at(2), QLatin1String("baz")); - QCOMPARE(myvec.at(3), QLatin1String("hello")); - - // remove an item - myvec.remove(1); - QVERIFY(myvec.size() == 3); - QCOMPARE(myvec.at(0), QLatin1String("foo")); - QCOMPARE(myvec.at(1), QLatin1String("baz")); - QCOMPARE(myvec.at(2), QLatin1String("hello")); -} - -template<typename T> -void tst_QVector::capacity() const -{ - QVector<T> myvec; - - // TODO: is this guaranteed? seems a safe assumption, but I suppose preallocation of a - // few items isn't an entirely unforseeable possibility. - QVERIFY(myvec.capacity() == 0); - - // test it gets a size - myvec << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2); - QVERIFY(myvec.capacity() >= 3); - - // make sure it grows ok - myvec << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2); - QVERIFY(myvec.capacity() >= 6); - // let's try squeeze a bit - myvec.remove(3); - myvec.remove(3); - myvec.remove(3); - myvec.squeeze(); - QVERIFY(myvec.capacity() >= 3); - - myvec.remove(0); - myvec.remove(0); - myvec.remove(0); - myvec.squeeze(); - QVERIFY(myvec.capacity() == 0); -} - -void tst_QVector::capacityInt() const -{ - capacity<int>(); -} - -void tst_QVector::capacityMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - capacity<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::capacityCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - capacity<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -template<typename T> -void tst_QVector::clear() const -{ - QVector<T> myvec; - myvec << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2); - - const auto oldCapacity = myvec.capacity(); - QCOMPARE(myvec.size(), 3); - myvec.clear(); - QCOMPARE(myvec.size(), 0); - QCOMPARE(myvec.capacity(), oldCapacity); -} - -void tst_QVector::clearInt() const -{ - clear<int>(); -} - -void tst_QVector::clearMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - clear<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::clearCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - clear<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -void tst_QVector::constData() const -{ - int arr[] = { 42, 43, 44 }; - QVector<int> myvec; - myvec << 42 << 43 << 44; - - QVERIFY(memcmp(myvec.constData(), reinterpret_cast<const int *>(&arr), sizeof(int) * 3) == 0); -} - -void tst_QVector::contains() const -{ - QVector<QString> myvec; - myvec << "aaa" << "bbb" << "ccc"; - - QVERIFY(myvec.contains(QLatin1String("aaa"))); - QVERIFY(myvec.contains(QLatin1String("bbb"))); - QVERIFY(myvec.contains(QLatin1String("ccc"))); - QVERIFY(!myvec.contains(QLatin1String("I don't exist"))); - - // add it and make sure it does :) - myvec.append(QLatin1String("I don't exist")); - QVERIFY(myvec.contains(QLatin1String("I don't exist"))); -} - -template<typename T> -void tst_QVector::count() const -{ - // total size - { - // zero size - QVector<T> myvec; - QVERIFY(myvec.count() == 0); - - // grow - myvec.append(SimpleValue<T>::at(0)); - QVERIFY(myvec.count() == 1); - myvec.append(SimpleValue<T>::at(1)); - QVERIFY(myvec.count() == 2); - - // shrink - myvec.remove(0); - QVERIFY(myvec.count() == 1); - myvec.remove(0); - QVERIFY(myvec.count() == 0); - } - - // count of items - { - QVector<T> myvec; - myvec << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2); - - // initial tests - QVERIFY(myvec.count(SimpleValue<T>::at(0)) == 1); - QVERIFY(myvec.count(SimpleValue<T>::at(3)) == 0); - - // grow - myvec.append(SimpleValue<T>::at(0)); - QVERIFY(myvec.count(SimpleValue<T>::at(0)) == 2); - - // shrink - myvec.remove(0); - QVERIFY(myvec.count(SimpleValue<T>::at(0)) == 1); - } -} - -void tst_QVector::countInt() const -{ - count<int>(); -} - -void tst_QVector::countMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - count<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::countCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - count<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -void tst_QVector::cpp17ctad() const -{ -#ifdef __cpp_deduction_guides -#define QVERIFY_IS_VECTOR_OF(obj, Type) \ - QVERIFY2((std::is_same<decltype(obj), QVector<Type>>::value), \ - QMetaType::typeName(qMetaTypeId<decltype(obj)::value_type>())) -#define CHECK(Type, One, Two, Three) \ - do { \ - const Type v[] = {One, Two, Three}; \ - QVector v1 = {One, Two, Three}; \ - QVERIFY_IS_VECTOR_OF(v1, Type); \ - QVector v2(v1.begin(), v1.end()); \ - QVERIFY_IS_VECTOR_OF(v2, Type); \ - QVector v3(std::begin(v), std::end(v)); \ - QVERIFY_IS_VECTOR_OF(v3, Type); \ - } while (false) \ - /*end*/ - CHECK(int, 1, 2, 3); - CHECK(double, 1.0, 2.0, 3.0); - CHECK(QString, QStringLiteral("one"), QStringLiteral("two"), QStringLiteral("three")); -#undef QVERIFY_IS_VECTOR_OF -#undef CHECK -#else - QSKIP("This test requires C++17 Constructor Template Argument Deduction support enabled in the compiler."); -#endif -} - -void tst_QVector::data() const -{ - QVector<int> myvec; - myvec << 42 << 43 << 44; - - // make sure it starts off ok - QCOMPARE(*(myvec.data() + 1), 43); - - // alter it - *(myvec.data() + 1) = 69; - - // check it altered - QCOMPARE(*(myvec.data() + 1), 69); - - int arr[] = { 42, 69, 44 }; - QVERIFY(memcmp(myvec.data(), reinterpret_cast<int *>(&arr), sizeof(int) * 3) == 0); -} - -template<typename T> -void tst_QVector::empty() const -{ - QVector<T> myvec; - - // starts empty - QVERIFY(myvec.empty()); - - // not empty - myvec.append(SimpleValue<T>::at(2)); - QVERIFY(!myvec.empty()); - - // empty again - myvec.remove(0); - QVERIFY(myvec.empty()); -} - -void tst_QVector::emptyInt() const -{ - empty<int>(); -} - -void tst_QVector::emptyMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - empty<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::emptyCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - empty<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -void tst_QVector::endsWith() const -{ - QVector<int> myvec; - - // empty vector - QVERIFY(!myvec.endsWith(1)); - - // add the one, should work - myvec.append(1); - QVERIFY(myvec.endsWith(1)); - - // add something else, fails now - myvec.append(3); - QVERIFY(!myvec.endsWith(1)); - - // remove it again :) - myvec.remove(1); - QVERIFY(myvec.endsWith(1)); -} - -template<typename T> -void tst_QVector::eraseEmpty() const -{ - QVector<T> v; - v.erase(v.begin(), v.end()); - QCOMPARE(v.size(), 0); -} - -void tst_QVector::eraseEmptyInt() const -{ - eraseEmpty<int>(); -} - -void tst_QVector::eraseEmptyMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - eraseEmpty<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::eraseEmptyCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - eraseEmpty<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -template<typename T> -void tst_QVector::eraseEmptyReserved() const -{ - QVector<T> v; - v.reserve(10); - v.erase(v.begin(), v.end()); - QCOMPARE(v.size(), 0); -} - -void tst_QVector::eraseEmptyReservedInt() const -{ - eraseEmptyReserved<int>(); -} - -void tst_QVector::eraseEmptyReservedMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - eraseEmptyReserved<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::eraseEmptyReservedCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - eraseEmptyReserved<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -template<typename T> -struct SharedVectorChecker -{ - SharedVectorChecker(const QVector<T> &original, bool doCopyVector) - : originalSize(-1), - copy(0) - { - if (doCopyVector) { - originalSize = original.size(); - copy = new QVector<T>(original); - // this is unlikely to fail, but if the check in the destructor fails it's good to know that - // we were still alright here. - QCOMPARE(originalSize, copy->size()); - } - } - - ~SharedVectorChecker() - { - if (copy) - QCOMPARE(copy->size(), originalSize); - delete copy; - } - - int originalSize; - QVector<T> *copy; -}; - -template<typename T> -void tst_QVector::erase(bool shared) const -{ - // note: remove() is actually more efficient, and more dangerous, because it uses the non-detaching - // begin() / end() internally. you can also use constBegin() and constEnd() with erase(), but only - // using reinterpret_cast... because both iterator types are really just pointers. - // so we use a mix of erase() and remove() to cover more cases. - { - QVector<T> v = SimpleValue<T>::vector(12); - SharedVectorChecker<T> svc(v, shared); - v.erase(v.begin()); - QCOMPARE(v.size(), 11); - for (int i = 0; i < 11; i++) - QCOMPARE(v.at(i), SimpleValue<T>::at(i + 1)); - v.erase(v.begin(), v.end()); - QCOMPARE(v.size(), 0); - if (shared) - QCOMPARE(SimpleValue<T>::vector(12), *svc.copy); - } - { - QVector<T> v = SimpleValue<T>::vector(12); - SharedVectorChecker<T> svc(v, shared); - v.remove(1); - QCOMPARE(v.size(), 11); - QCOMPARE(v.at(0), SimpleValue<T>::at(0)); - for (int i = 1; i < 11; i++) - QCOMPARE(v.at(i), SimpleValue<T>::at(i + 1)); - v.erase(v.begin() + 1, v.end()); - QCOMPARE(v.size(), 1); - QCOMPARE(v.at(0), SimpleValue<T>::at(0)); - if (shared) - QCOMPARE(SimpleValue<T>::vector(12), *svc.copy); - } - { - QVector<T> v = SimpleValue<T>::vector(12); - SharedVectorChecker<T> svc(v, shared); - v.erase(v.begin(), v.end() - 1); - QCOMPARE(v.size(), 1); - QCOMPARE(v.at(0), SimpleValue<T>::at(11)); - if (shared) - QCOMPARE(SimpleValue<T>::vector(12), *svc.copy); - } - { - QVector<T> v = SimpleValue<T>::vector(12); - SharedVectorChecker<T> svc(v, shared); - v.remove(5); - QCOMPARE(v.size(), 11); - for (int i = 0; i < 5; i++) - QCOMPARE(v.at(i), SimpleValue<T>::at(i)); - for (int i = 5; i < 11; i++) - QCOMPARE(v.at(i), SimpleValue<T>::at(i + 1)); - v.erase(v.begin() + 1, v.end() - 1); - QCOMPARE(v.at(0), SimpleValue<T>::at(0)); - QCOMPARE(v.at(1), SimpleValue<T>::at(11)); - QCOMPARE(v.size(), 2); - if (shared) - QCOMPARE(SimpleValue<T>::vector(12), *svc.copy); - } -} - -void tst_QVector::eraseInt() const -{ - erase<int>(false); -} - -void tst_QVector::eraseIntShared() const -{ - erase<int>(true); -} - -void tst_QVector::eraseMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - erase<Movable>(false); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::eraseMovableShared() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - erase<Movable>(true); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::eraseCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - erase<Custom>(false); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -void tst_QVector::eraseCustomShared() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - erase<Custom>(true); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -template<typename T> void tst_QVector::eraseReserved() const -{ - { - QVector<T> v(12); - v.reserve(16); - v.erase(v.begin()); - QCOMPARE(v.size(), 11); - v.erase(v.begin(), v.end()); - QCOMPARE(v.size(), 0); - } - { - QVector<T> v(12); - v.reserve(16); - v.erase(v.begin() + 1); - QCOMPARE(v.size(), 11); - v.erase(v.begin() + 1, v.end()); - QCOMPARE(v.size(), 1); - } - { - QVector<T> v(12); - v.reserve(16); - v.erase(v.begin(), v.end() - 1); - QCOMPARE(v.size(), 1); - } - { - QVector<T> v(12); - v.reserve(16); - v.erase(v.begin() + 5); - QCOMPARE(v.size(), 11); - v.erase(v.begin() + 1, v.end() - 1); - QCOMPARE(v.size(), 2); - } -} - -void tst_QVector::eraseReservedInt() const -{ - eraseReserved<int>(); -} - -void tst_QVector::eraseReservedMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - eraseReserved<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::eraseReservedCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - eraseReserved<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -template<typename T> -void tst_QVector::fill() const -{ - QVector<T> myvec; - - // resize - myvec.resize(5); - myvec.fill(SimpleValue<T>::at(1)); - QCOMPARE(myvec, QVector<T>() << SimpleValue<T>::at(1) << SimpleValue<T>::at(1) - << SimpleValue<T>::at(1) << SimpleValue<T>::at(1) - << SimpleValue<T>::at(1)); - - // make sure it can resize itself too - myvec.fill(SimpleValue<T>::at(2), 10); - QCOMPARE(myvec, QVector<T>() << SimpleValue<T>::at(2) << SimpleValue<T>::at(2) - << SimpleValue<T>::at(2) << SimpleValue<T>::at(2) - << SimpleValue<T>::at(2) << SimpleValue<T>::at(2) - << SimpleValue<T>::at(2) << SimpleValue<T>::at(2) - << SimpleValue<T>::at(2) << SimpleValue<T>::at(2)); -} - -void tst_QVector::fillInt() const -{ - fill<int>(); -} - -void tst_QVector::fillMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - fill<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::fillCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - fill<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -void tst_QVector::fillDetaches() const -{ - QVector<int> test = { 1, 2, 3 }; - QVector<int> copy = test; - copy.fill(42); - - QCOMPARE(test, QVector<int>({1, 2, 3})); - QCOMPARE(copy, QVector<int>({42, 42, 42})); -} - -void tst_QVector::first() const -{ - QVector<int> myvec; - myvec << 69 << 42 << 3; - - // test it starts ok - QCOMPARE(myvec.first(), 69); - QCOMPARE(myvec.constFirst(), 69); - - // test removal changes - myvec.remove(0); - QCOMPARE(myvec.first(), 42); - QCOMPARE(myvec.constFirst(), 42); - - // test prepend changes - myvec.prepend(23); - QCOMPARE(myvec.first(), 23); - QCOMPARE(myvec.constFirst(), 23); -} - -void tst_QVector::constFirst() const -{ - QVector<int> myvec; - myvec << 69 << 42 << 3; - - // test it starts ok - QCOMPARE(myvec.constFirst(), 69); - QVERIFY(myvec.isDetached()); - - QVector<int> myvecCopy = myvec; - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); - - QCOMPARE(myvec.constFirst(), 69); - QCOMPARE(myvecCopy.constFirst(), 69); - - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); - - // test removal changes - myvec.remove(0); - QVERIFY(myvec.isDetached()); - QVERIFY(!myvec.isSharedWith(myvecCopy)); - QCOMPARE(myvec.constFirst(), 42); - QCOMPARE(myvecCopy.constFirst(), 69); - - myvecCopy = myvec; - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); - - QCOMPARE(myvec.constFirst(), 42); - QCOMPARE(myvecCopy.constFirst(), 42); - - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); - - // test prepend changes - myvec.prepend(23); - QVERIFY(myvec.isDetached()); - QVERIFY(!myvec.isSharedWith(myvecCopy)); - QCOMPARE(myvec.constFirst(), 23); - QCOMPARE(myvecCopy.constFirst(), 42); - - myvecCopy = myvec; - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); - - QCOMPARE(myvec.constFirst(), 23); - QCOMPARE(myvecCopy.constFirst(), 23); - - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); -} - - -template<typename T> -void tst_QVector::fromList() const -{ - QList<T> list; - list << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2) << SimpleValue<T>::at(3); - - QVector<T> myvec; - myvec = QVector<T>::fromList(list); - - // test it worked ok - QCOMPARE(myvec, QVector<T>() << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2) << SimpleValue<T>::at(3)); - QCOMPARE(list, QList<T>() << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2) << SimpleValue<T>::at(3)); -} - -void tst_QVector::fromListInt() const -{ - fromList<int>(); -} - -void tst_QVector::fromListMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - fromList<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::fromListCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - fromList<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) -void tst_QVector::fromStdVector() const -{ - // stl = :( - std::vector<QString> svec; - svec.push_back(QLatin1String("aaa")); - svec.push_back(QLatin1String("bbb")); - svec.push_back(QLatin1String("ninjas")); - svec.push_back(QLatin1String("pirates")); - QVector<QString> myvec = QVector<QString>::fromStdVector(svec); - - // test it converts ok - QCOMPARE(myvec, QVector<QString>() << "aaa" << "bbb" << "ninjas" << "pirates"); -} -#endif - -void tst_QVector::indexOf() const -{ - QVector<QString> myvec; - myvec << "A" << "B" << "C" << "B" << "A"; - - QVERIFY(myvec.indexOf("B") == 1); - QVERIFY(myvec.indexOf("B", 1) == 1); - QVERIFY(myvec.indexOf("B", 2) == 3); - QVERIFY(myvec.indexOf("X") == -1); - QVERIFY(myvec.indexOf("X", 2) == -1); - - // add an X - myvec << "X"; - QVERIFY(myvec.indexOf("X") == 5); - QVERIFY(myvec.indexOf("X", 5) == 5); - QVERIFY(myvec.indexOf("X", 6) == -1); - - // remove first A - myvec.remove(0); - QVERIFY(myvec.indexOf("A") == 3); - QVERIFY(myvec.indexOf("A", 3) == 3); - QVERIFY(myvec.indexOf("A", 4) == -1); -} - -template <typename T> -void tst_QVector::insert() const -{ - QVector<T> myvec; - const T - tA = SimpleValue<T>::at(0), - tB = SimpleValue<T>::at(1), - tC = SimpleValue<T>::at(2), - tX = SimpleValue<T>::at(3), - tZ = SimpleValue<T>::at(4), - tT = SimpleValue<T>::at(5), - ti = SimpleValue<T>::at(6); - myvec << tA << tB << tC; - QVector<T> myvec2 = myvec; - - // first position - QCOMPARE(myvec.at(0), tA); - myvec.insert(0, tX); - QCOMPARE(myvec.at(0), tX); - QCOMPARE(myvec.at(1), tA); - - QCOMPARE(myvec2.at(0), tA); - myvec2.insert(myvec2.begin(), tX); - QCOMPARE(myvec2.at(0), tX); - QCOMPARE(myvec2.at(1), tA); - - // middle - myvec.insert(1, tZ); - QCOMPARE(myvec.at(0), tX); - QCOMPARE(myvec.at(1), tZ); - QCOMPARE(myvec.at(2), tA); - - myvec2.insert(myvec2.begin() + 1, tZ); - QCOMPARE(myvec2.at(0), tX); - QCOMPARE(myvec2.at(1), tZ); - QCOMPARE(myvec2.at(2), tA); - - // end - myvec.insert(5, tT); - QCOMPARE(myvec.at(5), tT); - QCOMPARE(myvec.at(4), tC); - - myvec2.insert(myvec2.end(), tT); - QCOMPARE(myvec2.at(5), tT); - QCOMPARE(myvec2.at(4), tC); - - // insert a lot of garbage in the middle - myvec.insert(2, 2, ti); - QCOMPARE(myvec, QVector<T>() << tX << tZ << ti << ti - << tA << tB << tC << tT); - - myvec2.insert(myvec2.begin() + 2, 2, ti); - QCOMPARE(myvec2, myvec); - - // insert from references to the same container: - myvec.insert(0, 1, myvec[5]); // inserts tB - myvec2.insert(0, 1, myvec2[5]); // inserts tB - QCOMPARE(myvec, QVector<T>() << tB << tX << tZ << ti << ti - << tA << tB << tC << tT); - QCOMPARE(myvec2, myvec); - - myvec.insert(0, 1, const_cast<const QVector<T>&>(myvec)[0]); // inserts tB - myvec2.insert(0, 1, const_cast<const QVector<T>&>(myvec2)[0]); // inserts tB - QCOMPARE(myvec, QVector<T>() << tB << tB << tX << tZ << ti << ti - << tA << tB << tC << tT); - QCOMPARE(myvec2, myvec); -} - -void tst_QVector::insertInt() const -{ - insert<int>(); -} - -void tst_QVector::insertMovable() const -{ - insert<Movable>(); -} - -void tst_QVector::insertCustom() const -{ - insert<Custom>(); -} - -void tst_QVector::isEmpty() const -{ - QVector<QString> myvec; - - // starts ok - QVERIFY(myvec.isEmpty()); - - // not empty now - myvec.append(QLatin1String("hello there")); - QVERIFY(!myvec.isEmpty()); - - // empty again - myvec.remove(0); - QVERIFY(myvec.isEmpty()); -} - -void tst_QVector::last() const -{ - QVector<QString> myvec; - myvec << "A" << "B" << "C"; - - // test starts ok - QCOMPARE(myvec.last(), QLatin1String("C")); - QCOMPARE(myvec.constLast(), QLatin1String("C")); - - // test it changes ok - myvec.append(QLatin1String("X")); - QCOMPARE(myvec.last(), QLatin1String("X")); - QCOMPARE(myvec.constLast(), QLatin1String("X")); - - // and remove again - myvec.remove(3); - QCOMPARE(myvec.last(), QLatin1String("C")); - QCOMPARE(myvec.constLast(), QLatin1String("C")); -} - -void tst_QVector::constLast() const -{ - QVector<int> myvec; - myvec << 69 << 42 << 3; - - // test it starts ok - QCOMPARE(myvec.constLast(), 3); - QVERIFY(myvec.isDetached()); - - QVector<int> myvecCopy = myvec; - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); - - QCOMPARE(myvec.constLast(), 3); - QCOMPARE(myvecCopy.constLast(), 3); - - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); - - // test removal changes - myvec.removeLast(); - QVERIFY(myvec.isDetached()); - QVERIFY(!myvec.isSharedWith(myvecCopy)); - QCOMPARE(myvec.constLast(), 42); - QCOMPARE(myvecCopy.constLast(), 3); - - myvecCopy = myvec; - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); - - QCOMPARE(myvec.constLast(), 42); - QCOMPARE(myvecCopy.constLast(), 42); - - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); - - // test prepend changes - myvec.append(23); - QVERIFY(myvec.isDetached()); - QVERIFY(!myvec.isSharedWith(myvecCopy)); - QCOMPARE(myvec.constLast(), 23); - QCOMPARE(myvecCopy.constLast(), 42); - - myvecCopy = myvec; - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); - - QCOMPARE(myvec.constLast(), 23); - QCOMPARE(myvecCopy.constLast(), 23); - - QVERIFY(!myvec.isDetached()); - QVERIFY(!myvecCopy.isDetached()); - QVERIFY(myvec.isSharedWith(myvecCopy)); - QVERIFY(myvecCopy.isSharedWith(myvec)); -} - -void tst_QVector::lastIndexOf() const -{ - QVector<QString> myvec; - myvec << "A" << "B" << "C" << "B" << "A"; - - QVERIFY(myvec.lastIndexOf("B") == 3); - QVERIFY(myvec.lastIndexOf("B", 2) == 1); - QVERIFY(myvec.lastIndexOf("X") == -1); - QVERIFY(myvec.lastIndexOf("X", 2) == -1); - - // add an X - myvec << "X"; - QVERIFY(myvec.lastIndexOf("X") == 5); - QVERIFY(myvec.lastIndexOf("X", 5) == 5); - QVERIFY(myvec.lastIndexOf("X", 3) == -1); - - // remove first A - myvec.remove(0); - QVERIFY(myvec.lastIndexOf("A") == 3); - QVERIFY(myvec.lastIndexOf("A", 3) == 3); - QVERIFY(myvec.lastIndexOf("A", 2) == -1); -} - -void tst_QVector::mid() const -{ - QVector<QString> list; - list << "foo" << "bar" << "baz" << "bak" << "buck" << "hello" << "kitty"; - - QCOMPARE(list.mid(3, 3), QVector<QString>() << "bak" << "buck" << "hello"); - QCOMPARE(list.mid(6, 10), QVector<QString>() << "kitty"); - QCOMPARE(list.mid(-1, 20), list); - QCOMPARE(list.mid(4), QVector<QString>() << "buck" << "hello" << "kitty"); -} - -template <typename T> -void tst_QVector::qhash() const -{ - QVector<T> l1, l2; - QCOMPARE(qHash(l1), qHash(l2)); - l1 << SimpleValue<T>::at(0); - l2 << SimpleValue<T>::at(0); - QCOMPARE(qHash(l1), qHash(l2)); -} - -template <typename T> -void tst_QVector::move() const -{ - QVector<T> list; - list << T_FOO << T_BAR << T_BAZ; - - // move an item - list.move(0, list.count() - 1); - QCOMPARE(list, QVector<T>() << T_BAR << T_BAZ << T_FOO); - - // move it back - list.move(list.count() - 1, 0); - QCOMPARE(list, QVector<T>() << T_FOO << T_BAR << T_BAZ); - - // move an item in the middle - list.move(1, 0); - QCOMPARE(list, QVector<T>() << T_BAR << T_FOO << T_BAZ); -} - -void tst_QVector::moveInt() const -{ - move<int>(); -} - -void tst_QVector::moveMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - move<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::moveCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - move<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -template<typename T> -void tst_QVector::prepend() const -{ - QVector<T> myvec; - T val1 = SimpleValue<T>::at(0); - T val2 = SimpleValue<T>::at(1); - T val3 = SimpleValue<T>::at(2); - T val4 = SimpleValue<T>::at(3); - T val5 = SimpleValue<T>::at(4); - myvec << val1 << val2 << val3; - - // starts ok - QVERIFY(myvec.size() == 3); - QCOMPARE(myvec.at(0), val1); - - // add something - myvec.prepend(val4); - QCOMPARE(myvec.at(0), val4); - QCOMPARE(myvec.at(1), val1); - QVERIFY(myvec.size() == 4); - - // something else - myvec.prepend(val5); - QCOMPARE(myvec.at(0), val5); - QCOMPARE(myvec.at(1), val4); - QCOMPARE(myvec.at(2), val1); - QVERIFY(myvec.size() == 5); - - // clear and prepend to an empty vector - myvec.clear(); - QVERIFY(myvec.size() == 0); - myvec.prepend(val5); - QVERIFY(myvec.size() == 1); - QCOMPARE(myvec.at(0), val5); -} - -void tst_QVector::prependInt() const -{ - prepend<int>(); -} - -void tst_QVector::prependMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - prepend<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::prependCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - prepend<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -void tst_QVector::removeAllWithAlias() const -{ - QVector<QString> strings; - strings << "One" << "Two" << "Three" << "One" /* must be distinct, but equal */; - QCOMPARE(strings.removeAll(strings.front()), 2); // will trigger asan/ubsan -} - -template<typename T> -void tst_QVector::remove() const -{ - QVector<T> myvec; - T val1 = SimpleValue<T>::at(1); - T val2 = SimpleValue<T>::at(2); - T val3 = SimpleValue<T>::at(3); - T val4 = SimpleValue<T>::at(4); - myvec << val1 << val2 << val3; - myvec << val1 << val2 << val3; - myvec << val1 << val2 << val3; - // remove middle - myvec.remove(1); - QCOMPARE(myvec, QVector<T>() << val1 << val3 << val1 << val2 << val3 << val1 << val2 << val3); - - // removeOne() - QVERIFY(!myvec.removeOne(val4)); - QVERIFY(myvec.removeOne(val2)); - QCOMPARE(myvec, QVector<T>() << val1 << val3 << val1 << val3 << val1 << val2 << val3); - - QVector<T> myvecCopy = myvec; - QVERIFY(myvecCopy.isSharedWith(myvec)); - // removeAll() - QCOMPARE(myvec.removeAll(val4), 0); - QVERIFY(myvecCopy.isSharedWith(myvec)); - QCOMPARE(myvec.removeAll(val1), 3); - QVERIFY(!myvecCopy.isSharedWith(myvec)); - QCOMPARE(myvec, QVector<T>() << val3 << val3 << val2 << val3); - myvecCopy = myvec; - QVERIFY(myvecCopy.isSharedWith(myvec)); - QCOMPARE(myvec.removeAll(val2), 1); - QVERIFY(!myvecCopy.isSharedWith(myvec)); - QCOMPARE(myvec, QVector<T>() << val3 << val3 << val3); - - // remove rest - myvec.remove(0, 3); - QCOMPARE(myvec, QVector<T>()); -} - -void tst_QVector::removeInt() const -{ - remove<int>(); -} - -void tst_QVector::removeMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - remove<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::removeCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - remove<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -struct RemoveLastTestClass -{ - RemoveLastTestClass() { other = 0; deleted = false; } - RemoveLastTestClass *other; - bool deleted; - ~RemoveLastTestClass() - { - deleted = true; - if (other) - other->other = 0; - } -}; - -void tst_QVector::removeFirstLast() const -{ - // pop_pack - pop_front - QVector<int> t, t2; - t.append(1); - t.append(2); - t.append(3); - t.append(4); - t2 = t; - t.pop_front(); - QCOMPARE(t.size(), 3); - QCOMPARE(t.at(0), 2); - t.pop_back(); - QCOMPARE(t.size(), 2); - QCOMPARE(t.at(0), 2); - QCOMPARE(t.at(1), 3); - - // takefirst - takeLast - int n1 = t2.takeLast(); - QCOMPARE(t2.size(), 3); - QCOMPARE(n1, 4); - QCOMPARE(t2.at(0), 1); - QCOMPARE(t2.at(2), 3); - n1 = t2.takeFirst(); - QCOMPARE(t2.size(), 2); - QCOMPARE(n1, 1); - QCOMPARE(t2.at(0), 2); - QCOMPARE(t2.at(1), 3); - - // remove first - QVector<int> x, y; - x.append(1); - x.append(2); - y = x; - x.removeFirst(); - QCOMPARE(x.size(), 1); - QCOMPARE(y.size(), 2); - QCOMPARE(x.at(0), 2); - - // remove Last - QVector<RemoveLastTestClass> v; - v.resize(2); - v[0].other = &(v[1]); - v[1].other = &(v[0]); - // Check dtor - complex type - QVERIFY(v.at(0).other != 0); - v.removeLast(); - QVERIFY(v.at(0).other == 0); - QCOMPARE(v.at(0).deleted, false); - // check iterator - int count = 0; - for (QVector<RemoveLastTestClass>::const_iterator i = v.constBegin(); i != v.constEnd(); ++i) { - ++count; - QVERIFY(i->other == 0); - QCOMPARE(i->deleted, false); - } - // Check size - QCOMPARE(count, 1); - QCOMPARE(v.size(), 1); - v.removeLast(); - QCOMPARE(v.size(), 0); - // Check if we do correct realloc - QVector<int> v2, v3; - v2.append(1); - v2.append(2); - v3 = v2; // shared - v2.removeLast(); - QCOMPARE(v2.size(), 1); - QCOMPARE(v3.size(), 2); - QCOMPARE(v2.at(0), 1); - QCOMPARE(v3.at(0), 1); - QCOMPARE(v3.at(1), 2); - - // Remove last with shared - QVector<int> z1, z2; - z1.append(9); - z2 = z1; - z1.removeLast(); - QCOMPARE(z1.size(), 0); - QCOMPARE(z2.size(), 1); - QCOMPARE(z2.at(0), 9); -} - - -void tst_QVector::resizePOD_data() const -{ - QTest::addColumn<QVector<int> >("vector"); - QTest::addColumn<int>("size"); - - QVERIFY(!QTypeInfo<int>::isComplex); - QVERIFY(!QTypeInfo<int>::isStatic); - - QVector<int> null; - QVector<int> empty(0, 5); - QVector<int> emptyReserved; - QVector<int> nonEmpty; - QVector<int> nonEmptyReserved; - - emptyReserved.reserve(10); - nonEmptyReserved.reserve(15); - nonEmpty << 0 << 1 << 2 << 3 << 4; - nonEmptyReserved << 0 << 1 << 2 << 3 << 4 << 5 << 6; - QVERIFY(emptyReserved.capacity() >= 10); - QVERIFY(nonEmptyReserved.capacity() >= 15); - - QTest::newRow("null") << null << 10; - QTest::newRow("empty") << empty << 10; - QTest::newRow("emptyReserved") << emptyReserved << 10; - QTest::newRow("nonEmpty") << nonEmpty << 10; - QTest::newRow("nonEmptyReserved") << nonEmptyReserved << 10; -} - -void tst_QVector::resizePOD() const -{ - QFETCH(QVector<int>, vector); - QFETCH(int, size); - - const int oldSize = vector.size(); - - vector.resize(size); - QCOMPARE(vector.size(), size); - QVERIFY(vector.capacity() >= size); - for (int i = oldSize; i < size; ++i) - QVERIFY(vector[i] == 0); // check initialization - - const int capacity = vector.capacity(); - - vector.clear(); - QCOMPARE(vector.size(), 0); - QVERIFY(vector.capacity() <= capacity); -} - -void tst_QVector::resizeComplexMovable_data() const -{ - QTest::addColumn<QVector<Movable> >("vector"); - QTest::addColumn<int>("size"); - - QVERIFY(QTypeInfo<Movable>::isComplex); - QVERIFY(!QTypeInfo<Movable>::isStatic); - - QVector<Movable> null; - QVector<Movable> empty(0, 'Q'); - QVector<Movable> emptyReserved; - QVector<Movable> nonEmpty; - QVector<Movable> nonEmptyReserved; - - emptyReserved.reserve(10); - nonEmptyReserved.reserve(15); - nonEmpty << '0' << '1' << '2' << '3' << '4'; - nonEmptyReserved << '0' << '1' << '2' << '3' << '4' << '5' << '6'; - QVERIFY(emptyReserved.capacity() >= 10); - QVERIFY(nonEmptyReserved.capacity() >= 15); - - QTest::newRow("null") << null << 10; - QTest::newRow("empty") << empty << 10; - QTest::newRow("emptyReserved") << emptyReserved << 10; - QTest::newRow("nonEmpty") << nonEmpty << 10; - QTest::newRow("nonEmptyReserved") << nonEmptyReserved << 10; -} - -void tst_QVector::resizeComplexMovable() const -{ - const int items = Movable::counter.loadAcquire(); - { - QFETCH(QVector<Movable>, vector); - QFETCH(int, size); - - const int oldSize = vector.size(); - - vector.resize(size); - QCOMPARE(vector.size(), size); - QVERIFY(vector.capacity() >= size); - for (int i = oldSize; i < size; ++i) - QVERIFY(vector[i] == 'j'); // check initialization - - const int capacity = vector.capacity(); - - vector.resize(0); - QCOMPARE(vector.size(), 0); - QVERIFY(vector.capacity() <= capacity); - } - QCOMPARE(items, Movable::counter.loadAcquire()); -} - -void tst_QVector::resizeComplex_data() const -{ - QTest::addColumn<QVector<Custom> >("vector"); - QTest::addColumn<int>("size"); - - QVERIFY(QTypeInfo<Custom>::isComplex); - QVERIFY(QTypeInfo<Custom>::isStatic); - - QVector<Custom> null; - QVector<Custom> empty(0, '0'); - QVector<Custom> emptyReserved; - QVector<Custom> nonEmpty; - QVector<Custom> nonEmptyReserved; - - emptyReserved.reserve(10); - nonEmptyReserved.reserve(15); - nonEmpty << '0' << '1' << '2' << '3' << '4'; - nonEmptyReserved << '0' << '1' << '2' << '3' << '4' << '5' << '6'; - QVERIFY(emptyReserved.capacity() >= 10); - QVERIFY(nonEmptyReserved.capacity() >= 15); - - QTest::newRow("null") << null << 10; - QTest::newRow("empty") << empty << 10; - QTest::newRow("emptyReserved") << emptyReserved << 10; - QTest::newRow("nonEmpty") << nonEmpty << 10; - QTest::newRow("nonEmptyReserved") << nonEmptyReserved << 10; -} - -void tst_QVector::resizeComplex() const -{ - const int items = Custom::counter.loadAcquire(); - { - QFETCH(QVector<Custom>, vector); - QFETCH(int, size); - - int oldSize = vector.size(); - vector.resize(size); - QCOMPARE(vector.size(), size); - QVERIFY(vector.capacity() >= size); - for (int i = oldSize; i < size; ++i) - QVERIFY(vector[i].i == 'j'); // check default initialization - - const int capacity = vector.capacity(); - - vector.resize(0); - QCOMPARE(vector.size(), 0); - QVERIFY(vector.isEmpty()); - QVERIFY(vector.capacity() <= capacity); - } - QCOMPARE(Custom::counter.loadAcquire(), items); -} - -void tst_QVector::resizeCtorAndDtor() const -{ - const int items = Custom::counter.loadAcquire(); - { - QVector<Custom> null; - QVector<Custom> empty(0, '0'); - QVector<Custom> emptyReserved; - QVector<Custom> nonEmpty; - QVector<Custom> nonEmptyReserved; - - emptyReserved.reserve(10); - nonEmptyReserved.reserve(15); - nonEmpty << '0' << '1' << '2' << '3' << '4'; - nonEmptyReserved << '0' << '1' << '2' << '3' << '4' << '5' << '6'; - QVERIFY(emptyReserved.capacity() >= 10); - QVERIFY(nonEmptyReserved.capacity() >= 15); - - // start playing with vectors - null.resize(21); - nonEmpty.resize(2); - emptyReserved.resize(0); - nonEmpty.resize(0); - nonEmptyReserved.resize(2); - } - QCOMPARE(Custom::counter.loadAcquire(), items); -} - -void tst_QVector::reverseIterators() const -{ - QVector<int> v; - v << 1 << 2 << 3 << 4; - QVector<int> vr = v; - std::reverse(vr.begin(), vr.end()); - const QVector<int> &cvr = vr; - QVERIFY(std::equal(v.begin(), v.end(), vr.rbegin())); - QVERIFY(std::equal(v.begin(), v.end(), vr.crbegin())); - QVERIFY(std::equal(v.begin(), v.end(), cvr.rbegin())); - QVERIFY(std::equal(vr.rbegin(), vr.rend(), v.begin())); - QVERIFY(std::equal(vr.crbegin(), vr.crend(), v.begin())); - QVERIFY(std::equal(cvr.rbegin(), cvr.rend(), v.begin())); -} - -template<typename T> -void tst_QVector::size() const -{ - // zero size - QVector<T> myvec; - QVERIFY(myvec.size() == 0); - - // grow - myvec.append(SimpleValue<T>::at(0)); - QVERIFY(myvec.size() == 1); - myvec.append(SimpleValue<T>::at(1)); - QVERIFY(myvec.size() == 2); - - // shrink - myvec.remove(0); - QVERIFY(myvec.size() == 1); - myvec.remove(0); - QVERIFY(myvec.size() == 0); -} - -void tst_QVector::sizeInt() const -{ - size<int>(); -} - -void tst_QVector::sizeMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - size<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::sizeCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - size<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -// ::squeeze() is tested in ::capacity(). - -void tst_QVector::startsWith() const -{ - QVector<int> myvec; - - // empty vector - QVERIFY(!myvec.startsWith(1)); - - // add the one, should work - myvec.prepend(1); - QVERIFY(myvec.startsWith(1)); - - // add something else, fails now - myvec.prepend(3); - QVERIFY(!myvec.startsWith(1)); - - // remove it again :) - myvec.remove(0); - QVERIFY(myvec.startsWith(1)); -} - -template<typename T> -void tst_QVector::swap() const -{ - QVector<T> v1, v2; - T val1 = SimpleValue<T>::at(0); - T val2 = SimpleValue<T>::at(1); - T val3 = SimpleValue<T>::at(2); - T val4 = SimpleValue<T>::at(3); - T val5 = SimpleValue<T>::at(4); - T val6 = SimpleValue<T>::at(5); - v1 << val1 << val2 << val3; - v2 << val4 << val5 << val6; - - v1.swap(v2); - QCOMPARE(v1,QVector<T>() << val4 << val5 << val6); - QCOMPARE(v2,QVector<T>() << val1 << val2 << val3); -} - -void tst_QVector::swapInt() const -{ - swap<int>(); -} - -void tst_QVector::swapMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - swap<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::swapCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - swap<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -void tst_QVector::toList() const -{ - QVector<QString> myvec; - myvec << "A" << "B" << "C"; - - // make sure it converts and doesn't modify the original vector - QCOMPARE(myvec.toList(), QList<QString>() << "A" << "B" << "C"); - QCOMPARE(myvec, QVector<QString>() << "A" << "B" << "C"); -} - -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) -void tst_QVector::toStdVector() const -{ - QVector<QString> myvec; - myvec << "A" << "B" << "C"; - - std::vector<QString> svec = myvec.toStdVector(); - QCOMPARE(svec.at(0), QLatin1String("A")); - QCOMPARE(svec.at(1), QLatin1String("B")); - QCOMPARE(svec.at(2), QLatin1String("C")); - - QCOMPARE(myvec, QVector<QString>() << "A" << "B" << "C"); -} -#endif - -void tst_QVector::value() const -{ - QVector<QString> myvec; - myvec << "A" << "B" << "C"; - - // valid calls - QCOMPARE(myvec.value(0), QLatin1String("A")); - QCOMPARE(myvec.value(1), QLatin1String("B")); - QCOMPARE(myvec.value(2), QLatin1String("C")); - - // default calls - QCOMPARE(myvec.value(-1), QString()); - QCOMPARE(myvec.value(3), QString()); - - // test calls with a provided default, valid calls - QCOMPARE(myvec.value(0, QLatin1String("default")), QLatin1String("A")); - QCOMPARE(myvec.value(1, QLatin1String("default")), QLatin1String("B")); - QCOMPARE(myvec.value(2, QLatin1String("default")), QLatin1String("C")); - - // test calls with a provided default that will return the default - QCOMPARE(myvec.value(-1, QLatin1String("default")), QLatin1String("default")); - QCOMPARE(myvec.value(3, QLatin1String("default")), QLatin1String("default")); -} - -void tst_QVector::testOperators() const -{ - QVector<QString> myvec; - myvec << "A" << "B" << "C"; - QVector<QString> myvectwo; - myvectwo << "D" << "E" << "F"; - QVector<QString> combined; - combined << "A" << "B" << "C" << "D" << "E" << "F"; - - // != - QVERIFY(myvec != myvectwo); - - // + - QCOMPARE(myvec + myvectwo, combined); - QCOMPARE(myvec, QVector<QString>() << "A" << "B" << "C"); - QCOMPARE(myvectwo, QVector<QString>() << "D" << "E" << "F"); - - // += - myvec += myvectwo; - QCOMPARE(myvec, combined); - - // == - QVERIFY(myvec == combined); - - // <, >, <=, >= - QVERIFY(!(myvec < combined)); - QVERIFY(!(myvec > combined)); - QVERIFY( myvec <= combined); - QVERIFY( myvec >= combined); - combined.push_back("G"); - QVERIFY( myvec < combined); - QVERIFY(!(myvec > combined)); - QVERIFY( myvec <= combined); - QVERIFY(!(myvec >= combined)); - QVERIFY(combined > myvec); - QVERIFY(combined >= myvec); - - // [] - QCOMPARE(myvec[0], QLatin1String("A")); - QCOMPARE(myvec[1], QLatin1String("B")); - QCOMPARE(myvec[2], QLatin1String("C")); - QCOMPARE(myvec[3], QLatin1String("D")); - QCOMPARE(myvec[4], QLatin1String("E")); - QCOMPARE(myvec[5], QLatin1String("F")); -} - - -int fooCtor; -int fooDtor; - -struct Foo -{ - int *p; - - Foo() { p = new int; ++fooCtor; } - Foo(const Foo &other) { Q_UNUSED(other); p = new int; ++fooCtor; } - - void operator=(const Foo & /* other */) { } - - ~Foo() { delete p; ++fooDtor; } -}; - -void tst_QVector::reserve() -{ - fooCtor = 0; - fooDtor = 0; - { - QVector<Foo> a; - a.resize(2); - QCOMPARE(fooCtor, 2); - QVector<Foo> b(a); - b.reserve(1); - QCOMPARE(b.size(), a.size()); - QCOMPARE(fooDtor, 0); - } - QCOMPARE(fooCtor, fooDtor); -} - -// This is a regression test for QTBUG-51758 -void tst_QVector::reserveZero() -{ - QVector<int> vec; - vec.detach(); - vec.reserve(0); // should not crash - QCOMPARE(vec.size(), 0); - QCOMPARE(vec.capacity(), 0); - vec.squeeze(); - QCOMPARE(vec.size(), 0); - QCOMPARE(vec.capacity(), 0); - vec.reserve(-1); - QCOMPARE(vec.size(), 0); - QCOMPARE(vec.capacity(), 0); - vec.append(42); - QCOMPARE(vec.size(), 1); - QVERIFY(vec.capacity() >= 1); -} - -template<typename T> -void tst_QVector::initializeList() -{ - T val1(SimpleValue<T>::at(1)); - T val2(SimpleValue<T>::at(2)); - T val3(SimpleValue<T>::at(3)); - T val4(SimpleValue<T>::at(4)); - - QVector<T> v1 {val1, val2, val3}; - QCOMPARE(v1, QVector<T>() << val1 << val2 << val3); - QCOMPARE(v1, (QVector<T> {val1, val2, val3})); - - QVector<QVector<T>> v2{ v1, {val4}, QVector<T>(), {val1, val2, val3} }; - QVector<QVector<T>> v3; - v3 << v1 << (QVector<T>() << val4) << QVector<T>() << v1; - QCOMPARE(v3, v2); - - QVector<T> v4({}); - QCOMPARE(v4.size(), 0); -} - -void tst_QVector::initializeListInt() -{ - initializeList<int>(); -} - -void tst_QVector::initializeListMovable() -{ - const int instancesCount = Movable::counter.loadAcquire(); - initializeList<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::initializeListCustom() -{ - const int instancesCount = Custom::counter.loadAcquire(); - initializeList<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -void tst_QVector::const_shared_null() -{ - QVector<int> v2; - QVERIFY(!v2.isDetached()); -} - -template<typename T> -void tst_QVector::detach() const -{ - { - // detach an empty vector - QVector<T> v; - v.detach(); - QVERIFY(!v.isDetached()); - QCOMPARE(v.size(), 0); - QCOMPARE(v.capacity(), 0); - } - { - // detach an empty referenced vector - QVector<T> v; - QVector<T> ref(v); - QVERIFY(!v.isDetached()); - v.detach(); - QVERIFY(!v.isDetached()); - QCOMPARE(v.size(), 0); - QCOMPARE(v.capacity(), 0); - } - { - // detach a not empty referenced vector - QVector<T> v(31); - QVector<T> ref(v); - QVERIFY(!v.isDetached()); - v.detach(); - QVERIFY(v.isDetached()); - QCOMPARE(v.size(), 31); - QCOMPARE(v.capacity(), 31); - } - { - // detach a not empty vector - QVector<T> v(31); - QVERIFY(v.isDetached()); - v.detach(); // detaching a detached vector - QVERIFY(v.isDetached()); - QCOMPARE(v.size(), 31); - QCOMPARE(v.capacity(), 31); - } - { - // detach a not empty vector with preallocated space - QVector<T> v(3); - v.reserve(8); - QVector<T> ref(v); - QVERIFY(!v.isDetached()); - v.detach(); - QVERIFY(v.isDetached()); - QCOMPARE(v.size(), 3); - QCOMPARE(v.capacity(), 8); - } - { - // detach a not empty vector with preallocated space - QVector<T> v(3); - v.reserve(8); - QVERIFY(v.isDetached()); - v.detach(); // detaching a detached vector - QVERIFY(v.isDetached()); - QCOMPARE(v.size(), 3); - QCOMPARE(v.capacity(), 8); - } - { - // detach a not empty, initialized vector - QVector<T> v(7, SimpleValue<T>::at(1)); - QVector<T> ref(v); - QVERIFY(!v.isDetached()); - v.detach(); - QVERIFY(v.isDetached()); - QCOMPARE(v.size(), 7); - for (int i = 0; i < v.size(); ++i) - QCOMPARE(v[i], SimpleValue<T>::at(1)); - } - { - // detach a not empty, initialized vector - QVector<T> v(7, SimpleValue<T>::at(2)); - QVERIFY(v.isDetached()); - v.detach(); // detaching a detached vector - QVERIFY(v.isDetached()); - QCOMPARE(v.size(), 7); - for (int i = 0; i < v.size(); ++i) - QCOMPARE(v[i], SimpleValue<T>::at(2)); - } - { - // detach a not empty, initialized vector with preallocated space - QVector<T> v(7, SimpleValue<T>::at(3)); - v.reserve(31); - QVector<T> ref(v); - QVERIFY(!v.isDetached()); - v.detach(); - QVERIFY(v.isDetached()); - QCOMPARE(v.size(), 7); - QCOMPARE(v.capacity(), 31); - for (int i = 0; i < v.size(); ++i) - QCOMPARE(v[i], SimpleValue<T>::at(3)); - } -} - -void tst_QVector::detachInt() const -{ - detach<int>(); -} - -void tst_QVector::detachMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - detach<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QVector::detachCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - detach<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -static QAtomicPointer<QVector<int> > detachThreadSafetyDataInt; -static QAtomicPointer<QVector<Movable> > detachThreadSafetyDataMovable; -static QAtomicPointer<QVector<Custom> > detachThreadSafetyDataCustom; - -template<typename T> QAtomicPointer<QVector<T> > *detachThreadSafetyData(); -template<> QAtomicPointer<QVector<int> > *detachThreadSafetyData() { return &detachThreadSafetyDataInt; } -template<> QAtomicPointer<QVector<Movable> > *detachThreadSafetyData() { return &detachThreadSafetyDataMovable; } -template<> QAtomicPointer<QVector<Custom> > *detachThreadSafetyData() { return &detachThreadSafetyDataCustom; } - -static QSemaphore detachThreadSafetyLock; - -template<typename T> -void tst_QVector::detachThreadSafety() const -{ - delete detachThreadSafetyData<T>()->fetchAndStoreOrdered(new QVector<T>(SimpleValue<T>::vector(400))); - - static const uint threadsCount = 5; - - struct : QThread { - void run() override - { - QVector<T> copy(*detachThreadSafetyData<T>()->loadRelaxed()); - QVERIFY(!copy.isDetached()); - detachThreadSafetyLock.release(); - detachThreadSafetyLock.acquire(100); - copy.detach(); - } - } threads[threadsCount]; - - for (uint i = 0; i < threadsCount; ++i) - threads[i].start(); - QThread::yieldCurrentThread(); - detachThreadSafetyLock.acquire(threadsCount); - - // destroy static original data - delete detachThreadSafetyData<T>()->fetchAndStoreOrdered(0); - - QVERIFY(threadsCount < 100); - detachThreadSafetyLock.release(threadsCount * 100); - QThread::yieldCurrentThread(); - - for (uint i = 0; i < threadsCount; ++i) - threads[i].wait(); -} - -void tst_QVector::detachThreadSafetyInt() const -{ - for (uint i = 0; i < 128; ++i) - detachThreadSafety<int>(); -} - -void tst_QVector::detachThreadSafetyMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - for (uint i = 0; i < 128; ++i) { - detachThreadSafety<Movable>(); - QCOMPARE(Movable::counter.loadAcquire(), instancesCount); - } -} - -void tst_QVector::detachThreadSafetyCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - for (uint i = 0; i < 128; ++i) { - detachThreadSafety<Custom>(); - QCOMPARE(Custom::counter.loadAcquire(), instancesCount); - } -} - -void tst_QVector::insertMove() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - { - QVector<Movable> vec; - vec.reserve(7); - Movable m0; - Movable m1; - Movable m2; - Movable m3; - Movable m4; - Movable m5; - Movable m6; - - vec.append(std::move(m3)); - QVERIFY(m3.wasConstructedAt(nullptr)); - QVERIFY(vec.at(0).wasConstructedAt(&m3)); - vec.push_back(std::move(m4)); - QVERIFY(m4.wasConstructedAt(nullptr)); - QVERIFY(vec.at(0).wasConstructedAt(&m3)); - QVERIFY(vec.at(1).wasConstructedAt(&m4)); - vec.prepend(std::move(m1)); - QVERIFY(m1.wasConstructedAt(nullptr)); - QVERIFY(vec.at(0).wasConstructedAt(&m1)); - QVERIFY(vec.at(1).wasConstructedAt(&m3)); - QVERIFY(vec.at(2).wasConstructedAt(&m4)); - vec.insert(1, std::move(m2)); - QVERIFY(m2.wasConstructedAt(nullptr)); - QVERIFY(vec.at(0).wasConstructedAt(&m1)); - QVERIFY(vec.at(1).wasConstructedAt(&m2)); - QVERIFY(vec.at(2).wasConstructedAt(&m3)); - QVERIFY(vec.at(3).wasConstructedAt(&m4)); - vec += std::move(m5); - QVERIFY(m5.wasConstructedAt(nullptr)); - QVERIFY(vec.at(0).wasConstructedAt(&m1)); - QVERIFY(vec.at(1).wasConstructedAt(&m2)); - QVERIFY(vec.at(2).wasConstructedAt(&m3)); - QVERIFY(vec.at(3).wasConstructedAt(&m4)); - QVERIFY(vec.at(4).wasConstructedAt(&m5)); - vec << std::move(m6); - QVERIFY(m6.wasConstructedAt(nullptr)); - QVERIFY(vec.at(0).wasConstructedAt(&m1)); - QVERIFY(vec.at(1).wasConstructedAt(&m2)); - QVERIFY(vec.at(2).wasConstructedAt(&m3)); - QVERIFY(vec.at(3).wasConstructedAt(&m4)); - QVERIFY(vec.at(4).wasConstructedAt(&m5)); - QVERIFY(vec.at(5).wasConstructedAt(&m6)); - vec.push_front(std::move(m0)); - QVERIFY(m0.wasConstructedAt(nullptr)); - QVERIFY(vec.at(0).wasConstructedAt(&m0)); - QVERIFY(vec.at(1).wasConstructedAt(&m1)); - QVERIFY(vec.at(2).wasConstructedAt(&m2)); - QVERIFY(vec.at(3).wasConstructedAt(&m3)); - QVERIFY(vec.at(4).wasConstructedAt(&m4)); - QVERIFY(vec.at(5).wasConstructedAt(&m5)); - QVERIFY(vec.at(6).wasConstructedAt(&m6)); - - QCOMPARE(Movable::counter.loadAcquire(), instancesCount + 14); - } - QCOMPARE(Movable::counter.loadAcquire(), instancesCount); -} - -void tst_QVector::swapItemsAt() const -{ - QVector<int> v; - v << 0 << 1 << 2 << 3; - - v.swapItemsAt(0, 2); - QCOMPARE(v.at(0), 2); - QCOMPARE(v.at(2), 0); - - auto copy = v; - copy.swapItemsAt(0, 2); - QCOMPARE(v.at(0), 2); - QCOMPARE(v.at(2), 0); - QCOMPARE(copy.at(0), 0); - QCOMPARE(copy.at(2), 2); -} - -QTEST_MAIN(tst_QVector) -#include "tst_qvector.moc" diff --git a/tests/auto/corelib/tools/qversionnumber/CMakeLists.txt b/tests/auto/corelib/tools/qversionnumber/CMakeLists.txt new file mode 100644 index 0000000000..8f6ed66841 --- /dev/null +++ b/tests/auto/corelib/tools/qversionnumber/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qversionnumber Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qversionnumber LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qversionnumber + SOURCES + tst_qversionnumber.cpp +) + +## Scopes: +##################################################################### diff --git a/tests/auto/corelib/tools/qversionnumber/qversionnumber.pro b/tests/auto/corelib/tools/qversionnumber/qversionnumber.pro deleted file mode 100644 index e2ae91cb64..0000000000 --- a/tests/auto/corelib/tools/qversionnumber/qversionnumber.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -qtConfig(c++11): CONFIG += c++11 -qtConfig(c++14): CONFIG += c++14 -TARGET = tst_qversionnumber -QT = core testlib -SOURCES = tst_qversionnumber.cpp diff --git a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp index 7c4d1071ce..da9dcc9366 100644 --- a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp +++ b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp @@ -1,33 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2014 Keith Gardner <kreios4004@gmail.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2014 Keith Gardner <kreios4004@gmail.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <QtCore/qversionnumber.h> #include <QtCore/qlibraryinfo.h> @@ -73,10 +48,15 @@ private slots: void assignment(); void fromString_data(); void fromString(); + void fromString_extra(); void toString_data(); void toString(); void isNull_data(); void isNull(); + void iterators_data(); + void iterators(); + void iteratorsAreDefaultConstructible(); + void valueInitializedIteratorsCompareEqual(); void serialize_data(); void serialize(); void moveSemantics(); @@ -85,56 +65,56 @@ private slots: void tst_QVersionNumber::singleInstanceData() { - QTest::addColumn<QVector<int> >("segments"); + QTest::addColumn<QList<int>>("segments"); QTest::addColumn<QVersionNumber>("expectedVersion"); QTest::addColumn<QString>("expectedString"); QTest::addColumn<QString>("constructionString"); QTest::addColumn<int>("suffixIndex"); QTest::addColumn<bool>("isNull"); - // segments expectedVersion expectedString constructionString suffixIndex null - QTest::newRow("null") << QVector<int>() << QVersionNumber(QVector<int>()) << QString() << QString() << 0 << true; - QTest::newRow("text") << QVector<int>() << QVersionNumber(QVector<int>()) << QString() << QStringLiteral("text") << 0 << true; - QTest::newRow(" text") << QVector<int>() << QVersionNumber(QVector<int>()) << QString() << QStringLiteral(" text") << 0 << true; - QTest::newRow("Empty String") << QVector<int>() << QVersionNumber(QVector<int>()) << QString() << QStringLiteral("Empty String") << 0 << true; - QTest::newRow("-1.-2") << (QVector<int>()) << QVersionNumber() << QStringLiteral("") << QStringLiteral("-1.-2") << 0 << true; - QTest::newRow("1.-2-3") << (QVector<int>() << 1) << QVersionNumber(QVector<int>() << 1) << QStringLiteral("1") << QStringLiteral("1.-2-3") << 1 << false; - QTest::newRow("1.2-3") << (QVector<int>() << 1 << 2) << QVersionNumber(QVector<int>() << 1 << 2) << QStringLiteral("1.2") << QStringLiteral("1.2-3") << 3 << false; - QTest::newRow("0") << (QVector<int>() << 0) << QVersionNumber(QVector<int>() << 0) << QStringLiteral("0") << QStringLiteral("0") << 1 << false; - QTest::newRow("0.1") << (QVector<int>() << 0 << 1) << QVersionNumber(QVector<int>() << 0 << 1) << QStringLiteral("0.1") << QStringLiteral("0.1") << 3 << false; - QTest::newRow("0.1.2") << (QVector<int>() << 0 << 1 << 2) << QVersionNumber(0, 1, 2) << QStringLiteral("0.1.2") << QStringLiteral("0.1.2") << 5 << false; - QTest::newRow("0.1.2alpha") << (QVector<int>() << 0 << 1 << 2) << QVersionNumber(0, 1, 2) << QStringLiteral("0.1.2") << QStringLiteral("0.1.2alpha") << 5 << false; - QTest::newRow("0.1.2-alpha") << (QVector<int>() << 0 << 1 << 2) << QVersionNumber(0, 1, 2) << QStringLiteral("0.1.2") << QStringLiteral("0.1.2-alpha") << 5 << false; - QTest::newRow("0.1.2.alpha") << (QVector<int>() << 0 << 1 << 2) << QVersionNumber(0, 1, 2) << QStringLiteral("0.1.2") << QStringLiteral("0.1.2.alpha") << 5 << false; - QTest::newRow("0.1.2.3alpha") << (QVector<int>() << 0 << 1 << 2 << 3) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3) << QStringLiteral("0.1.2.3") << QStringLiteral("0.1.2.3alpha") << 7 << false; - QTest::newRow("0.1.2.3.alpha") << (QVector<int>() << 0 << 1 << 2 << 3) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3) << QStringLiteral("0.1.2.3") << QStringLiteral("0.1.2.3.alpha") << 7 << false; - QTest::newRow("0.1.2.3.4.alpha") << (QVector<int>() << 0 << 1 << 2 << 3 << 4) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4) << QStringLiteral("0.1.2.3.4") << QStringLiteral("0.1.2.3.4.alpha") << 9 << false; - QTest::newRow("0.1.2.3.4 alpha") << (QVector<int>() << 0 << 1 << 2 << 3 << 4) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4) << QStringLiteral("0.1.2.3.4") << QStringLiteral("0.1.2.3.4 alpha") << 9 << false; - QTest::newRow("0.1.2.3.4 alp ha") << (QVector<int>() << 0 << 1 << 2 << 3 << 4) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4) << QStringLiteral("0.1.2.3.4") << QStringLiteral("0.1.2.3.4 alp ha") << 9 << false; - QTest::newRow("0.1.2.3.4alp ha") << (QVector<int>() << 0 << 1 << 2 << 3 << 4) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4) << QStringLiteral("0.1.2.3.4") << QStringLiteral("0.1.2.3.4alp ha") << 9 << false; - QTest::newRow("0.1.2.3.4alpha ") << (QVector<int>() << 0 << 1 << 2 << 3 << 4) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4) << QStringLiteral("0.1.2.3.4") << QStringLiteral("0.1.2.3.4alpha ") << 9 << false; - QTest::newRow("0.1.2.3.4.5alpha ") << (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5) << QStringLiteral("0.1.2.3.4.5") << QStringLiteral("0.1.2.3.4.5alpha ") << 11 << false; - QTest::newRow("0.1.2.3.4.5.6alpha ") << (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6) << QStringLiteral("0.1.2.3.4.5.6") << QStringLiteral("0.1.2.3.4.5.6alpha ") << 13 << false; - QTest::newRow("0.1.2.3.4.5.6.7alpha ") << (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7) << QStringLiteral("0.1.2.3.4.5.6.7") << QStringLiteral("0.1.2.3.4.5.6.7alpha ") << 15 << false; - QTest::newRow("0.1.2.3.4.5.6.7.8alpha ") << (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8) << QStringLiteral("0.1.2.3.4.5.6.7.8") << QStringLiteral("0.1.2.3.4.5.6.7.8alpha ") << 17 << false; - QTest::newRow("0.1.2.3.4.5.6.7.8.alpha") << (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8) << QStringLiteral("0.1.2.3.4.5.6.7.8") << QStringLiteral("0.1.2.3.4.5.6.7.8.alpha") << 17 << false; - QTest::newRow("0.1.2.3.4.5.6.7.8 alpha") << (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8) << QStringLiteral("0.1.2.3.4.5.6.7.8") << QStringLiteral("0.1.2.3.4.5.6.7.8 alpha") << 17 << false; - QTest::newRow("0.1.2.3.4.5.6.7.8 alp ha") << (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8) << QStringLiteral("0.1.2.3.4.5.6.7.8") << QStringLiteral("0.1.2.3.4.5.6.7.8 alp ha") << 17 << false; - QTest::newRow("0.1.2.3.4.5.6.7.8alp ha") << (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8) << QVersionNumber(QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8) << QStringLiteral("0.1.2.3.4.5.6.7.8") << QStringLiteral("0.1.2.3.4.5.6.7.8alp ha") << 17 << false; - QTest::newRow("10.09") << (QVector<int>() << 10 << 9) << QVersionNumber(QVector<int>() << 10 << 9) << QStringLiteral("10.9") << QStringLiteral("10.09") << 5 << false; - QTest::newRow("10.0x") << (QVector<int>() << 10 << 0) << QVersionNumber(QVector<int>() << 10 << 0) << QStringLiteral("10.0") << QStringLiteral("10.0x") << 4 << false; - QTest::newRow("10.0xTest") << (QVector<int>() << 10 << 0) << QVersionNumber(QVector<int>() << 10 << 0) << QStringLiteral("10.0") << QStringLiteral("10.0xTest") << 4 << false; - QTest::newRow("127.09") << (QVector<int>() << 127 << 9) << QVersionNumber(QVector<int>() << 127 << 9) << QStringLiteral("127.9") << QStringLiteral("127.09") << 6 << false; - QTest::newRow("127.0x") << (QVector<int>() << 127 << 0) << QVersionNumber(QVector<int>() << 127 << 0) << QStringLiteral("127.0") << QStringLiteral("127.0x") << 5 << false; - QTest::newRow("127.0xTest") << (QVector<int>() << 127 << 0) << QVersionNumber(QVector<int>() << 127 << 0) << QStringLiteral("127.0") << QStringLiteral("127.0xTest") << 5 << false; - QTest::newRow("128.09") << (QVector<int>() << 128 << 9) << QVersionNumber(QVector<int>() << 128 << 9) << QStringLiteral("128.9") << QStringLiteral("128.09") << 6 << false; - QTest::newRow("128.0x") << (QVector<int>() << 128 << 0) << QVersionNumber(QVector<int>() << 128 << 0) << QStringLiteral("128.0") << QStringLiteral("128.0x") << 5 << false; - QTest::newRow("128.0xTest") << (QVector<int>() << 128 << 0) << QVersionNumber(QVector<int>() << 128 << 0) << QStringLiteral("128.0") << QStringLiteral("128.0xTest") << 5 << false; + // segments expectedVersion expectedString constructionString suffixIndex null + QTest::newRow("null") << QList<int>() << QVersionNumber() << QString() << QString() << 0 << true; + QTest::newRow("text") << QList<int>() << QVersionNumber() << QString() << QStringLiteral("text") << 0 << true; + QTest::newRow(" text") << QList<int>() << QVersionNumber() << QString() << QStringLiteral(" text") << 0 << true; + QTest::newRow("Empty String") << QList<int>() << QVersionNumber() << QString() << QStringLiteral("Empty String") << 0 << true; + QTest::newRow("-1.-2") << QList<int>() << QVersionNumber() << QStringLiteral("") << QStringLiteral("-1.-2") << 0 << true; + QTest::newRow("1.-2-3") << QList<int> { 1 } << QVersionNumber(QList<int> { 1 }) << QStringLiteral("1") << QStringLiteral("1.-2-3") << 1 << false; + QTest::newRow("1.2-3") << QList<int> { 1, 2 } << QVersionNumber(QList<int> { 1, 2 }) << QStringLiteral("1.2") << QStringLiteral("1.2-3") << 3 << false; + QTest::newRow("0") << QList<int> { 0 } << QVersionNumber(QList<int> { 0 }) << QStringLiteral("0") << QStringLiteral("0") << 1 << false; + QTest::newRow("0.1") << QList<int> { 0, 1 } << QVersionNumber(QList<int> { 0, 1 }) << QStringLiteral("0.1") << QStringLiteral("0.1") << 3 << false; + QTest::newRow("0.1.2") << QList<int> { 0, 1, 2 } << QVersionNumber(QList<int> { 0, 1, 2 }) << QStringLiteral("0.1.2") << QStringLiteral("0.1.2") << 5 << false; + QTest::newRow("0.1.2alpha") << QList<int> { 0, 1, 2 } << QVersionNumber(QList<int> { 0, 1, 2 }) << QStringLiteral("0.1.2") << QStringLiteral("0.1.2alpha") << 5 << false; + QTest::newRow("0.1.2-alpha") << QList<int> { 0, 1, 2 } << QVersionNumber(QList<int> { 0, 1, 2 }) << QStringLiteral("0.1.2") << QStringLiteral("0.1.2-alpha") << 5 << false; + QTest::newRow("0.1.2.alpha") << QList<int> { 0, 1, 2 } << QVersionNumber(QList<int> { 0, 1, 2 }) << QStringLiteral("0.1.2") << QStringLiteral("0.1.2.alpha") << 5 << false; + QTest::newRow("0.1.2.3alpha") << QList<int> { 0, 1, 2, 3 } << QVersionNumber(QList<int> { 0, 1, 2, 3 }) << QStringLiteral("0.1.2.3") << QStringLiteral("0.1.2.3alpha") << 7 << false; + QTest::newRow("0.1.2.3.alpha") << QList<int> { 0, 1, 2, 3 } << QVersionNumber(QList<int> { 0, 1, 2, 3 }) << QStringLiteral("0.1.2.3") << QStringLiteral("0.1.2.3.alpha") << 7 << false; + QTest::newRow("0.1.2.3.4.alpha") << QList<int> { 0, 1, 2, 3, 4 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4 }) << QStringLiteral("0.1.2.3.4") << QStringLiteral("0.1.2.3.4.alpha") << 9 << false; + QTest::newRow("0.1.2.3.4 alpha") << QList<int> { 0, 1, 2, 3, 4 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4 }) << QStringLiteral("0.1.2.3.4") << QStringLiteral("0.1.2.3.4 alpha") << 9 << false; + QTest::newRow("0.1.2.3.4 alp ha") << QList<int> { 0, 1, 2, 3, 4 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4 }) << QStringLiteral("0.1.2.3.4") << QStringLiteral("0.1.2.3.4 alp ha") << 9 << false; + QTest::newRow("0.1.2.3.4alp ha") << QList<int> { 0, 1, 2, 3, 4 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4 }) << QStringLiteral("0.1.2.3.4") << QStringLiteral("0.1.2.3.4alp ha") << 9 << false; + QTest::newRow("0.1.2.3.4alpha ") << QList<int> { 0, 1, 2, 3, 4 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4 }) << QStringLiteral("0.1.2.3.4") << QStringLiteral("0.1.2.3.4alpha ") << 9 << false; + QTest::newRow("0.1.2.3.4.5alpha ") << QList<int> { 0, 1, 2, 3, 4, 5 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4, 5 }) << QStringLiteral("0.1.2.3.4.5") << QStringLiteral("0.1.2.3.4.5alpha ") << 11 << false; + QTest::newRow("0.1.2.3.4.5.6alpha ") << QList<int> { 0, 1, 2, 3, 4, 5, 6 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4, 5, 6 }) << QStringLiteral("0.1.2.3.4.5.6") << QStringLiteral("0.1.2.3.4.5.6alpha ") << 13 << false; + QTest::newRow("0.1.2.3.4.5.6.7alpha ") << QList<int> { 0, 1, 2, 3, 4, 5, 6, 7 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4, 5, 6, 7 }) << QStringLiteral("0.1.2.3.4.5.6.7") << QStringLiteral("0.1.2.3.4.5.6.7alpha ") << 15 << false; + QTest::newRow("0.1.2.3.4.5.6.7.8alpha ") << QList<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 }) << QStringLiteral("0.1.2.3.4.5.6.7.8") << QStringLiteral("0.1.2.3.4.5.6.7.8alpha ") << 17 << false; + QTest::newRow("0.1.2.3.4.5.6.7.8.alpha") << QList<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 }) << QStringLiteral("0.1.2.3.4.5.6.7.8") << QStringLiteral("0.1.2.3.4.5.6.7.8.alpha") << 17 << false; + QTest::newRow("0.1.2.3.4.5.6.7.8 alpha") << QList<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 }) << QStringLiteral("0.1.2.3.4.5.6.7.8") << QStringLiteral("0.1.2.3.4.5.6.7.8 alpha") << 17 << false; + QTest::newRow("0.1.2.3.4.5.6.7.8 alp ha") << QList<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 }) << QStringLiteral("0.1.2.3.4.5.6.7.8") << QStringLiteral("0.1.2.3.4.5.6.7.8 alp ha") << 17 << false; + QTest::newRow("0.1.2.3.4.5.6.7.8alp ha") << QList<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 } << QVersionNumber(QList<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 }) << QStringLiteral("0.1.2.3.4.5.6.7.8") << QStringLiteral("0.1.2.3.4.5.6.7.8alp ha") << 17 << false; + QTest::newRow("10.09") << QList<int> { 10, 9 } << QVersionNumber(QList<int> { 10, 9 }) << QStringLiteral("10.9") << QStringLiteral("10.09") << 5 << false; + QTest::newRow("10.0x") << QList<int> { 10, 0 } << QVersionNumber(QList<int> { 10, 0 }) << QStringLiteral("10.0") << QStringLiteral("10.0x") << 4 << false; + QTest::newRow("10.0xTest") << QList<int> { 10, 0 } << QVersionNumber(QList<int> { 10, 0 }) << QStringLiteral("10.0") << QStringLiteral("10.0xTest") << 4 << false; + QTest::newRow("127.09") << QList<int> { 127, 9 } << QVersionNumber(QList<int> { 127, 9 }) << QStringLiteral("127.9") << QStringLiteral("127.09") << 6 << false; + QTest::newRow("127.0x") << QList<int> { 127, 0 } << QVersionNumber(QList<int> { 127, 0 }) << QStringLiteral("127.0") << QStringLiteral("127.0x") << 5 << false; + QTest::newRow("127.0xTest") << QList<int> { 127, 0 } << QVersionNumber(QList<int> { 127, 0 }) << QStringLiteral("127.0") << QStringLiteral("127.0xTest") << 5 << false; + QTest::newRow("128.09") << QList<int> { 128, 9 } << QVersionNumber(QList<int> { 128, 9 }) << QStringLiteral("128.9") << QStringLiteral("128.09") << 6 << false; + QTest::newRow("128.0x") << QList<int> { 128, 0 } << QVersionNumber(QList<int> { 128, 0 }) << QStringLiteral("128.0") << QStringLiteral("128.0x") << 5 << false; + QTest::newRow("128.0xTest") << QList<int> { 128, 0 } << QVersionNumber(QList<int> { 128, 0 }) << QStringLiteral("128.0") << QStringLiteral("128.0xTest") << 5 << false; } namespace UglyOperator { // ugh, but the alternative (operator <<) is even worse... -static inline QVector<int> operator+(QVector<int> v, int i) { v.push_back(i); return v; } +static inline QList<int> operator+(QList<int> v, int i) { v.push_back(i); return v; } } void tst_QVersionNumber::comparisonData() @@ -196,7 +176,7 @@ void tst_QVersionNumber::comparisonData() QTest::newRow("0.-129, 0") << QVersionNumber(0, -129) << QVersionNumber(0) << false << true << true << true << false << false << -129 << false << QVersionNumber(0); QTest::newRow("0, 0.-129") << QVersionNumber(0) << QVersionNumber(0, -129) << false << true << false << false << true << true << 129 << true << QVersionNumber(0); - const QVector<int> common = QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6; + const QList<int> common = QList<int>({ 0, 1, 2, 3, 4, 5, 6 }); using namespace UglyOperator; QTest::newRow("0.1.2.3.4.5.6.0.1.2, 0.1.2.3.4.5.6.0.1") << QVersionNumber(common + 0 + 1 + 2) << QVersionNumber(common + 0 + 1) << false << true << false << false << true << true << 2 << false << QVersionNumber(common + 0 + 1); QTest::newRow("0.1.2.3.4.5.6.0.1, 0.1.2.3.4.5.6.0.1.2") << QVersionNumber(common + 0 + 1) << QVersionNumber(common + 0 + 1 + 2) << false << true << true << true << false << false << -2 << true << QVersionNumber(common + 0 + 1); @@ -213,7 +193,7 @@ void tst_QVersionNumber::comparisonData() void tst_QVersionNumber::initTestCase() { - qRegisterMetaType<QVector<int> >(); + qRegisterMetaType<QList<int>>(); } void tst_QVersionNumber::constructorDefault() @@ -223,7 +203,7 @@ void tst_QVersionNumber::constructorDefault() QCOMPARE(version.majorVersion(), 0); QCOMPARE(version.minorVersion(), 0); QCOMPARE(version.microVersion(), 0); - QCOMPARE(version.segments(), QVector<int>()); + QVERIFY(version.segments().isEmpty()); } void tst_QVersionNumber::constructorVersioned_data() @@ -233,7 +213,7 @@ void tst_QVersionNumber::constructorVersioned_data() void tst_QVersionNumber::constructorVersioned() { - QFETCH(QVector<int>, segments); + QFETCH(QList<int>, segments); QFETCH(QVersionNumber, expectedVersion); QVersionNumber version(segments); @@ -246,17 +226,17 @@ void tst_QVersionNumber::constructorVersioned() void tst_QVersionNumber::constructorExplicit() { QVersionNumber v1(1); - QVersionNumber v2(QVector<int>() << 1); + QVersionNumber v2(QList<int>({ 1 })); QCOMPARE(v1.segments(), v2.segments()); QVersionNumber v3(1, 2); - QVersionNumber v4(QVector<int>() << 1 << 2); + QVersionNumber v4(QList<int>({ 1, 2 })); QCOMPARE(v3.segments(), v4.segments()); QVersionNumber v5(1, 2, 3); - QVersionNumber v6(QVector<int>() << 1 << 2 << 3); + QVersionNumber v6(QList<int>({ 1, 2, 3 })); QCOMPARE(v5.segments(), v6.segments()); @@ -264,6 +244,11 @@ void tst_QVersionNumber::constructorExplicit() QVersionNumber v8 = {4, 5, 6}; QCOMPARE(v7.segments(), v8.segments()); + + QVersionNumber v9(4, 5, 6); + QVersionNumber vA({4, 5, 6}); + + QCOMPARE(v9.segments(), vA.segments()); } void tst_QVersionNumber::constructorCopy_data() @@ -273,7 +258,7 @@ void tst_QVersionNumber::constructorCopy_data() void tst_QVersionNumber::constructorCopy() { - QFETCH(QVector<int>, segments); + QFETCH(QList<int>, segments); QFETCH(QVersionNumber, expectedVersion); QVersionNumber original(segments); @@ -418,14 +403,14 @@ void tst_QVersionNumber::normalized_data() QTest::addColumn<QVersionNumber>("version"); QTest::addColumn<QVersionNumber>("expected"); - QTest::newRow("0") << QVersionNumber(0) << QVersionNumber(); - QTest::newRow("1") << QVersionNumber(1) << QVersionNumber(1); - QTest::newRow("1.2") << QVersionNumber(1, 2) << QVersionNumber(1, 2); - QTest::newRow("1.0") << QVersionNumber(1, 0) << QVersionNumber(1); - QTest::newRow("1.0.0") << QVersionNumber(1, 0, 0) << QVersionNumber(1); - QTest::newRow("1.0.1") << QVersionNumber(1, 0, 1) << QVersionNumber(1, 0, 1); - QTest::newRow("1.0.1.0") << QVersionNumber(QVector<int>() << 1 << 0 << 1 << 0) << QVersionNumber(1, 0, 1); - QTest::newRow("0.0.1.0") << QVersionNumber(QVector<int>() << 0 << 0 << 1 << 0) << QVersionNumber(0, 0, 1); + QTest::newRow("0") << QVersionNumber(0) << QVersionNumber(); + QTest::newRow("1") << QVersionNumber(1) << QVersionNumber(1); + QTest::newRow("1.2") << QVersionNumber(1, 2) << QVersionNumber(1, 2); + QTest::newRow("1.0") << QVersionNumber(1, 0) << QVersionNumber(1); + QTest::newRow("1.0.0") << QVersionNumber(1, 0, 0) << QVersionNumber(1); + QTest::newRow("1.0.1") << QVersionNumber(1, 0, 1) << QVersionNumber(1, 0, 1); + QTest::newRow("1.0.1.0") << QVersionNumber(QList<int>({ 1, 0, 1, 0 })) << QVersionNumber(1, 0, 1); + QTest::newRow("0.0.1.0") << QVersionNumber(QList<int>({ 0, 0, 1, 0 })) << QVersionNumber(0, 0, 1); } void tst_QVersionNumber::normalized() @@ -442,13 +427,13 @@ void tst_QVersionNumber::isNormalized_data() QTest::addColumn<QVersionNumber>("version"); QTest::addColumn<bool>("expected"); - QTest::newRow("null") << QVersionNumber() << true; - QTest::newRow("0") << QVersionNumber(0) << false; - QTest::newRow("1") << QVersionNumber(1) << true; - QTest::newRow("1.2") << QVersionNumber(1, 2) << true; - QTest::newRow("1.0") << QVersionNumber(1, 0) << false; - QTest::newRow("1.0.0") << QVersionNumber(1, 0, 0) << false; - QTest::newRow("1.0.1") << QVersionNumber(1, 0, 1) << true; + QTest::newRow("null") << QVersionNumber() << true; + QTest::newRow("0") << QVersionNumber(0) << false; + QTest::newRow("1") << QVersionNumber(1) << true; + QTest::newRow("1.2") << QVersionNumber(1, 2) << true; + QTest::newRow("1.0") << QVersionNumber(1, 0) << false; + QTest::newRow("1.0.0") << QVersionNumber(1, 0, 0) << false; + QTest::newRow("1.0.1") << QVersionNumber(1, 0, 1) << true; } void tst_QVersionNumber::isNormalized() @@ -466,7 +451,7 @@ void tst_QVersionNumber::assignment_data() void tst_QVersionNumber::assignment() { - QFETCH(QVector<int>, segments); + QFETCH(QList<int>, segments); QFETCH(QVersionNumber, expectedVersion); QVersionNumber original(segments); @@ -488,17 +473,17 @@ void tst_QVersionNumber::fromString_data() const QString largerThanIntCanHoldString1 = "0." + QString::number(largerThanIntCanHold); QTest::newRow(qPrintable(largerThanIntCanHoldString0)) - << QVector<int>() << QVersionNumber() << QString() << largerThanIntCanHoldString0 << 0 << true; + << QList<int>() << QVersionNumber() << QString() << largerThanIntCanHoldString0 << 0 << true; QTest::newRow(qPrintable(largerThanIntCanHoldString1)) - << QVector<int>(0) << QVersionNumber(0) << QStringLiteral("0") << largerThanIntCanHoldString1 << 1 << true; + << QList<int>(0) << QVersionNumber(0) << QStringLiteral("0") << largerThanIntCanHoldString1 << 1 << true; const QString largerThanULongLongCanHoldString0 = QString::number(std::numeric_limits<qulonglong>::max()) + "0.0"; // 10x ULLONG_MAX const QString largerThanULongLongCanHoldString1 = "0." + QString::number(std::numeric_limits<qulonglong>::max()) + '0'; // 10x ULLONG_MAX QTest::newRow(qPrintable(largerThanULongLongCanHoldString0)) - << QVector<int>() << QVersionNumber() << QString() << largerThanULongLongCanHoldString0 << 0 << true; + << QList<int>() << QVersionNumber() << QString() << largerThanULongLongCanHoldString0 << 0 << true; QTest::newRow(qPrintable(largerThanULongLongCanHoldString1)) - << QVector<int>(0) << QVersionNumber(0) << QStringLiteral("0") << largerThanULongLongCanHoldString1 << 1 << true; + << QList<int>(0) << QVersionNumber(0) << QStringLiteral("0") << largerThanULongLongCanHoldString1 << 1 << true; } void tst_QVersionNumber::fromString() @@ -507,7 +492,7 @@ void tst_QVersionNumber::fromString() QFETCH(QVersionNumber, expectedVersion); QFETCH(int, suffixIndex); - int index; + qsizetype index; QCOMPARE(QVersionNumber::fromString(constructionString), expectedVersion); QCOMPARE(QVersionNumber::fromString(constructionString, &index), expectedVersion); QCOMPARE(index, suffixIndex); @@ -519,16 +504,56 @@ void tst_QVersionNumber::fromString() QCOMPARE(QVersionNumber::fromString(QLatin1String(constructionString.toLatin1())), expectedVersion); QCOMPARE(QVersionNumber::fromString(QLatin1String(constructionString.toLatin1()), &index), expectedVersion); QCOMPARE(index, suffixIndex); + +#if QT_DEPRECATED_SINCE(6, 4) + QT_WARNING_PUSH + QT_WARNING_DISABLE_DEPRECATED + // check deprecated `int *suffixIndex` overload, too + { + int i; + QCOMPARE(QVersionNumber::fromString(constructionString, &i), expectedVersion); + QCOMPARE(i, suffixIndex); + + QCOMPARE(QVersionNumber::fromString(QStringView(constructionString), &i), expectedVersion); + QCOMPARE(i, suffixIndex); + + QCOMPARE(QVersionNumber::fromString(QLatin1String(constructionString.toLatin1()), &i), expectedVersion); + QCOMPARE(i, suffixIndex); + } + QT_WARNING_POP +#endif +} + +void tst_QVersionNumber::fromString_extra() +{ + // check the overloaded fromString() functions aren't ambiguous + // when passing explicit nullptr: + { + auto v = QVersionNumber::fromString("1.2.3-rc1", nullptr); + QCOMPARE(v, QVersionNumber({1, 2, 3})); + } + { + auto v = QVersionNumber::fromString("1.2.3-rc1", 0); + QCOMPARE(v, QVersionNumber({1, 2, 3})); + } + + // check the UTF16->L1 conversion isn't doing something weird + { + qsizetype i = -1; + auto v = QVersionNumber::fromString(u"1.0ı", &i); // LATIN SMALL LETTER DOTLESS I + QCOMPARE(v, QVersionNumber(1, 0)); + QCOMPARE(i, 3); + } } void tst_QVersionNumber::toString_data() { singleInstanceData(); - // segments expectedVersion expectedString constructionString suffixIndex null - QTest::newRow("-1") << (QVector<int>() << -1) << QVersionNumber(-1) << QString("-1") << QString() << 0 << true; - QTest::newRow("-1.0") << (QVector<int>() << -1 << 0) << QVersionNumber(-1, 0) << QString("-1.0") << QString() << 0 << true; - QTest::newRow("1.-2") << (QVector<int>() << 1 << -2) << QVersionNumber(1, -2) << QString("1.-2") << QString() << 0 << true; + // segments expectedVersion expectedString constructionString suffixIndex null + QTest::newRow("-1") << (QList<int>({ -1 })) << QVersionNumber(-1) << QString("-1") << QString() << 0 << true; + QTest::newRow("-1.0") << (QList<int>({ -1, 0 })) << QVersionNumber(-1, 0) << QString("-1.0") << QString() << 0 << true; + QTest::newRow("1.-2") << (QList<int>({ 1, -2 })) << QVersionNumber(1, -2) << QString("1.-2") << QString() << 0 << true; } void tst_QVersionNumber::toString() @@ -546,7 +571,7 @@ void tst_QVersionNumber::isNull_data() void tst_QVersionNumber::isNull() { - QFETCH(QVector<int>, segments); + QFETCH(QList<int>, segments); QFETCH(bool, isNull); QVersionNumber version(segments); @@ -554,6 +579,45 @@ void tst_QVersionNumber::isNull() QCOMPARE(version.isNull(), isNull); } +void tst_QVersionNumber::iterators_data() +{ + singleInstanceData(); +} + +void tst_QVersionNumber::iterators() +{ + QFETCH(const QList<int>, segments); + QFETCH(QVersionNumber, expectedVersion); + + QVERIFY(std::equal(expectedVersion.begin(), expectedVersion.end(), + segments.begin(), segments.end())); + QVERIFY(std::equal(std::as_const(expectedVersion).begin(), std::as_const(expectedVersion).end(), + segments.begin(), segments.end())); + QVERIFY(std::equal(expectedVersion.cbegin(), expectedVersion.cend(), + segments.cbegin(), segments.cend())); + QVERIFY(std::equal(expectedVersion.rbegin(), expectedVersion.rend(), + segments.rbegin(), segments.rend())); + QVERIFY(std::equal(std::as_const(expectedVersion).rbegin(), std::as_const(expectedVersion).rend(), + segments.rbegin(), segments.rend())); + QVERIFY(std::equal(expectedVersion.crbegin(), expectedVersion.crend(), + segments.crbegin(), segments.crend())); +} + +void tst_QVersionNumber::iteratorsAreDefaultConstructible() +{ + static_assert(std::is_default_constructible_v<QVersionNumber::const_iterator>); + [[maybe_unused]] QVersionNumber::const_iterator ci; + [[maybe_unused]] QVersionNumber::const_reverse_iterator cri; +} + +void tst_QVersionNumber::valueInitializedIteratorsCompareEqual() +{ + QVersionNumber::const_iterator it = {}, jt = {}; + QCOMPARE_EQ(it, jt); + QVersionNumber::const_reverse_iterator rit = {}, rjt = {}; + QCOMPARE_EQ(rit, rjt); +} + void tst_QVersionNumber::serialize_data() { singleInstanceData(); @@ -561,7 +625,7 @@ void tst_QVersionNumber::serialize_data() void tst_QVersionNumber::serialize() { - QFETCH(QVector<int>, segments); + QFETCH(QList<int>, segments); QVersionNumber original(segments); QVersionNumber version; @@ -597,9 +661,9 @@ void tst_QVersionNumber::moveSemantics() v2 = std::move(v1); QCOMPARE(v2, QVersionNumber(1, 2, 3)); } - // QVersionNumber(QVector<int> &&) + // QVersionNumber(QList<int> &&) { - QVector<int> segments = QVector<int>() << 1 << 2 << 3; + QList<int> segments = QList<int>({ 1, 2, 3}); QVersionNumber v1(segments); QVersionNumber v2(std::move(segments)); QVERIFY(!v1.isNull()); @@ -624,7 +688,7 @@ void tst_QVersionNumber::moveSemantics() { QVersionNumber v(1, 2, 3); QVERIFY(!v.isNull()); - QVector<int> segments; + QList<int> segments; segments = v.segments(); QVERIFY(!v.isNull()); QVERIFY(!segments.empty()); diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro deleted file mode 100644 index 5a7c8478f1..0000000000 --- a/tests/auto/corelib/tools/tools.pro +++ /dev/null @@ -1,43 +0,0 @@ -TEMPLATE=subdirs -SUBDIRS=\ - collections \ - containerapisymmetry \ - qalgorithms \ - qarraydata \ - qbitarray \ - qcache \ - qcommandlineparser \ - qcontiguouscache \ - qcryptographichash \ - qeasingcurve \ - qexplicitlyshareddatapointer \ - qfreelist \ - qhash \ - qhashfunctions \ - qline \ - qlinkedlist \ - qmakearray \ - qmap \ - qmargins \ - qmessageauthenticationcode \ - qoffsetstringarray \ - qpair \ - qpoint \ - qpointf \ - qqueue \ - qrect \ - qringbuffer \ - qscopedpointer \ - qscopedvaluerollback \ - qscopeguard \ - qset \ - qsharedpointer \ - qsize \ - qsizef \ - qstl \ - qtimeline \ - qvarlengtharray \ - qvector \ - qversionnumber - -darwin: SUBDIRS += qmacautoreleasepool |